Chapter 6: Object-Oriented Programming – Starting the Pong Game

In this chapter, there's quite a large amount of theory, but the theory will give us the knowledge that we need to start using object-oriented programming (OOP) with some expertise. Furthermore, we will not waste any time in putting that theory to good use as we will use it to code the next project, a Pong game. We will get to look behind the scenes at how we can create new types that we can use as objects by coding a class. First, we will look at a simplified Pong scenario so that we can learn about some class basics, and then we will start again and code a Pong game for real using the principles we have learned.

In this chapter, we will cover the following topics:

  • Learn about OOP and classes using a hypothetical Bat class
  • Start working on the Pong game and code a real class to represent the player's bat

OOP

Object-oriented programming is a programming paradigm that we could consider to be almost the standard way to code. It is true there are non-OOP ways to code and there are even some non-OOP game coding languages/libraries. However, since we are starting from scratch, there is no reason to do things in any other way.

OOP will do the following:

  • Make our code easier to manage, change, or update
  • Make our code quicker and more reliable to write
  • Make it possible to easily use other people's code (like we have with SFML)

We have already seen the third benefit in action. Let's discuss exactly what OOP is.

OOP is a way of programming that involves breaking our requirements down into chunks that are more manageable than the whole. Each chunk is self-contained yet potentially reusable by other programs, while working together as a whole with the other chunks. These chunks are what we have been referring to as objects.

When we plan and code an object, we do so with a class.

Tip

A class can be thought of as the blueprint of an object.

We implement an object of a class. This is called an instance of a class. Think about a house blueprint. You can't live in it, but you can build a house from it. You build an instance of the house. Often, when we design classes for our games, we write them to represent real-world things. In the next project, we will write classes for a bat that the player controls and a ball that the player can bounce around the screen with the bat. However, OOP is more than this.

Tip

OOP is a way of doing things, a methodology that defines best practices.

The three core principles of OOP are encapsulation, polymorphism, and inheritance. This might sound complex but, taken a step at a time, this is reasonably straightforward.

Encapsulation

Encapsulation means keeping the internal workings of your code safe from interference from the code that uses it. You can achieve this by allowing only the variables and functions you choose to be accessed. This means your code can always be updated, extended, or improved without affecting the programs that use it, provided the exposed parts are still accessed in the same way.

As an example, with proper encapsulation, it wouldn't matter whether the SFML team needed to update the way their Sprite class works. If the function signatures remain the same, they don't have to worry about what goes on inside. The code that we wrote before the update will still work after the update.

Polymorphism

Polymorphism allows us to write code that is less dependent on the types we are trying to manipulate. This will make our code clearer and more efficient. Polymorphism means different forms. If the objects that we code can be more than one type of thing, then we can take advantage of this. Polymorphism might sound a little bit like black magic at this point. We will use polymorphism in the fourth project, which we will start in Chapter 14, Abstraction and Code Management – Making Better Use of OOP. Everything will become clearer.

Inheritance

Just like it sounds, inheritance means we can harness all the features and benefits of other peoples' classes, including encapsulation and polymorphism, while further refining their code specifically to our situation. We will use inheritance for the first time at the same time as we use polymorphism.

Why use OOP?

When written properly, OOP allows you to add new features without worrying about how they interact with existing features. When you do have to change a class, its self-contained (encapsulated) nature means less or perhaps even zero consequences for other parts of the program.

You can use other people's code (like the SFML classes) without knowing or perhaps even caring for how it works inside.

OOP and, by extension, SFML, allows you to write games that use complicated concepts such as multiple cameras, multiplayer, OpenGL, directional sound, and more besides—all of this without breaking a sweat.

You can create multiple, similar, yet different versions of a class without starting the class from scratch by using inheritance.

You can still use the functions intended for the original type of object with your new object because of polymorphism.

All this makes sense really. And as we know, C++ was designed from the start with all this OOP in mind.

Tip

