SFML Game Development

5 (11 reviews total)
By Jan Haller , Henrik Vogelius Hansson , Artur Moreira
  • Instant online access to over 8,000+ books and videos
  • Constantly updated with 100+ new titles each month
  • Breadth and depth in over 1,000+ technologies
  1. Making a Game Tick

About this book

Game development comprises the combination of many different aspects such as game logics, graphics, audio, user input, physics and much more. SFML is an Open Source C++ library designed to make game development more accessible, exposing multimedia components to the user through a simple, yet powerful interface.

If you are a C++ programmer with a stack of ideas in your head and seeking a platform for implementation, your search ends here.

Starting with nothing more than a blank screen, SFML Game Development will provide you with all the guidance you need to create your first fully featured 2D game using SFML 2.0. By the end, you'll have learned the basic principles of game development, including advanced topics such as how to network your game, how to utilize particle systems and much more.

SFML Game Development starts with an overview of windows, graphics, and user inputs. After this brief introduction, you will start to get to grips with SFML by building up a world of different game objects, and implementing more and more gameplay features. Eventually, you’ll be handling advanced visual effects, audio effects and network programming like an old pro. New concepts are discussed, while the code steadily develops.

SFML Game Development will get you started with animations, particle effects and shaders. As well as these fundamental game aspects, we’re also covering network programming to the extent where you’ll be able to support the game running from two different machines. The most important part, the gameplay implementation with enemies and missiles, will make up the core of our top-scrolling airplane shoot' em-up game!

You will learn everything you need in SFML Game Development in order to start with game development and come closer to creating your own game.

Publication date:
June 2013
Publisher
Packt
Pages
296
ISBN
9781849696845

 

Chapter 1. Making a Game Tick

Through the words in this book, we will attempt to convey the best knowledge we possibly can. We aim to teach techniques that we learned along the years, techniques that we would like to have been told about in our early days of game development. We now write this book to save your time, by showing you directly the solution to common problems, and why things work the way they do.

Alongside our attempt to teach the basics and a little beyond game development, we will especially focus on the Simple and Fast Multimedia Library (SFML). Every part of this book will be about developing a game and leveraging the advantages that SFML is able to provide us. To spice things up a little, and since we use the C++ programming language, we will try our best to use the language in a modern way, applying more recent language features, techniques, and programming styles, in a fully object-oriented approach. C++ is a great tool and it always has been, however, it is a good thing if we grow with it and adapt to the possibilities it has to offer in the present day.

This chapter introduces the SFML library and shows you its capabilities by creating a small application. We are going to address the basic concepts relevant to game development, namely; rendering, game loops, and code organization. Furthermore, the first part of our game code developed, will serve as a basis for the coming chapters.

 

Introducing SFML


Before we start developing a game, we would like to tell you a little bit about the library we will use throughout the book. SFML is an object-oriented C++ framework. As can be guessed by the name, its philosophy consists of having a simple, user-friendly application programming interface (API), and allowing for both high performances and fast development.

SFML is a multimedia library, meaning that it provides a layer between you and the hardware. It is split into five modules:

  • System: This is a core module upon which all other modules are built. It provides two-dimensional and three-dimensional vector classes, clocks, threads, and Unicode strings, among other things.

  • Window: The Window module makes it possible to create application windows, and to collect user input, such as mouse movement or key presses.

  • Graphics: This module provides all functionalities that are related to two-dimensional rendering, such as images, texts, shapes, and colors.

  • Audio: SFML also offers a module to work with sound. When you want to load a music theme and play it on the computer's loudspeakers, this is the module you have to look for.

  • Network: Another medium SFML covers is the network, a more and more important part of our interconnected world. This module allows you to send data over LAN or the Internet; it also lets you work with protocols, such as HTTP or FTP.

If you don't need all the modules, it is possible to use only a part of SFML. We will cover every module in SFML, but of course we are not able to use every single class. We recommend having a look at the SFML documentation, which is available at www.sfml-dev.org/documentation.php. The documentation explains every class and function in a detailed manner, and is an invaluable tool when developing a game using SFML.

SFML is open source, which means that you have access to its complete source code. Usually, the implementations aren't relevant to the user, but if you are interested in how something was solved, don't hesitate to skim through SFML's code.

The library uses the zlib/libpng license, which is extremely permissive. You can use SFML in both open and closed source projects, both free and commercial.

Downloading and installation

