Moving the clouds and the bee
Let's use the elapsed time since the last frame, to breathe life into the bee and the clouds. This will solve the problem of having a consistent frame rate across different PCs.
Giving life to the bee
The first thing we want to do is to set up the bee at a certain height and a certain speed. We only want to do this when the bee is inactive. So we wrap the next code in an if
block. Examine and add the highlighted code, then we will discuss it:
/*
****************************************
Update the scene
****************************************
*/
// Measure time
Time dt = clock.restart();
// Setup the bee if (!beeActive) { // How fast is the bee srand((int)time(0)); beeSpeed = (rand() % 200) + 200; // How high is the bee srand((int)time(0) * 10); float height = (rand() % 500) + 500; spriteBee.setPosition(2000, height); beeActive = true; }
/*
****************************************
Draw the scene
****************************************
*/
Now, if the bee is not active, just like it won't be when the game first starts, if(!beeActive)
will be true
and the code above will do the following things, in this order:
- Seed the random number generator
- Get a random number between 199 and 399 and assign the result to
beeSpeed
- Seed the random number generator again
- Get a random number between 499 and 999 and assign the result to a new
float
variable calledheight
- Set the position of the bee to
2000
on the x axis (just off-screen to the right) and to whateverheight
equals on the y axis - Set
beeActive
to true
Note
Note that the height
variable is the first variable we have ever declared inside the game loop. Furthermore, because it was declared inside an if
block, it is actually "invisible" outside the if
block. This is fine for our use because once we have set the height of the bee we don't need it any more. This phenomenon, which affects variables, is called scope. We will explore this more fully in Chapter 4: Loops, Arrays, Switch, Enumerations, and Functions - Implementing Game Mechanics.
If we run the game, nothing will actually happen to the bee yet, but now the bee is active we can write some code that runs when beeActive
is true
.
Add the following highlighted code, which, as you can see, executes whenever beeActive
is true
. This is because it follows with an else
after the if(!beeActive)
block:
// Set up the bee
if (!beeActive)
{
// How fast is the bee
srand((int)time(0) );
beeSpeed = (rand() % 200) + 200;
// How high is the bee
srand((int)time(0) * 10);
float height = (rand() % 1350) + 500;
spriteBee.setPosition(2000, height);
beeActive = true;
}
else // Move the bee { spriteBee.setPosition( spriteBee.getPosition().x - (beeSpeed * dt.asSeconds()), spriteBee.getPosition().y); // Has the bee reached the right hand edge of the screen? if (spriteBee.getPosition().x < -100) { // Set it up ready to be a whole new cloud next frame beeActive = false; } }
/*
****************************************
Draw the scene
****************************************
*/
In the else
block the following things happen.
The bee position is changed using the following criteria. The setPosition
function uses the getPosition
function to get the current x coordinate of the bee. It then adds beeSpeed * dt.asSeconds()
to that coordinate.
The beeSpeed
variable value is many pixels per second and was randomly assigned in the previous if
block. The value of dt.asSeconds()
will be a fraction of 1 that represents how long the previous frame of the animation took.
Assume that the bee's current x coordinate is 1000. Now suppose a fairly basic PC loops at 5000 frames per second. This would mean that dt.asSeconds
would be 0.0002. And further suppose that beeSpeed
was set to the maximum 399 pixels per second. Then the code that determines the value that setPosition
uses for the x coordinate can be explained like this:
1000 - 0.0002 x 399
So the new position on the x axis for the bee would be 999.9202. We can see that the bee is, very smoothly drifting to the left, at well under a pixel per frame. If the frame rate fluctuates then the formula will produce a new value to suit. If we run the same code on a PC that only achieves 100 frames per second or a PC that achieves a million frames per second, the bee will move at the same speed.
The setPosition
function uses getPosition().y
to keep the bee in exactly the same y coordinate throughout this cycle of being active.
The final code in the else
block we just added is this:
// Has the bee reached the right hand edge of the screen? if (spriteBee.getPosition().x < -100) { // Set it up ready to be a whole new cloud next frame beeActive = false; }
This code tests each and every frame (when beeActive
is true
), whether the bee has disappeared off the left-hand side of the screen. If the getPosition
function returns less than -100, it will certainly be out of view of the player. When this has occurred, beeActive
is set to false
and, on the next frame, a new bee will be set flying at a new random height and a new random speed.
Try running the game and watch our bee dutifully fly from right to left and then come back to the right again at a new height and speed. It's almost like a new bee every time.
Tip
Of course a real bee would stick around for ages and pester you while you're trying to concentrate on chopping the tree. We will make some smarter game characters in the next project.
Now we will get the clouds moving in a very similar way.
Blowing the clouds
The first thing we want to do is set up the first cloud at a certain height and a certain speed. We only want to do this when the cloud is inactive. So we wrap the next code in an if
block. Examine and add the highlighted code, just after the code we added for the bee, then we will discuss it. It is almost identical to the code we used for the bee:
else
// Move the bee
{
spriteBee.setPosition(
spriteBee.getPosition().x -
(beeSpeed * dt.asSeconds()),
spriteBee.getPosition().y);
// Has the bee reached the right hand edge of the screen?
if (spriteBee.getPosition().x < -100)
{
// Set it up ready to be a whole new bee next frame
beeActive = false;
}
}
// Manage the clouds // Cloud 1 if (!cloud1Active) { // How fast is the cloud srand((int)time(0) * 10); cloud1Speed = (rand() % 200); // How high is the cloud srand((int)time(0) * 10); float height = (rand() % 150); spriteCloud1.setPosition(-200, height); cloud1Active = true; }
/*
****************************************
Draw the scene
****************************************
*/
The only difference between the code we have just added and the bee code is that we work on a different sprite and use different ranges for our random numbers. Also, we use *10
to the result returned by time(0) so we are always guaranteed to get a different seed for each of the clouds. When we code the other cloud movement next you will see that we use *20
and*30
respectively.
Now we can take action when the cloud is active. We will do so in the else
block. As with the if
block, the code is identical to that of the bee, except that all the code works on the cloud and not the bee:
// Manage the clouds
if (!cloud1Active)
{
// How fast is the cloud
srand((int)time(0) * 10);
cloud1Speed = (rand() % 200);
// How high is the cloud
srand((int)time(0) * 10);
float height = (rand() % 150);
spriteCloud1.setPosition(-200, height);
cloud1Active = true;
}
else { spriteCloud1.setPosition( spriteCloud1.getPosition().x + (cloud1Speed * dt.asSeconds()), spriteCloud1.getPosition().y); // Has the cloud reached the right hand edge of the screen? if (spriteCloud1.getPosition().x > 1920) { // Set it up ready to be a whole new cloud next frame cloud1Active = false; } }
/*
****************************************
Draw the scene
****************************************
*/
Now we know what to do, we can duplicate the same code for the second and third clouds. Add this highlighted code that handles the second and third clouds, immediately after the code for the first cloud:
...
// Cloud 2 if (!cloud2Active) { // How fast is the cloud srand((int)time(0) * 20); cloud2Speed = (rand() % 200); // How high is the cloud srand((int)time(0) * 20); float height = (rand() % 300) - 150; spriteCloud2.setPosition(-200, height); cloud2Active = true; } else { spriteCloud2.setPosition( spriteCloud2.getPosition().x + (cloud2Speed * dt.asSeconds()), spriteCloud2.getPosition().y); // Has the cloud reached the right hand edge of the screen? if (spriteCloud2.getPosition().x > 1920) { // Set it up ready to be a whole new cloud next frame cloud2Active = false; } } if (!cloud3Active) { // How fast is the cloud srand((int)time(0) * 30); cloud3Speed = (rand() % 200); // How high is the cloud srand((int)time(0) * 30); float height = (rand() % 450) - 150; spriteCloud3.setPosition(-200, height); cloud3Active = true; } else { spriteCloud3.setPosition( spriteCloud3.getPosition().x + (cloud3Speed * dt.asSeconds()), spriteCloud3.getPosition().y); // Has the cloud reached the right hand edge of the screen? if (spriteCloud3.getPosition().x > 1920) { // Set it up ready to be a whole new cloud next frame cloud3Active = false; } }
/*
****************************************
Draw the scene
****************************************
*/
Now you can run the game and the clouds will randomly and continuously drift across the screen, and the bee will buzz from right to left before re-spawning once more back on the right.
Tip
Does all this cloud and bee handling seem a little bit repetitious? We will see how we could save lots of typing and make our code more readable. In C++ there are ways of handling multiple instances of the same type of variable or object. These are called arrays and we will learn about them in Chapter 4: Loops, Arrays, Switch, Enumerations, and Functions - Implementing Game Mechanics. At the end of the project, once we have learnt about arrays, we will discuss how we could improve our cloud code.
Take a look at a few frequently asked questions related to the topics in this chapter.