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:
- First, we test whether time has run out with if(timeRemaining<= 0.0f).
- 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).
- 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.