In this tutorial, we're going to look at how you can get the computer to calculate with decimal values (numbers like 3.14), instead of only integral values (numbers like 3).

# Getting the stuff you need

As with the last tutorial, we'll use the template again. Extract the package to a fresh directory (say, c:\my_projects\floats) and load up the .sln file. Remove the 'hello world' stuff in the Tick function.

# Sprites Again

Before we start working with decimal numbers, let's setup things so we can show something interesting. Let's load a Sprite again!

Add a Sprite variable above the tick function like this:

Sprite theSprite( new Surface("assets/ctankbase.tga"), 16 );

void Game::Tick( float a_DT )
{
...


This will create a variable that is a Sprite, from the 'ctankbase.tga' image you can find in the assets folder.

Now make your Tick function like this:

void Game::Tick( float a_DT )
{
// render a single frame here
m_Screen->Clear( 0 );
theSprite.Draw( 0, 0, m_Screen );
}


Run the program and look at the pretty tank again!

So far, so good, we're back where we were at the end of tutorial 4 (sprites and loops).

Let's make the tank move; Add a variable above the tick function like this:

int tankY=0;

void Game::Tick( float a_DT )
{
...


and change the Tick function to make it look like this:

void Game::Tick( float a_DT )
{
// render a single frame here
m_Screen->Clear( 0 );
theSprite.Draw( 0, tankY, m_Screen );
tankY = tankY + 1;
if( tankY > 430 )
{
tankY = 1;
}
}


This should be quite readable for you by know. Try understanding the code first, before you run it, and predict what it is going to do. Then run it and see if it does what you expected it to do!

As you have gathered, this moves the tank down by exactly one line each frame. That's great, but what if we want to make the tank move down more slowly, while still drawing the same amount of frames per second? Let's say that we want to make the tank move at one third of the speed it is doing now. The answer is quite simple: why don't we just add 0.33 to the tankY variable? Try it! Make the tick function look like this:

void Game::Tick( float a_DT )
{
// render a single frame here
m_Screen->Clear( 0 );
theSprite.Draw( 0, tankY, m_Screen );
tankY = tankY + 0.33;
if( tankY > 430 )
{
tankY = 1;
}
}


Try running this code. Does it do what you want it to do?

No! The tank just stays in the same position! What is going on?

I'll tell you: the tankY variable has been declared as an int in the line that reads:

int tankY=0;

This means you have told the compiler that tankY is an integer, which is a kind of number that cannot hold decimal values.

So when you write

tankY = tankY + 0.33;

What really happens is that the result of tankY + 0.33 is rounded down to the nearest integer, which is simply tankY again! (try it with 0.8. What happens now?)

In order for our original idea to work, you have to change the type of the tankY variable to something that can hold decimal values. This kind of variable is called a float, or a floating point number in normal language.

Try changing the line that said

int tankY=0;

to

float tankY=0;

Now run the application again. If you did this correctly, you will see that the tank is moving down again, at a third of the speed as before. Try setting the 0.33 to something even smaller, like 0.1, and see the tank move even slower.

Just one question should remain for you by now: if the tankY variable is now a decimal value, does that mean we can draw stuff at decimal pixel places? After all, we are using it in this line:

    theSprite.Draw( 0, tankY, m_Screen );

to draw the tank at a specific location.

The answer is no, we can't draw at decimal places. On a screen, all coordinates are integers, so you can't draw on decimal values. What you can do however, is pass a float to a function that expects an int, and it will be rounded down automatically. Just make sure you know what you are doing!

Likewise, you can pass an int to something that requires a float, and it will be converted automatically. Just remember that if you use the same variable to get a float result back, you will lose the part after the decimal point due to automatic rounding!

# Warnings

You may have seen more and more warnings appear when compiling your code after introducing floats. Stuff like

warning C4244: 'argument' : conversion from 'float' to 'int', possible loss
of data

And the offending line that caused this warning was

    theSprite.Draw( 0, tankY, m_Screen );

This is just the compiler telling you it is automatically changing the value it got from tankY (a float) to an int, by rounding down, or 'possibly losing data' as it puts it.

Since we know what we are doing, and we did this on purpose, we can safely ignore this warning.

Ignoring warnings, however, is a bad idea. If your code becomes bigger, more warnings may appear. These might be similar warnings that you can ignore because you know what you did and why, but sometimes they are real warnings that point out something you did not think about. These real warnings will be lost between all the other warnings and you won't see them, rendering the entire warning system useless and making you a slower, less efficient programmer!

So it's a good idea to get rid of the warnings. How can we get rid of this one? Well, by specifically telling the compiler that you do, in fact, know what you are doing, thankyouverymuch. And how do we do that in this case? By specifiying that we are 'casting' the float value to an int. Here's what that looks like:

    theSprite.Draw( 0, (int) tankY, m_Screen );

By putting (int) in front of the variable, we are telling the compiler that it needs to cast, or translate, the float value to an int before passing it to the function. This changes nothing in how the program works, but you've reassured the compiler that the behaviour is intentional, and now it won't warn you about that line anymore.

Any other warnings you accidentally create will now stand out nicely again for you to see.

By the way, it's always a good idea to fix every warning that appears so your code compiles without any warnings at all, just so you clearly see new ones appear and make sure you didn't do anything stupid.