Beginning C++ Game Programming
上QQ阅读APP看书,第一时间看更新

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 set up the bee at a certain height and a certain speed. We only want to do this when the bee is inactive. Due to this, we will wrap the following code in an if block. Examine and add the following highlighted code, and 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 preceding code will do the following things, in this order:

  • Seed the random number generator.
  • Get a random number between 200 and 399 and assign the result to beeSpeed.
  • Seed the random number generator again.
  • Get a random number between 500 and 999 and assign the result to a new float variable called height.
  • Set the position of the bee to 2000 on the x axis (just off-screen to the right) and to whatever height equals on the y axis.
  • Set beeActive to true.

    Important 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 of the if block. This is fine for our use because once we have set the height of the bee, we don't need it anymore. This phenomenon that affects variables is called scope. We will explore this more fully in Chapter 4, Loops, Arrays, Switches, Enumerations, and Functions – Implementing Game Mechanics.

If we run the game, nothing will 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 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 left-hand edge of the screen?

    if (spriteBee.getPosition().x < -100)

    {

        // Set it up ready to be a whole new bee next frame

        beeActive = false;

    }

}

/*

****************************************

Draw the scene

****************************************

*/

In the else block, the following things happen.

The bee's position is changed using the following criteria. The setPosition function uses the getPosition function to get the current horizontal coordinate of the bee. It then subtracts beeSpeed * dt.asSeconds() from 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 animation took.

Let's assume that the bee's current horizontal coordinate is 1000. Now, suppose a basic PC loops at 5,000 frames per second. This would mean that dt.asSeconds would be 0.0002. Now, let's also suppose that beeSpeed was set to the maximum 399 pixels per second. With this information, we can say that the code that determines the value that setPosition uses for the horizontal coordinate is as follows:

1000 - 0.0002 x 399

Therefore, the new position on the horizontal axis for the bee would be 999.9202. We can see that the bee is very, 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 vertical coordinate throughout this cycle of being active.

The final part of the code in the else block we just added is as follows:

// 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;

}

This code tests, in each and every frame (when beeActive is true), whether the bee has disappeared off of 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 occurs, 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 later projects.

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. Consequently, we will wrap the code that follows in an if block. Examine and add the following highlighted code, just after the code we added for the bee, and then we will discuss it. It is almost identical to the code we used on 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-related code is that we work on a different sprite and use different ranges for our random numbers. Also, we multiply by ten (* 10 ) to the result returned by time(0) so that 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 act when the cloud is active. We will do so in the else block. Like the if block, the code is identical to that of the bee-related code, except that all of 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 that we know what to do, we can duplicate the same code for the second and third clouds. Add the following highlighted code, which 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. The bee will also buzz from right to left before respawning once more back on the right. The following screenshot shows what we've achieved in this chapter:

Tip

Does all of 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 as, in C++, there are ways of handling multiple instances of the same type of variable or object. One such way is called arrays, and we will learn about them in Chapter 4, Loops, Arrays, Switches, Enumerations, and Functions – Implementing Game Mechanics. At the end of this project, once we have learned about arrays, we will discuss how we can improve our cloud code.

Take a look at a few frequently asked questions related to the topics in this chapter.