The ultimate key to success with OOP and making games (or any other type of app), other than the determination to succeed, is planning and design. It is not so much just "knowing" all the C++, SFML, and OOP topics that will help you to write great code, but rather applying all that knowledge to write code that is well-structured/designed. The code in this book is presented in an order and manner that's appropriate for learning about the various C++ topics in a gaming context. The art and science of structuring your code is called design patterns. As your code gets longer and more complex, effective use of design patterns will become more important. The good news is that we don't need to invent these design patterns ourselves. We will need to learn about them as our projects get more complex. As our projects become more complex, our design patterns will evolve too.

In this project, we will learn about and use basic classes and encapsulation. As this book progresses, we will get a bit more daring and use inheritance, polymorphism, and other OOP-related C++ features too.

What exactly is a class?

A class is a bunch of code that can contains functions, variables, loops, and all the other C++ syntax we have already learned about. Each new class will be declared in its own .h code file with the same name as the class, while its functions will be defined in their own .cpp file.

Once we have written a class, we can use it to make as many objects from it as we want. Remember, the class is the blueprint, and we make objects based on the blueprint. The house isn't the blueprint, just like the object isn't the class. It is an object made from the class.

Tip

You can think of an object as a variable and the class as a type.

Of course, with all this talk of OOP and classes, we haven't actually seen any code. Let's fix that now.

The theory of a Pong Bat

What follows is a hypothetical discussion of how we might use OOP to get started with the Pong project by coding a Bat class. Don't add any code to the project just yet as what follows is over-simplified in order to explain the theory. Later in this chapter, we will code it for real. When we get to coding the class for real, it will actually be quite different, but the principles we will learn about here will prepare us for success.

We will begin by exploring variables and functions as part of a class.

The class variable and function declarations

A bat that bounces a ball would be an excellent first candidate for a class.

Tip

If you don't know what Pong is, then take a look at this link: https://en.wikipedia.org/wiki/Pong.

Let's take a look at a hypothetical Bat.h file:

class Bat

{

    private:

        // Length of the pong bat

        int m_Length = 100;

        // Height of the pong bat

        int m_Height = 10;

        // Location on x axis

        int m_XPosition;      

        // Location on y axis

        int m_YPosition;      

    public:

        void moveRight();

        void moveLeft();

};

At first glance, the code might appear a little complex, but when it has been explained, we will see there are very few concepts we haven't already covered.

The first thing to notice is that a new class is declared using the class keyword, followed by the name of the class and that the entire declaration is enclosed in curly braces, followed by a closing semicolon:

class Bat

{

    …

    …

};

Now, let's take a look at the variable declarations and their names:

// Length of the pong bat

int m_Length = 100;

// Height of the pong bat

int m_Height = 10;

// Location on x axis

int m_XPosition;      

// Location on y axis

int m_YPosition;

All the names are prefixed with m_. This m_ prefix is not compulsory, but it is a good convention. Variables that are declared as part of the class are called member variables. Prefixing with an m_ makes it plain when we are dealing with a member variable. When we write functions for our classes, we will start to see local (non-member) variables and parameters as well. The m_ convention will then prove itself useful.

Also, notice that all the variables are in a section of the code headed with the private: keyword. Scan your eyes over the previous code and note that the body of the class code is separated into two sections:

private:

    // more code here

public:

    // More code here

The public and private keywords control the encapsulation of our class. Anything that is private cannot be accessed directly by the user of an instance/object of the class. If you are designing a class for others to use, you don't want them to be able to alter anything at will. Note that member variables do not have to be private, but good encapsulation is achieved by making them private whenever possible.

This means that our four member variables (m_Length, m_Height, m_XPosition, and m_YPosition) cannot be accessed directly by our game engine from the main function. They can only be accessed indirectly by the code of the class. This is encapsulation in action. For the m_Length and m_Height variables, this is fairly easy to accept as long as we don't need to change the size of the bat. The m_XPosition and m_YPosition member variables, however, need to be accessed, or how on earth will we move the bat?

This problem is solved in the public: section of the code, as follows:

void moveRight();

void moveLeft();