There are two possibilities when using SFML: download the pre-built libraries, or recompile them yourself. The first option is simpler, but you have to wait for major versions (2.0, 2.1, and so on) to be released. If you want to use the latest development sources, you can download the current Git revision. The configuration software CMake is used to prepare the sources for compilation with a compiler of your choice. For example, CMake creates Visual Studio solutions or g++ Makefiles. The recompilation process is explained in detail in the SFML tutorials, which can be found at www.sfml-dev.org/tutorials.php.

As mentioned, SFML is split into five modules. There are five headers to include a complete module (and its dependencies). To include the whole Audio module, you can write:

#include <SFML/Audio.hpp>

On the other hand, if you need a specific header file, you can find it in the directory of the corresponding module:

#include <SFML/Audio/Sound.hpp>

Each module is compiled to a separate library, which makes it possible to use only the modules you need. SFML can be built for release or debug mode, and it can be linked statically or dynamically. The resulting libraries are named according to the scheme sfml-module[-s][-d]. The -s postfix is required if you link statically; the -d postfix specifies debug mode. For example, to link the Graphics module statically in release mode, you have to specify the library sfml-graphics-s in your linker options. Depending on your compiler, a file extension (such as .lib) might be necessary. Keep in mind that some modules depend on others; therefore, you have to link the dependencies too. For example, Graphics depends on Window, which depends on System; therefore, you should link the three (in this order).

An important point to note is that if you link SFML statically, you have to define the macro SFML_STATIC in your projects, so that the linker knows what functions to resolve.

In case you do not know how linking a library works for a specific compiler, please refer to the online tutorials. They explain how to install everything correctly, and are always up-to-date.

A minimal example

Before you go deeper into the book and SFML itself, let's take a look at a minimal application example to show how an application that uses this library looks like, its general flow of execution, and some basic functionality.

#include <SFML/Graphics.hpp>

int main()
{
    sf::RenderWindow window(sf::VideoMode(640, 480), "SFML Application");
    sf::CircleShape shape;
    shape.setRadius(40.f);
    shape.setPosition(100.f, 100.f);
    shape.setFillColor(sf::Color::Cyan);
    while (window.isOpen())
    {
    sf::Event event;
    while (window.pollEvent(event))
    {
        if (event.type == sf::Event::Closed)
            window.close();
    }
    window.clear();
    window.draw(shape);
    window.display();
    }
}

All this application does is to open a window onto which we can render, with a width of 640 pixels and a height of 480 pixels. Its title says "SFML Application". Then, a cyan geometric circle is created, and while the window is open, it is drawn to the screen. Finally, for each time the circle is drawn, the program checks for user input that may have arrived from the underlying window. In our case, we only handle the sf::Event::Closed event, which arrives every time the application is requested to terminate, such as when we click on the close button, or press an application-terminating shortcut, such as Alt + F4.

If you failed to understand a part of or the whole snippet of code, don't fear. This book contains all you need to know about this and much more.

 

A few notes on C++


C++ is a very powerful, but also very complex programming language; even after years one never stops learning. We expect you to understand the basic language features (variables, data types, functions, classes, polymorphism, pointers, and templates), as well as the most important parts of the standard library (strings, streams, and the STL). If you feel unsure, we recommend reading a good C++ book, before or in parallel to this book, since SFML and our code sometimes uses advanced techniques. Game development is a difficult topic on its own; it is very frustrating if you additionally have to fight C++. Even if it takes some time to reasonably learn the programming language, it is a good investment, since it will save you days of tedious debugging.

You may have heard that in 2011, a new C++ standard was released, which introduced a lot of extremely useful features. We are going to use a few C++11 features in the book, and show how they can improve code. Each time we use a new C++11 technique, we will briefly explain it.

An issue that is widely underestimated, especially by beginners, is the importance of clean code. Before making a game, it is always a good idea to have a rough imagination of the game features and their implementation. It may help to draw sketches on a paper, in order to visualize contexts better. Also during development, it is crucial to keep an eye on the code design, and to refactor messy code where necessary.

