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

Implementing the HUD

Now, we know enough about Strings, SFML Text, and SFML Font to go about implementing the HUD. HUD stands for Heads Up Display. It can be as simple as the score and text messages on the screen or it can include more complex elements such as a time-bar, mini-map, or compass that represents the direction that the player character is facing.

To get started with the HUD, we need to add another #include directive to the top of the code file to add access to the sstream class. As we already know, the sstream class adds some really useful functionality for combining Strings and other variable types into a String.

Add the following line of highlighted code:

#include <sstream>

#include <SFML/Graphics.hpp>

using namespace sf;

int main()

{

Next, we will set up our SFML Text objects: one to hold a message that we will vary to suit the state of the game and one that will hold the score and will need to be regularly updated.

The code declares the Text and Font objects, loads the font, assigns the font to the Text objects, and then adds the String messages, color, and size. This should look familiar from our discussion in the previous section. In addition, we added a new int variable called score that we can manipulate so that it holds the player's score.

Tip

Remember that, if you chose a different font from KOMIKAP_.ttf, back in Chapter 1, C++, SFML, Visual Studio, and Starting the First Game, you will need to change that part of the code to match the .ttf file that you have in the Visual Studio Stuff/Projects/Timber/fonts folder.

By adding the following highlighted code, we will be ready to move on to updating the HUD:

// Track whether the game is running

bool paused = true;

// Draw some text

int score = 0;

Text messageText;

Text scoreText;

// We need to choose a font

Font font;

font.loadFromFile("fonts/KOMIKAP_.ttf");

// Set the font to our message

messageText.setFont(font);

scoreText.setFont(font);

// Assign the actual message

messageText.setString("Press Enter to start!");

scoreText.setString("Score = 0");

// Make it really big

messageText.setCharacterSize(75);

scoreText.setCharacterSize(100);

// Choose a color

messageText.setFillColor(Color::White);

scoreText.setFillColor(Color::White);

while (window.isOpen())

{

    /*

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

    Handle the players input

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

    */

In the preceding code we have achieved the following:

  • Declared a variable to hold the score
  • Declared some SFML Text and Font objects
  • Initialized the Font object by loading a font from a file
  • Initialized the Text objects using the font and some Strings
  • Set the size and color of the Text objects using the setCharacterSize and setFillColor functions

The following snippet of code might look a little convoluted, even complex. It is, however, straightforward when you break it down a bit. Examine and add the new highlighted code. We will go through it after this:

// Choose a color

messageText.setFillColor(Color::White);

scoreText.setFillColor(Color::White);

// Position the text

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

scoreText.setPosition(20, 20);

while (window.isOpen())

{

    /*

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

    Handle the players input

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

    */

We have two objects of the Text type that we will display on the screen. We want to position scoreText to the top left with a little bit of padding. This is not a challenge; we simply use scoreText.setPosition(20, 20), which positions it at the top left with 20 pixels of horizontal and vertical padding.

Positioning messageText, however, is not so easy. We want to position it in the exact midpoint of the screen. Initially, this might not seem like a problem, but then we have to remember that the origin of everything we draw is at the top left-hand corner. So, if we simply divide the screen width and height by two and use the results in mesageText.setPosition..., then the top left of the text will be in the center of the screen and it will spread out untidily to the right.

The following is the code under discussion again for convenience:

// Position the text

FloatRect textRect = messageText.getLocalBounds();

messageText.setOrigin(textRect.left +

    textRect.width / 2.0f,

    textRect.top +

    textRect.height / 2.0f);

What the code does is set the center of messageText to the center of the screen. The rather complex-looking bit of code that we are reviewing repositions the origin of messageText to the center of itself.

In the preceding code, we first declare a new object of the FloatRect type called textRect. A FloatRect object, as its name suggests, holds a rectangle with floating-point coordinates.

The code then uses the mesageText.getLocalBounds function to initialize textRect with the coordinates of the rectangle that wraps messageText.

The next line of code, which is spread over four lines as it is quite long, uses the messageText.setOrigin function to change the origin (the point that is used to draw at) to the center of textRect. Of course, textRect holds a rectangle that matches the coordinates that wrap messageText. Then, this following line of code executes:

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

Now, messageText will be neatly positioned in the exact center of the screen. We will use this code each time we change the text of messageText because changing the message changes the size of messageText, so its origin will need recalculating.

Next, we declare an object of the stringstream type called ss. Note that we use the full name, including the namespace, that is, std::stringstream. We could avoid this syntax by adding using namespace std to the top of our code file. We aren't going to here, though, because we use it infrequently. Take a look at the following code and add it to the game; then, we can go through it in more detail. Since we only want this code to execute when the game is not paused, be sure to add it with the other code, inside the if(!paused) block, as follows:

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;

        }

    }

    // Update the score text

    std::stringstream ss;

    ss<< "Score = " << score;

    scoreText.setString(ss.str());

}// End if(!paused)

/*

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

Draw the scene

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

*/

We use ss and the special functionality provided by the << operator, which concatenates variables into a stringstream. Here, ss << "Score = " << score has the effect of creating a String with "Score = ". Whatever the value of score is, is concatenated together. For example, when the game first starts, score is equal to zero, so ss will hold the "Score = 0" value. If score ever changes, ss will adapt each frame.

The following line of code simply sets the String contained in ss to scoreText:

scoreText.setString(ss.str());

It is now ready to be drawn to the screen.

This following code draws both Text objects (scoreText and messageText), but the code that draws messageText is wrapped in an if statement. This if statement causes messageText to only be drawn when the game is paused.

Add the following highlighted code:

// Now draw the insect

window.draw(spriteBee);

// Draw the score

window.draw(scoreText);

if (paused)

{

    // Draw our message

    window.draw(messageText);

}

// Show everything we just drew

window.display();

We can now run the game and see our HUD being drawn on the screen. You will see the Score = 0 and PRESS ENTER TO START messages. The latter will disappear when you press Enter:

If you want to see the score updating, add a temporary line of code, score ++;, anywhere in the while(window.isOpen) loop. If you add this temporary line, you will see the score go up fast, very fast!

If you added the temporary code, that is, score ++;, be sure to delete it before continuing.