The class provides two functions that are public and will be usable with an object of the Bat type. When we look at the definitions of these functions, we will see how exactly these functions manipulate the private variables.

In summary, we have a bunch of inaccessible (private) variables that cannot be used from the main function. This is good because encapsulation makes our code less error-prone and more maintainable. We then solve the problem of moving the bat by providing indirect access to the m_XPosition and m_YPosition variables by providing two public functions.

The code in the main function can call these functions using an instance of the class, but the code inside the functions control exactly how the variables are used.

Let's take a look at the function definitions.

The class function definitions

The function definitions we will write in this book will all go in a separate file to the class and function declarations. We will use files with the same name as the class and the .cpp file extension. So, for example, the following code would go in a file called Bat.cpp. Look at the following code, which has just one new concept:

#include "Bat.h"

void Bat::moveRight()

{

    // Move the bat a pixel to the right

    xPosition ++;

}

void Bat::moveLeft()

{

    // Move the bat a pixel to the left

    xPosition --;

}

The first thing to note is that we must use an include directive to include the class and function declarations from the Bat.h file.

The new concept we can see here is the use of the scope resolution operator, ::. Since the functions belong to a class, we must write the signature part by prefixing the function name with the class name, as well as ::. void Bat::moveLeft() and void Bat::moveRight.

Important note

Actually, we have briefly seen the scope resolution operator before, that is, whenever we declare an object of a class, and we have not previously used using namespace...

Note that we could have put the function definitions and declarations in one file, like this:

class Bat

{

    private:

        // Length of the pong bat

        int m_Length = 100;

        // Length of the pong bat

        int m_Height = 10;

        // Location on x axis

        int m_XPosition;      

        // Location on y axis

        int m_YPosition;      

    public:

        void Bat::moveRight()

        {

            // Move the bat a pixel to the right

            xPosition ++;

        }

        void Bat::moveLeft()

        {

            // Move the bat a pixel to the left

            xPosition --;

        }

};

However, when our classes get longer (as they will with our first Zombie Arena class), it is more organized to separate the function definitions into their own file. Furthermore, header files are considered "public", and are often used for documentation purposes if other people will be using the code that we write.

But how do we use a class once we have coded it?

Using an instance of a class

Despite all the code we have seen related to classes, we haven't actually used the class. We already know how to do this as we have used the SFML classes many times already.

First, we would create an instance of the Bat class, like this:

Bat bat;

The bat object has all the variables we declared in Bat.h. We just can't access them directly. We can, however, move our bat using its public functions, like this:

bat.moveLeft();

Or we can move it like this:

bat.moveRight();

Remember that bat is a Bat, and as such it has all the member variables and has all of the functions available to it.

Later, we may decide to make our Pong game multiplayer. In the main function, we could change the code so that the game has two bats, perhaps like this:

Bat bat;

Bat bat2;

It is vitally important to realize that each of these instances of Bat are separate objects with their very own set of variables. There are more ways to initialize an instance of a class, and we will see an example of this when we code the Bat class for real, next.

Now, we can start the project for real.

Creating the Pong project