Some key aspects of good code are as follows:

  • Modularity: In this the functionalities are separated, and dependencies between them are reduced to a minimum. This allows you to maintain and debug application parts locally, as well as, to change the implementation of a module without affecting the other modules. Concretely, we achieve this by widely avoiding global variables, distributing functionality to different classes, and keeping interfaces between them small. We also split the code base to different headers and implementation files, and try to include only what is really necessary.

  • Abstraction: In this, the functionality is encapsulated into classes and functions. Code duplication is avoided. The usage of low-level operations, such as manual memory management (new/delete) is minimized, because it is inherently error-prone, and replaced with idioms such as RAII. In short, keep most of your code on a high abstraction level, such that it is expressive and achieves a lot of actions within a few lines. When you need boilerplate code, wrap it into functions, so that the code using it still looks clean.

  • Code style: One thing, be consistent. It does not matter what naming convention you use, or if you have a space between if and the opening parenthesis, so long as you stick to one style. It is important that you keep the code readable and expressive, so that you still recognize what you have done after several weeks. Use comments where appropriate.

After this initial sermon, we hope that you have recognized how a well-structured code can keep up your motivation to develop, while on the other hand, a total mess is contra-productive and frustrating, when it comes to maintenance, debugging, or integration of new features. Don't be afraid if this advice sounds very abstract; you will automatically gain experience while developing projects.

By the way, the code we are going to develop during the chapters is available for download on the Packt Publishing website.

 

Developing the first game


Now that we got the boring parts finished, we can finally start making a game. So where do we start? What do we do first? First, you should have an idea of what kind of game you want to develop, and what elements it will incorporate. For the purpose of this book, we have chosen to create a shoot-em-up game. The player controls an aircraft viewed from the top, and has to find its way through a level full of enemies.

In order to tease you a little, we show you a screenshot we will have at the end of this chapter.

It might not be the most amazing game you have seen so far, but it exemplifies a good point. To make a game, we need a medium for communicating what is going on to the user. For us, that amounts to showing images on the screen to the player, and having a way for the player to manipulate the game.

The Game class

In this chapter, we implement the basis for your game that will get you going. The root for us is a class called Game; instead of doing our logic in the main() function as we did in the minimal example, we move everything into the Game class instead. This is a good starting point—it gives us a better overview of our code, as we can extract separate functionality into their own functions, and use them within the Game class. If we look at the minimal example, we had three distinct areas in the code: initialization, event processing, and rendering. Now if we continued to develop there, these three parts would grow quite a lot, and we would end up with a gigantic wall of code, which would be nearly impossible to navigate. The Game class helps us out here.

Here is the general design of the class and its intended usage:

class Game
{
    public:Game();
        void             run();

    private:
        void             processEvents();
        void             update();
        void             render();

    private:
        sf::RenderWindow mWindow;
        sf::CircleShape  mPlayer;
};

int main()
{
    Game game;
    game.run();
}

As you can clearly see, we replaced all the code in the main() function from the minimal example with just a Game object and a call to its run() function. The idea here is that we have hidden the loop we had previously in the run() function. It doesn't happen very often that we have to fiddle with it anyway. Now, we can move the actual code that updates the game to the update() function, and the code that renders it to the render() function. The method processEvents() is responsible for player input. So if we want to get something actually done, we implement it in one of the three private functions.

Tip

Downloading the example code

You can download the example code files for all Packt books you have purchased from your account at http://www.packtpub.com . If you purchased this book elsewhere, you can visit http://www.packtpub.com/support and register to have the files e-mailed directly to you.

Let's have a look at the code now:

Game::Game()
: mWindow(sf::VideoMode(640, 480), "SFML Application")
, mPlayer()
{
    mPlayer.setRadius(40.f);
    mPlayer.setPosition(100.f, 100.f);
    mPlayer.setFillColor(sf::Color::Cyan);
}
void Game::run()
{
    while (mWindow.isOpen())
    {
        processEvents();
        update();
        render();
    }
}

