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

Adding a time-bar

Since time is a crucial mechanism in the game, it is necessary to keep the player aware of it. They need to know if their allotted six seconds are about to run out. It will give them a sense of urgency as the end of the game draws near and a sense of accomplishment if they perform well enough to maintain or increase their remaining time.

Drawing the number of seconds remaining on the screen is not easy to read (when concentrating on the branches), nor is it a particularly interesting way to achieve the objective.

What we need is a time-bar. Our time-bar will be a simple red rectangle that's prominently displayed on the screen. It will start off nice and wide, but rapidly shrink as time runs out. When the player's remaining time reaches zero, the time-bar will be gone completely.

At the same time as adding the time-bar, we will add the necessary code to keep track of the player's remaining time, and respond when it runs out. Let's go through this step by step.

Find the Clock clock; declaration from earlier and add the highlighted code just after it, as follows:

// Variables to control time itself

Clock clock;

// Time bar

RectangleShape timeBar;

float timeBarStartWidth = 400;

float timeBarHeight = 80;

timeBar.setSize(Vector2f(timeBarStartWidth, timeBarHeight));

timeBar.setFillColor(Color::Red);

timeBar.setPosition((1920 / 2) - timeBarStartWidth / 2, 980);

Time gameTimeTotal;

float timeRemaining = 6.0f;

float timeBarWidthPerSecond = timeBarStartWidth / timeRemaining;

// Track whether the game is running

bool paused = true;

First, we declare an object of the RectangleShape type and call it timeBar. RectagleShape is an SFML class that is perfect for drawing simple rectangles.

Next, we will add a few float variables, timeBarStartWidth and timeBarHeight. We initialize them to 400 and 80, respectively. These variables will help us keep track of the size we need to draw timeBar at each frame.

Next, we set the size of timeBar using the timeBar.setSize function. We don't just pass in our two new float variables. First, we create a new object of the Vector2f type. What is different here, however, is that we don't give the new object a name. Instead, we simply initialize it with our two float variables and pass it straight in to the setSize function.

Tip

Vector2f is a class that holds two float variables. It also has some other functionality that will be introduced throughout this book.

After that, we color timeBar red by using the setFillColor function.

The last thing we do to timeBar in the previous code is set its position. The vertical coordinate is completely straightforward but the way we set the horizontal coordinate is slightly convoluted. Here is the calculation again:

(1920 / 2) - timeBarStartWidth / 2

First, the code divides 1920 by 2. Then, it divides timeBarStartWidth by 2. Finally, it subtracts the latter from the former.

The result makes timeBar sit neatly and centrally, in a horizontal fashion, on the screen.

The final three lines of code that we are talking about declare a new Time object called gameTimeTotal, a new float called timeRemaining that is initialized to 6, and a curious-sounding float named timeBarWidthPerSecond, which we will discuss next.

The timeBarWidthPerSecond variable is initialized with timeBarStartWidth divided by timeRemaining. The result is exactly the amount of pixels that timeBar needs to shrink by each second of the game. This will be useful when we resize timeBar in each frame.

Obviously, we need to reset the time remaining each time the player starts a new game. The logical place to do this is when the Enter key is pressed. We can also set score back to zero at the same time. Let's do that now by adding the following highlighted code

// Start the game

if (Keyboard::isKeyPressed(Keyboard::Return))

{

    paused = false;

    // Reset the time and the score

    score = 0;

    timeRemaining = 6;

}

Now, we must reduce each frame by the amount of time remaining and resize timeBar accordingly. Add the following highlighted code to the update section, as shown here:

/*

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

Update the scene

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

*/

if (!paused)

{

    // Measure time

    Time dt = clock.restart();

    // Subtract from the amount of time remaining

    timeRemaining -= dt.asSeconds();

    // size up the time bar

    timeBar.setSize(Vector2f(timeBarWidthPerSecond *

        timeRemaining, timeBarHeight));

    // Set up the bee

    if (!beeActive)

    {

        // How fast is the bee

        srand((int)time(0) * 10);

        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

First, we subtracted the amount of time the player has left by however long the previous frame took to execute with the following code:

timeRemaining -= dt.asSeconds();

Then, we adjusted the size of timeBar with the following code:

timeBar.setSize(Vector2f(timeBarWidthPerSecond *

        timeRemaining, timeBarHeight));

The x value of Vector2F is initialized with timebarWidthPerSecond when multiplied by timeRemaining. This produces exactly the right width, relative to how long the player has left. The height remains the same and timeBarHeight is used without any manipulation.

And of course, we must detect when the time has run out. For now, we will simply detect that time has run out, pause the game, and change the text of messageText. Later, we will do more work here. Add the following highlighted code right after the previous code we added. We will look at it in more detail later:

// Measure time

Time dt = clock.restart();

// Subtract from the amount of time remaining

timeRemaining -= dt.asSeconds();

// resize up the time bar

timeBar.setSize(Vector2f(timeBarWidthPerSecond *

    timeRemaining, timeBarHeight));

if (timeRemaining<= 0.0f) {

    

    // Pause the game

    paused = true;

    // Change the message shown to the player

    messageText.setString("Out of time!!");

    //Reposition the text based on its new size

    FloatRect textRect = messageText.getLocalBounds();

    messageText.setOrigin(textRect.left +

        textRect.width / 2.0f,

        textRect.top +

        textRect.height / 2.0f);

    messageText.setPosition(1920 / 2.0f, 1080 / 2.0f);

}

// Set up the bee

if (!beeActive)

{

    // How fast is the bee

    srand((int)time(0) * 10);

    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

Let's step through the previous code:

  1. First, we test whether time has run out with if(timeRemaining<= 0.0f).
  2. Then, we set paused to true, so this will be the last time the update part of our code is executed (until the player presses Enter again).
  3. Then, we change the message of messageText, calculate its new center to be set as its origin, and position it in the center of the screen.

Finally, for this part of the code, we need to draw timeBar. There is nothing new in this code that we haven't seen many times before. Just note that we draw timeBar after the tree so that it is not partially obscured. Add the following highlighted code to draw the time-bar:

// Draw the score

window.draw(scoreText);

// Draw the timebar

window.draw(timeBar);

if (paused)

{

    // Draw our message

    window.draw(messageText);

}

// Show everything we just drew

window.display();

Now, you can run the game, press Enter to start it, and watch the time-bar smoothly disappear down to nothing:

The game then pauses and the OUT OF TIME!! message will appear in the center of the screen:

You can, of course, press Enter to start the game again and watch it run from the beginning.