Since setting up a project is a fiddly process, we will go through it a step by step, like we did for the Timber!!! project. I won't show you the same screenshots that I did for the Timber!!! project, but the process is the same, so flip back to Chapter 1, C++, SFML, Visual Studio, and Starting the First Game if you want a reminder of the locations of the various project properties:

  1. Start Visual Studio and click on the Create New Project button. Or, if you still have the Timber!!! project open, you can select File | New project.
  2. In the window shown next, choose Console app and click the Next button. You will then see the Configure your new project window.
  3. In the Configure your new project window, type Pong in the Project name field. Note that this causes Visual Studio to automatically configure the Solution name field so that it has the same name.
  4. In the Location field, browse to the VS Projects folder that we created in Chapter 1. Like the Timber!!! project, this will be the location that all our project files will be kept.
  5. Check the option to Place solution and project in the same directory.
  6. When you have completed these steps, click Create. The project is generated by Visual Studio, including some C++ code in the main.cpp file, like it was previously.
  7. We will now configure the project to use the SFML files that we put in the SFML folder. From the main menu, select Project | Pong properties…. At this stage, you should have the Pong Property Pages window open.
  8. In the Pong Property Pages window, select All Configurations from the Configuration: drop-down.
  9. Now, select C/C++ and then General from the left-hand menu.
  10. After, locate the Additional Include Directories edit box and type the drive letter where your SFML folder is located, followed by SFMLinclude. The full path to type, if you located your SFML folder on your D drive, is D:SFMLinclude. Change your path if you installed SFML on a different drive.
  11. Click Apply to save your configurations so far.
  12. Now, still in the same window, perform these steps. From the left-hand menu, select Linker and then General.
  13. Now, find the Additional Library Directories edit box and type the drive letter where your SFML folder is, followed by SFMLlib. So, the full path to type if you located your SFML folder on your D drive is D:SFMLlib. Change your path if you installed SFML on a different drive.
  14. Click Apply to save your configurations so far.
  15. Next, still in the same window, perform these steps. Switch the Configuration: drop-down to Debug as we will be running and testing Pong in debug mode.
  16. Select Linker and then Input.
  17. Find the Additional Dependencies edit box and click into it on the far left-hand side. Now, copy and paste/type in the following: sfml-graphics-d.lib;sfml-window-d.lib;sfml-system-d.lib;sfml-network-d.lib;sfml-audio-d.lib;. Be extra careful to place the cursor exactly at the start of the edit box's current content so that you don't overwrite any of the text that is already there.
  18. Click OK.
  19.  Click Apply and then OK.
  20. Now, we need to copy the SFML .dll files into the main project directory. My main project directory is D:VS ProjectsPong. It was created by Visual Studio in the previous steps. If you put your VS Projects folder somewhere else, then perform this step there instead. The files we need to copy into the project folder are located in our SFMLin folder. Open a window for each of the two locations and highlight all the files in the SFMLin folder.
  21. Now, copy and paste the highlighted files into the project folder, that is, D:VS ProjectsPong.

We now have the project properties configured and ready to go.

We will be displaying some text for a HUD (Heads Up Display) in this game that will show the player's score and remaining lives. For this, we need a font.

Important note

Download this free-for-personal-use font from http://www.dafont.com/theme.php?cat=302 and unzip the download. Or feel free to use a font of your choice. You will just need to make some minor changes to the code when we load the font.

Create a new folder called fonts in the VS ProjectsPong folder and add the DS-DIGIT.ttf file into the VS ProjectsPongfonts folder.

We are now ready to code our first C++ class.

Coding the Bat class

The simple Pong bat example was a good way of introducing the basics of classes. Classes can be simple and short, like the preceding Bat class, but they can also be longer and more complicated and contain other objects made from other classes.

When it comes to making games, there is a few vital things missing from the hypothetical Bat class. It might be fine for all these private member variables and public functions, but how will we draw anything? Our Pong bat needs a sprite, and in some games, they will also need a texture. Furthermore, we need a way to control the rate of animation of all our game objects, just like we did with the bee and the clouds in the previous project. We can include other objects in our class in exactly the same way that we included them in the main.cpp file. Let's code our Bat class for real so that we can see how we can solve all these issues.

Coding Bat.h

To get started, we will code the header file. Right-click on Header Files in the Solution Explorer window and select ADD | New Item. Next, choose the Header File (.h) option and name the new file Bat.h. Click the Add button. We are now ready to code the file.

Add the following code to Bat.h:

#pragma once

#include <SFML/Graphics.hpp>

using namespace sf;

class Bat

{

private:

    Vector2f m_Position;

    // A RectangleShape object

    RectangleShape m_Shape;

    float m_Speed = 1000.0f;

    bool m_MovingRight = false;

    bool m_MovingLeft = false;

public:

    Bat(float startX, float startY);

    FloatRect getPosition();

    RectangleShape getShape();

    void moveLeft();

    void moveRight();

    void stopLeft();

    void stopRight();