The function processEvents() handles user input. It polls the application window for any input events, and will close the window if a Closed event occurs (the user clicks on the window's X button).

void Game::processEvents()
{
    sf::Event event;
    while (mWindow.pollEvent(event))
    {
        if (event.type == sf::Event::Closed)
            mWindow.close();
    }
}

The method update() updates the game logic, that is, everything that happens in the game. For the moment, we leave the implementation empty. We are going to fill it as we add functionality to the game.

void Game::update()
{
}

The render() method renders our game to the screen. It consists of three parts. First, we clear the window with a color, usually black. Therefore, the output of the last rendering is completely overridden. Then, we draw all the objects of the current frame by calling the sf::RenderWindow::draw() method. After we have drawn everything, we need to actually display it on the screen. The render() method looks as follows:

void Game::render()
{
    mWindow.clear();
    mWindow.draw(mPlayer);
    mWindow.display();
}

Later in the chapter, when we display something more interesting than a cyan circle, we are going to have a deeper look at the rendering step.

Even though this actually is more code than what we started with, it still looks like it is less, because at any given time, our eyes only have to rest on a smaller part.

And with this you should still get the same result as you would in the SFML minimal example: a cyan-colored circle in a window with a black background. Nothing fancy yet, but we are well on our way.

 

Game loops and frames


Now talking a little more in-depth about the loop we have placed in the run() function. This loop is most often called the main loop or the game loop because it controls the lifetime of an application. As long as this one continues to iterate, the application will stay alive. In our case, we would like our application to terminate its execution as soon as the window ceases to exist.

Now what do we do during an iteration of this loop? First we process the events from the window, then we update the game, and finally we render the results on the screen. An iteration of the game loop is most often called a frame or a tick. You might have heard of the term frames per second (FPS). This is a measurement of how many loop iterations the game can do during a second. Sometimes, the concept of FPS only accounts for rendering times, but it is not unusual for it to encompass the input processing and logic updates as well.

We can explain this visually with a flow chart to further help you see clearly the logic of our loop.

It accurately describes what our application does at the moment. The only thing left out is the event processing. That functionality could have its own flow chart. But it does one task only in our basic example. It tells the window to close itself if the user requests it.

Now, the way we work with the computer in C++ is very linear. Everything is done in the set order that we give it, and the computer does nothing for us unless we explicitly tell it to. So, if we don't tell it to draw the circle, it won't draw it. If the state of the game somehow changes, and we don't tell the computer how to render a new frame, then nothing will change on the screen because the computer won't know that the graphics have changed.

Now that we got this sorted out, let's see if we can get something to happen over several frames. We make the circle move by pressing keys on our keyboard.

Input over several frames

First we have to be able to detect that the user is pressing down a key on his keyboard. SFML provides this functionality in several ways, but for now we will settle with input detection by responding to events.

What are events? The word itself implies something that is happening with our window, emitting a notice of the happening. As soon as the user somehow interacts with our window, the operating system sends an event that we can process. For our convenience, SFML translates events from the underlying operating systems to a uniform structure that we can use with ease: sf::Event. Once the window internally detects that some kind of input has happened, it will store an sf::Event object containing information about that input. We will then poll all those events as fast as we can, in order to respond to them.

SFML supports a wide variety of events, but there are two event types that interest us here: sf::Event::KeyPressed and sf::Event::KeyReleased. They represent a key being pressed and released respectively.

So let's change our code so that it can handle this. We again poll the window for events, and have a case differentiation on the event type.

void Game::processEvents()
{
    sf::Event event;
    while (mWindow.pollEvent(event))
    {
        switch (event.type)
        {

For each time the while loop iterates, it means a new event that was registered by the window is being handled. While there can be many different events, we will only check for some types of events, which are of our interest right now.

            case sf::Event::KeyPressed:
                handlePlayerInput(event.key.code, true);
                break;
            case sf::Event::KeyReleased:
                handlePlayerInput(event.key.code, false);
                break;
            case sf::Event::Closed:
                mWindow.close();
                break;
        }
    }
}

In the handlePlayerInput() function, we check which key on the keyboard has been pressed or released. To store this information, we use four Boolean member variables: mIsMovingUp, mIsMovingDown, mIsMovingLeft, and mIsMovingRight. We set the corresponding variable depending on the key being pressed or released.

void Game::handlePlayerInput(sf::Keyboard::Key key,bool isPressed)
{  
    if (key == sf::Keyboard::W)
        mIsMovingUp = isPressed;
    else if (key == sf::Keyboard::S)
        mIsMovingDown = isPressed;
    else if (key == sf::Keyboard::A)
        mIsMovingLeft = isPressed;
    else if (key == sf::Keyboard::D)
        mIsMovingRight = isPressed;
}

In Game::handlePlayerInput()we receive the enumerator describing the key that was pressed or released. The flag describing whether a press or release occurred is passed as the second argument. So we check what key the user is manipulating, and change our state depending on that.

Now, we have a way to perceive that the user is pressing a key. We know when we want to move up, down, left, and right. We know at each iteration of the main loop exactly, what the user wants; we just have to update the circle with a new position depending on this input. This method gives us a great advantage. So finally we can write something in our update() function, namely, the movement of our player. We check which of the four Boolean member variables is true, and determine the movement accordingly. By using += (instead of =) and if (instead of else if), we implicitly handle the case where two opposite keys, such as right and left are pressed at the same time—the movement stays zero. The update() function is shown in the following code snippet:

void Game::update()
{
    sf::Vector2f movement(0.f, 0.f);
    if (mIsMovingUp)
        movement.y -= 1.f;
    if (mIsMovingDown)
        movement.y += 1.f;
    if (mIsMovingLeft)
        movement.x -= 1.f;
    if (mIsMovingRight)
        movement.x += 1.f;

    mPlayer.move(movement);
}

We introduce two new things here: a vector and the move() function on the circle shape. The move() function does what its name says, it moves the shape by the amount we provide it.

Vector algebra

Vectors are an important part of algebraic mathematics. They imply lots of rules and definitions, which go beyond the scope of our book. However, SFML's sf::Vector2 class template is way more practical, both in concept and functionality. To be as simple as we could possibly be, we know that a coordinate in a two-dimensional Cartesian system would need two components: x and y. Because in graphics all coordinates are expressed with the decimal float data type, sf::Vector2 is instantiated as sf::Vector2<float>, which conveniently has a typedef named sf::Vector2f. Such an object is made to contain two member variables, x and y. This makes our life simpler, because now we don't need to pass two variables to functions, as we can fit both in a single sf::Vector2f object. sf::Vector2f also defines common vector operations, such as additions and subtractions with other vectors, or multiplications and divisions with scalars (single values), effectively shortening our code.

To be a little more precise in the explanation, vectors are not only used to define positions, but they also are a perfect fit to define orientations. So, a vector is great to store a two-component coordinate, be it an absolute or relative position, or even to express a direction to follow, or to shoot a bullet towards. There is a little more to know about two-dimensional vectors, especially if they are directions, such as the concept of normalization or unit vector. This operation applies only to directions, as it makes no sense in positions. We consider a vector normalized if it has length one (hence the term unit vector) and the vector still expresses the same direction as before normalization. The following figure visualizes the vector (2, 3). This vector represents a translation of 2 units to the right and 3 units down.

Please do not confuse sf::Vector2f with std::vector. While their names are similar, the first refers to the mathematical concept; the latter is simply a dynamically allocated array from the standard C++ library.

In our case, our vector called movement expresses a movement from the origin of the current coordinate system. For us, this origin is the shape's position. It might be a bit tricky getting into the whole way of thinking in different spaces if you don't like math.

Vector algebra is very interesting, and definitely something very useful if you know it. So we recommend you study it. Mathematics is your friend as soon as you stop fighting it; it really makes a lot of things easier for you in programming. It is almost safe to claim that this subsection of math is the single most important topic when we need to implement gameplay mechanics. A wide range of problems that you will face in almost any kind of game are already solved and well-studied before, so you're better off learning this subject than reinventing the wheel every time. To avoid leaving you hanging, here's an example: Let's say you have point A and point B, which represent two characters in an action game. When the enemy at point A wants to shoot our player at point B, it needs to know the direction in which to shoot the projectile. Why waste your brains thinking on how to solve this problem if this field of math defines this operation as one of its most basic rules? All you need is to find the direction vector C, which is obtained by calculating B minus A. The difference between two positions gives us the direction between the two. Yes, that easy!

Frame-independent movement

If you run everything we have done so far, you will be able to move the circle, but it won't move uniformly. It will probably be very fast, because currently we have done the movement in a very naive way. Right now your computer will be running the update() function as fast as it can, which means it will probably call it a couple of hundreds of times each second, if not more. If we move the shape by one pixel for every frame, this can count up to several 100 pixels every second, making our little player fly all over the screen. You cannot just change the movement value to something lower, as it will only fix the problem for your computer. If you move to a slower or faster computer, the speed will change again.

So how do we solve this? Well, let's look at the problem we are facing. We are having a problem because our movement is frame-dependent. We want to provide the speed in a way that changes depending on the time a frame takes. There is a simple formula you should remember from your old school days. It's the formula that goes: distance = speed * time. Now why is this relevant for us? Because with this formula we can calculate a relevant speed for every frame, so that the circle always travels exactly the distance we want it to travel over one second, no matter what computer we are sitting on. So let's modify the function to what we actually need to make this work.

void Game::update(sf::Time deltaTime)
{
    sf::Vector2f movement(0.f, 0.f);
    if (mIsMovingUp)
        movement.y -= PlayerSpeed;
    if (mIsMovingDown)
        movement.y += PlayerSpeed;
    if (mIsMovingLeft)
        movement.x -= PlayerSpeed;
    if (mIsMovingRight)
        movement.x += PlayerSpeed;

    mPlayer.move(movement * deltaTime.asSeconds());
}

The major difference we have made here is that we now receive a time value every time we call the update. We calculate the distance we want to travel every frame, depending on how much time has elapsed. We call the time that has elapsed since the last frame delta time (or time step), and often abbreviate it as dt in the code. But how do we get this time? We are lucky because SFML provides the utilities for it.

In SFML, there is a class that measures the time from when it was started. What we have to do is to measure the time each frame takes. We are talking about the class sf::Clock. It has a function called restart(), which lets the clock return the elapsed time since its start, and restarts the clock from zero, making it ideal for our current situation. SFML uses the class sf::Time for all time formats; it is a convenient data type that can be converted from and to seconds, milliseconds, and microseconds. Here's the modified Game::run() member function:

void Game::run()
{
    sf::Clock clock;
    while (mWindow.isOpen())
    {
        sf::Time deltaTime = clock.restart();
        processEvents();
        update(deltaTime);
        render();
  }
}

There is no big difference; we create a clock, and in every frame we query it for its current elapsed time, restart the clock, and then pass this time to the update function.

Fixed time steps

The solution we have come up with so far is sufficient for many cases. But it is not a perfect solution, because you can have problems in certain scenarios where delta times vary strongly. The code can be quite hard to debug, because it is impossible to get 100 percent reproducible results, since every frame is unique, and you can't guarantee that the delta time remains the same.

Consider that a frame may sometimes take three times the average delta time. This can lead to severe mistakes in the game logic, for example, when a player moves three times the distance and passes through a wall he would normally collide with. This is why physics engines expect the delta time to be fixed.

The following is a figure describing the problem we are referring to:

What we will do now is use a technique called fixed time steps. We write code that guarantees that in any circumstances, we always give the same delta time to the update function, no matter what happens. If you find that sounding difficult, there is no big difference from what we already have. We just have to do some book-keeping in our code for how much time has passed since we last called the update() function.

void Game::run()
{
    sf::Clock clock;
    sf::Time timeSinceLastUpdate = sf::Time::Zero;
    while (mWindow.isOpen())
    {
        processEvents();
        timeSinceLastUpdate += clock.restart();
        while (timeSinceLastUpdate > TimePerFrame)
        {
            timeSinceLastUpdate -= TimePerFrame;
            processEvents();
            update(TimePerFrame);
        }
        render();
    }
}

The actual effect of this change is that we accumulate how much time has elapsed in a variable timeSinceLastUpdate. When we are over the required amount for one frame, we subtract the desired length of this frame (namely TimePerFrame), and update the game. We do this until we are below the required amount again. This solves the problem with variable delta times, as we are guaranteed that the same amount of frames is always run. In the application you can download, the logic frame rate will be set to 60 frames per second by having the TimePerFrame constant equal to sf::seconds(1.f / 60.f).

Eventually, we have two while loops. The outer one is the game loop as we know it, and calls the render() method. The inner one collects user input, and computes the game logic; this loop is executed at a constant rate. If rendering is slow, it may happen that processEvents() and update() are called multiple times before one render() call. As a result, the game occasionally stutters, since, not every update is rendered, but the game doesn't slow down. On the other hand, fast rendering can lead to render() being called multiple times without a logic update in between. Rendering the same state multiple times does not change anything on the screen, but it allows for techniques such as interpolations between two states to smoothen the game flow.

If you are interested in the topic, there is a famous article with detailed explanations at http://gafferongames.com/game-physics/fix-your-timestep.

Other techniques related to frame rates

SFML provides a few utilities that are worth knowing with respect to time handling and frame updates. One of them is sf::sleep(), a function that interrupts the execution for a given time, which gives the processor an opportunity to work on other tasks. Sleeping is not very accurate, so you should not use it for exact timing purposes. The method sf::RenderWindow::setFramerateLimit() tries to achieve the specified frame rate by calling sf::sleep() internally. It is a nice function for testing purposes, but it also lacks precision.

Another important technique is vertical synchronization, also known as V-Sync. Enabled V-Sync adapts the rate of graphical updates (calls of sf::RenderWindow::display()) to the refresh rate of the monitor, usually around 60Hz. This can avoid graphical artifacts such as screen tearing, where a part of your window shows the old frame, and another the new one. You can enable or disable V-Sync using the method sf::RenderWindow::setVerticalSyncEnabled().

 

Displaying sprites on the screen


Now let's get something interesting on the screen. Instead of just rendering a boring single color circle to the screen, let's actually render an image. To do this, SFML provides a couple of tools to make your life easy. First we have the sf::Texture class that holds the actual image data loaded from the hard drive. Next is the sf::Sprite class that represents an instance with position and orientation in the scene. The texture describes the image, while the sprite describes where and how to put one on the screen.

A simple example of their relationship is as follows:

sf::Texture texture;
if (!texture.loadFromFile("path/to/file.png"))
{
  // Handle loading error
}
sf::Sprite sprite(texture);
sprite.setPosition(100.f, 100.f);
window.clear();
window.draw(sprite);
window.display();

Here, we load a texture from the hard drive, and check if loading has succeeded. We construct a new sprite to use the texture and set its position to (100, 100), relative to the upper-left window corner.

File paths and working directories

About file paths, it would be useful to make some things clear. First, the slashes that separate directories in a path in order to locate a file will be most often forward slashes (/), especially in Unix-like operating systems. In Windows, you will occasionally see back slashes (\) used instead, but probably not everywhere. Do not let this confuse you, the only true cross-platform way to specify a file path is using forward slashes, so make sure they are always your choice, even in Windows.

Also, it is of extreme importance to note that every program being run in the operating systems that SFML supports has a working directory while being executed. Usually, when you run the program from a graphical window manager, the working directory is exactly where the executable is located; however, this is not always the case. It is possible to run a program with any working directory, and it is even possible to change the working directory programmatically from inside the program. In the context of a game, the point of understanding working directories is for loading files, which are loaded in paths relative to the executable in most cases. All paths passed on to SFML are relative to the working directory, unless you make them absolute by starting them with a slash in Unix-like operating systems, or with a drive letter in Windows.

Real-time rendering

In a game simulation, it is highly likely that there will be changes to what's drawn in every frame. Even when there is no player input, you will observe in most cases some kind of animation, which will effectively change the end result on the screen for that given frame.

Many programs use a render-on-demand approach. They will only redraw a new frame on the screen when a change actually happens, minimizing the performance costs in applications where the screen doesn't change very often. But, by the nature of gaming software, this approach would be made redundant, as we would be requesting new frames all the time. Because of this, game programmers adopted entirely the concept of real-time rendering, which ignores frame requests as they were previously known, and blindly draws to the screen as fast as possible. If while playing your favorite game you eventually noticed a usual FPS count of 30 or 60, this is due to the fact that our eyes can't perceive, in regular circumstances, more frames than that amount in a second. Because it makes virtually no difference to the end user, the programmers limit the FPS count to such a number, and save the processor power for other tasks, such as logic processing. In short, nowadays, the whole scene is rendered again and again, independently of what changed since the last frame.

To explain the notion of real-time rendering a bit further, we would like to mention the concept of double buffering that comes inherently attached to it. Double buffering is a technique that was created to negate graphical glitches derived from asynchronies in the rendering tasks. Before this technique became widely used, programmers needed to have additional concerns when drawing to the screen, to ensure that only what belongs to a frame is drawn in it, and that there are no remains of pixels from previous frames.

Double buffering defines two virtual screens to draw graphics to. The front buffer and the back buffer are the names chosen to address these render targets. The front buffer is what is currently displayed on the screen, while the back buffer is the one we are drawing to at the moment, preparing a new frame. Once our frame is fully drawn in the back buffer, we use the SFML's sf::RenderWindow::display() function to put the contents of the back buffer on the screen. The back buffer becomes the front buffer, while the front buffer that was set will now be the back buffer to draw on. This way we ensure that we always have a buffer with the previous frame unharmed, as well as a working buffer that we can safely change at any time, without affecting what is on the screen. By clearing, drawing, and then displaying these buffers in an interleaved manner, we achieve double buffering.

Adapting the code

In our code, we replace sf::CircleShape with sf::Sprite, which only requires minor modifications. We load an image file called Eagle.png. The relative path to it is Media/Textures. Don't worry if you don't have the file; you can download it together with the whole source code.

// Game.hpp
class Game
{
    public:
                    Game();
        ...
    private:
        sf::Texture mTexture;
        sf::Sprite  mPlayer;
        ...
};

// Game.cpp
Game::Game()
: ...
, mTexture()
, mPlayer()
{
    if (!mTexture.loadFromFile("Media/Textures/Eagle.png"))
    {
        // Handle loading error
    }
    mPlayer.setTexture(mTexture);
    mPlayer.setPosition(100.f, 100.f);
}

No code changes have to be done to get our render() function to work with sprites instead of shapes. SFML is nice in that way. Everything that can be drawn to a window has to inherit from the abstract class sf::Drawable. As long as it is possible, the SFML API keeps a consistency to it. This makes it a lot easier to use, because if you have used a function on one class, you can be pretty sure there will be identical or similar functions in other classes.

Now you have achieved the results you observed in the screenshot we had in the beginning of the chapter. You are also very well on the way to making your game. With these few tools alone you would be able to make a simple game, such as Snake or Pac-Man with ease.

 

Summary


We had to cover a lot of ground in this chapter. We have explained the most basic concepts that are required to create a game; concepts that you should always have present in order to save yourself from losing time in such issues, and instead, focus on making a great game.

In this chapter we:

  • Learned what SFML is, and what functionality it provides

  • Listened to input, and moved the player over several frames

  • Rendered an image to the screen

  • Learned about game loops and delta times, and saw the strengths and weaknesses of different approaches to handle time steps.

And if you are interested in how you would measure your FPS and render that to the screen, then have a look at the code base. We have implemented that functionality there, and it is based on the subjects we have already covered. We would recommend you try yourself; the only things you need are sf::Text and sf::Font in order to render text on the screen. You can learn more about these classes in SFML's API documentation at www.sfml-dev.org.

This concludes our introduction chapter. From now on we are going to investigate different aspects of SFML and game development in a more detailed manner. In the next chapter, we start with resource handling, which explains the backgrounds behind textures, fonts, and other resources.

About the Authors

  • Jan Haller

    Jan Haller is a Master's degree student of Electrical Engineering and Information Technology. In his free time, he occasionally develops games in C++. He is also interested in the creation of graphics and 3D models. In 2008, Jan stumbled upon SFML. Since then, he has used the library for many of his works. One of his bigger projects was a 2D Jump'n'Run game, where the character has to find its way through different levels, defeating enemies and collecting various items. During the years, Jan has actively participated in the SFML community, getting a lot of insights into the development of SFML 2. He has also written a C++ library called Thor, which extends SFML by ready-to-use features such as particle systems or animations.

    Browse publications by this author
  • Henrik Vogelius Hansson

    Henrik Vogelius Hansson has always been in love with both games and programming. He started his education fairly early and continued on into the indie scene with Defrost Games and their game Project Temporality. The next company that hired him was Paradox Development Studio where he got to work on titles such as Crusader Kings 2. Beside the game companies, Henrik has also been very active in the SFML community and has even provided a binding for Ruby called rbSFML.

    Browse publications by this author
  • Artur Moreira

    Artur Moreira is a Portuguese Software Engineer, who has been working with C++ since the first days he started to learn the ways of a software engineer, around twelve years ago. During this process, there were many significant steps that allowed gaining experience in many topics both inside the C++ development world and its surrounding ecosystem of tools. The first years were focused on learning and getting some practical experience around many kinds of development from 2D games to frontend applications and code libraries. From 2012, he has worked as a game and engine programmer, independently, over the course of three years and wrote his first book SFML Game Development, available in the Packt store. In 2016 he started working in a company named WIT Software in big telecom projects for huge clients like Vodafone Group and Reliance; a social communications and media suite of applications for all platforms. After that, in 2017, he started working for Euronext, the leading pan-European stock exchange, developing the next generation of trading chain software, which he currently works in as of the current date. During all the later years, he kept working on game projects in the free hours. One of the most cherished personal projects being developed by Artur is a new programming language with a focus on expressiveness, which would ultimately be used for developing better games.

    Browse publications by this author

Latest Reviews

(11 reviews total)
Excellent
Excellent
Excellent
Book Title
Access this book, plus 8,000 other titles for FREE
Access now