    void update(Time dt);

};

First, note the #pragma once declaration at the top of the file. This prevents the file from being processed by the compiler more than once. As our games get more complicated with perhaps dozens of classes, this will speed up compilation time.

Note the names of the member variables and the parameters and return types of the functions. We have a Vector2f called m_Position, which will hold the horizontal and vertical position of the player's bat. We also have an SFML RectangleShape, which will be the actual bat that appears on the screen.

There are two Boolean members that will track which direction, if any, the bat is currently moving in, and we have a float called m_Speed that tells us the number of pixels per second that the bat can move at when the player decides to move it left or right.

The next part of the code needs some explanation since we have a function called Bat; this is the exact same name as the class. This is called a constructor.

Constructor functions

When a class is coded, a special function is created by the compiler. We don't see this function in our code, but it is there. It is called a constructor. It is the function that would have been called if we used our hypothetical Bat class example.

When we need to write some code to prepare an object for use, often a good place to do this is in the constructor. When we want the constructor to do anything other than simply create an instance, we must replace the default (unseen) constructor provided by the compiler. This is what we will do with the Bat constructor function.

Notice that the Bat constructor takes two float parameters. This is perfect for initializing the position on the screen when we first create a Bat object. Also note that constructors have no return type, not even void.

We will soon use the constructor function, Bat, to put this game object into its starting position. Remember that this function is called at the time that an object of the Bat type is declared.

Continuing with the Bat.h explanation

Next is the getPosition function, which returns a FloatRect, the four points that define a rectangle. Then, we have getShape, which returns a RectangleShape. This will be used so that we can return to the main game loop,  m_Shape, so that it can be drawn.

We also have the moveLeft, moveRight, stopLeft, and stopRight functions, which are for controlling if, when, and in which direction the bat will be in motion.

Finally, we have the update function, which takes a Time parameter. This function will be used to calculate how to move the bat each frame. As a bat and a ball will both move quite differently to each other, it makes sense to encapsulate the movement code inside the class. We will call the update function once each frame of the game from the main function.

Tip

You can probably guess that the Ball class will also have an update function.

Now, we can code Bat.cpp, which will implement all the definitions and use the member variables.

Coding Bat.cpp

Let's create the file, and then we can start discussing the code. Right-click the Source Files folder in the Solution Explorer window. Now, select C++ File (.cpp) and enter Bat.cpp in the Name: field. Click the Add button and our new file will be created for us.

We will divide the code for this file into two parts to make discussing it simpler.

First, code the Bat constructor function, as follows:

#include "Bat.h"

 

// This the constructor and it is called when we create an object

Bat::Bat(float startX, float startY)

{

    m_Position.x = startX;

    m_Position.y = startY;

 

    m_Shape.setSize(sf::Vector2f(50, 5));

    m_Shape.setPosition(m_Position);

}

In the preceding code, we can see that we include the bat.h file. This makes all the functions and variables that were declared previously in bat.h available to us.

We implement the constructor because we need to do some work to get the instance set up, and the default unseen empty constructor provided by the compiler is not sufficient. Remember that the constructor is the code that runs when we initialize an instance of Bat.

Notice that we use the Bat::Bat syntax as the function name to make it clear we are using the Bat function from the Bat class.

This constructor receives two float values, startX and startY. The next thing that happens is we assign these values to m_Position.x and m_Position.y. The Vector2f named m_Position now holds the values that were passed in, and because m_Position is a member variable, these values are accessible throughout the class. Note, however, that m_Position was declared as private and will not accessible in our main function file—not directly, anyway. We will see how we can resolve this issue soon.

Finally, in the constructor, we initialize the RectangleShape called m_Shape by setting its size and position. This is different to how we coded the hypothetical Bat class in the The theory of a Pong Bat section. The SFML Sprite class has convenient variables for size and position that we can access using the setSize and setPosition functions, so we don't need the hypothetical m_Length and m_Height anymore.

Furthermore, note that we will need to vary how we initialize the Bat class (compared to the hypothetical Bat class) to suit our custom constructor.

We need to implement the remaining five functions of the Bat class. Add the following code to Bat.cpp after the constructor we just discussed:

FloatRect Bat::getPosition()

{

    return m_Shape.getGlobalBounds();

}

RectangleShape Bat::getShape()

{

    return m_Shape;

}

void Bat::moveLeft()

{

     m_MovingLeft = true;

}

void Bat::moveRight()

{

    m_MovingRight = true;

}

void Bat::stopLeft()

{

    m_MovingLeft = false;

}

void Bat::stopRight()

{

    m_MovingRight = false;

}

void Bat::update(Time dt)

{

    if (m_MovingLeft) {

        m_Position.x -= m_Speed * dt.asSeconds();

    }

    if (m_MovingRight) {

        m_Position.x += m_Speed * dt.asSeconds();

    }

    m_Shape.setPosition(m_Position);

}

Let's go through the code we have just added.

First, we have the getPosition function. All it does is return a FloatRect to the code that called it. The m_Shape.getGlobalBounds line of code returns a FloatRect that is initialized with the coordinates of the four corners of the RectangleShape, that is, m_Shape. We will call this function from the main function when we are determining whether the ball has hit the bat.

Next, we have the getShape function. All this function does is pass a copy of m_Shape to the calling code. This is necessary so that we can draw the bat in the main function. When we code a public function with the sole purpose of passing back private data from a class, we call it a getter function.

Now, we can look at the moveLeft, moveRight, stopLeft, and stopRight functions. All they do is set the m_MovingLeft and m_MovingRight Boolean variables appropriately so that they keep track of the player's current intentions. Note, however, that they don't do anything to the RectangleShape instance or the FloatRect instance that determine the position. This is just what we need.

The last function in the Bat class is update. We will call this function once per frame of the game. The update function will grow in complexity as our game projects get more complicated. For now, all we need to do is tweak m_Position, depending on whether the player is moving left or right. Note that the formula that's used to do this tweak is the same one that we used for updating the bee and the clouds in the Timber!!! project. The code multiplies the speed by the delta time and then adds or subtracts it from the position. This causes the bat to move relative to how long the frame took to update. Next, the code sets the position of m_Shape with whatever the latest values held in m_Position happen to be.

Having an update function in our Bat class rather than the main function is encapsulation. Rather than updating the positions of all the game objects in the main function like we did in the Timber!!! project, each object will be responsible for updating themselves. As we will do next, however, we will call this update function from the main function.

Using the Bat class and coding the main function

Switch to the main.cpp file that was automatically generated when we created the project. Delete all its auto-generated code and add the code that follows.

Code the Pong.cpp file as follows:

#include "Bat.h"

#include <sstream>

#include <cstdlib>

#include <SFML/Graphics.hpp>

int main()

{

    // Create a video mode object

    VideoMode vm(1920, 1080);

    // Create and open a window for the game

    RenderWindow window(vm, "Pong", Style::Fullscreen);

    int score = 0;

    int lives = 3;

    

    // Create a bat at the bottom center of the screen

    Bat bat(1920 / 2, 1080 - 20);

    // We will add a ball in the next chapter

    // Create a Text object called HUD

    Text hud;

    // A cool retro-style font

    Font font;

    font.loadFromFile("fonts/DS-DIGI.ttf");

    // Set the font to our retro-style

    hud.setFont(font);

    // Make it nice and big

    hud.setCharacterSize(75);

    // Choose a color

    hud.setFillColor(Color::White);

    hud.setPosition(20, 20);

    // Here is our clock for timing everything

    Clock clock;

    while (window.isOpen())

    {

        /*

        Handle the player input

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

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

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

        */

        /*

        Update the bat, the ball and the HUD

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

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

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

        */

        

        

        /*

        Draw the bat, the ball and the HUD

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

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

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

        */

        

    }

    return 0;

}

In the preceding code, the structure is similar to the one we used in the Timber!!! project. The first exception, however, is when we create an instance of the Bat class:

// Create a bat

Bat bat(1920 / 2, 1080 - 20);

The preceding code calls the constructor function to create a new instance of the Bat class. The code passes in the required arguments and allows the Bat class to initialize its position in the center of the screen near the bottom. This is the perfect position for our bat to start.

Also note that I have used comments to indicate where the rest of the code will eventually be placed. It is all within the game loop, just like it was in the Timber!!! project. Here is where the rest of the code will go again, just to remind you:

      /*

        Handle the player input

        …

        /*

        Update the bat, the ball and the HUD

        …

        

        

        /*

        Draw the bat, the ball and the HUD

        …

Next, add the code to the Handle the player input section, as follows:

Event event;

while (window.pollEvent(event))

{

    if (event.type == Event::Closed)

        // Quit the game when the window is closed

        window.close();

}

// Handle the player quitting

if (Keyboard::isKeyPressed(Keyboard::Escape))

{

    window.close();

}

// Handle the pressing and releasing of the arrow keys

if (Keyboard::isKeyPressed(Keyboard::Left))

{

    bat.moveLeft();

}

else

{

    bat.stopLeft();

}

if (Keyboard::isKeyPressed(Keyboard::Right))

{

    bat.moveRight();

}

else

{

    bat.stopRight();

}

The preceding code handles the player quitting the game by pressing the Escape key, exactly like it did in the Timber!!! project. Next, there are two ifelse structures that handle the player moving the bat. Let's analyze the first of these two structures:

if (Keyboard::isKeyPressed(Keyboard::Left))

{

    bat.moveLeft();

}

else

{

    bat.stopLeft();

}

The preceding code will detect whether the player is holding down the left arrow cursor key on the keyboard. If they are, then the moveLeft function is called on the Bat instance. When this function is called, the true value is set to the m_MovingLeft private Boolean variable. If, however, the left arrow key is not being held down, then the stopLeft function is called and the m_MovingLeft is set to false.

The exact same process is then repeated in the next ifelse block of code to handle the player pressing (or not pressing) the right arrow key.

Next, add the following code to the Update the bat the ball and the HUD section, as follows:

// Update the delta time

Time dt = clock.restart();

bat.update(dt);

// Update the HUD text

std::stringstream ss;

ss << "Score:" << score << "  Lives:" << lives;

hud.setString(ss.str());

In the preceding code, we use the exact same timing technique that we used for the Timber!!! project, only this time, we call update on the Bat instance and pass in the delta time. Remember that, when the Bat class receives the delta time, it will use the value to move the bat based on the previously received movement instructions from the player and the desired speed of the bat.

Next, add the following code to the Draw the bat, the ball and the HUD section, as follows:

window.clear();

window.draw(hud);

window.draw(bat.getShape());

window.display();

In the preceding code, we clear the screen, draw the text for the HUD, and use the bat.getShape function to grab the RectangleShape instance from the Bat instance and draw it to the screen. Finally, we call window.display, just like we did in the previous project, to draw the bat in its current position.

At this stage, you can run the game and you will see the HUD and a bat. The bat can be moved smoothly left and right using the arrow/cursor keys:

Congratulations! That is the first class, all coded and deployed.

Summary

In this chapter, we discovered the basics of OOP, such as how to code and use a class, including making use of encapsulation to control how code outside of our classes can access the member variables, but only to the extent and in the manner that we want it to. This is just like SFML classes, which allow us to create and use Sprite and Text instances, but only in the way they were designed to be used.

Don't concern yourself too much if some of the details around OOP and classes are not entirely clear. The reason I say this is because we will spend the rest of this book coding classes and the more we use them, the clearer they will become.

Furthermore, we have a working bat and a HUD for our Pong game.

In the next chapter, we will code the Ball class and get it bouncing around the screen. We will then be able to add collision detection and finish the game.

FAQ

Q) I have learned other languages and OOP seems much simpler in C++. Is this a correct assessment?

A) This was an introduction to OOP and its basic fundamentals. There is more to it than this. We will learn about more OOP concepts and details throughout this book.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.118.226.66