SFML Essentials

4.2 (5 reviews total)
By Milcho G. Milchev
  • Instant online access to over 7,500+ books and videos
  • Constantly updated with 100+ new titles each month
  • Breadth and depth in over 1,000+ technologies

About this book

SFML is a cross-platform, object-oriented multimedia API that is written in C++. It provides a simple interface to ease the development of games and multimedia applications.

This book will guide you through everything you need to know about building a 2D game in SFML. Concepts such as Sprites, Textures, Animation, and Cameras are explored in depth and finally the book ends with advanced topics like shaders and networking. You will also learn how to play sound and music on top of the gameplay. Every step through the journey is filled with examples in C++ to guide you in the right direction. By the end of the book you will feel confident about creating 2D games with SFML, without investing too much time on it.

This book contains a set of fast-paced tutorials about the core features of SFML.

Publication date:
February 2015
Publisher
Packt
Pages
156
ISBN
9781784397326

 

Chapter 1. Getting Started with SFML

In this chapter, we will explore the window and graphic modules of SFML and focus on strengthening our basics. We will also see how a typical game loop looks like in SFML, and how we can handle input to manipulate shapes on the screen.

In this chapter, we will cover:

  • Window creation

  • The game loop

  • Event handling

  • Shape rendering and transformations

 

Creating windows


The first thing you would probably want to do when you start developing a game is open a window. In SFML, this couldn't have been made any easier. Only one line of code is necessary to create a window:

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.

The only thing that the main function does is initialize the window variable by calling the sf::Window constructor, after which the program exits. There is an alternative way to open a window by using the default constructor and calling Window::create() later on. This function takes exactly the same arguments as the constructor, which we just went through. If Window::create() is called on a window, which is already open, it closes the window and reinitializes it with the new set of parameters.

In the example given in the preceding screenshot, notice that both Window and VideoMode are in the sf namespace. Every class in SFML is under that namespace, which separates all the classes in SFML from the classes in other libraries.

If we run the code in the example, we won't see much. The program exits immediately after it creates the window. That is because we just create a window without doing anything with it, and the program naturally exits after it reaches the end of the main() method. The fact that we created a window doesn't mean that it is fully functional (at least not yet). We will have to program it according to what we want it to do. Now, let's block the main function from finishing, by delaying the window's thread. SFML provides a simple interface for that; just add the sf::sleep(sf::seconds(3)) line after the line which creates the window. Now, the window is clearly visible for the duration of the sleep.

We can specify various configurations while creating the window—window size, title, style, and graphics settings. You may have noticed that we pass two arguments to the the Window constructor—an instance of VideoMode and a string (the title). The constructor can actually take up to four parameters, the last two being optional—Style and ContextSettings. The next part covers what those arguments mean, and how to use them.

VideoMode

The VideoMode class contains information about the video mode of the window, such as width, height, and bits per pixel. This last parameter is the number of bits used to represent the color of a single pixel. It has a default value of 32, which is unlikely to change on recent hardware. For example, a value of 8 would produce a monochrome result. If we want to create a fullscreen window, the supplied values have to be supported by the machine's monitor and graphics card. If we choose invalid arguments for a fullscreen window, the window creation will simply fail. The validity of the VideoMode class can can be checked with the VideoMode::isValid()method, which returns a boolean as the result.

If we need to create a window according to the size (or the pixel depth) of the desktop mode, VideoMode::getDesktopMode() is available as a static method. On the other hand, if we were to create a window in fullscreen mode, we might want to check the available resolutions with VideoMode::getFullScreenModes(). This returns std::vector of video modes, and we can choose one of the modes ourselves, or let the user decide which suits them best.

However, merely specifying fullscreen video mode is not enough to create a fullscreen window. We need to set a style as well.

Style

The Style argument is a bit mask. A mask is a combination of flags where each flag represents a specific bit of the mask. In this case, the flags are stored in an enum in the sf::Style namespace. We can use a combination of flags to create the desired mask. Here is what SFML offers in terms of styles:

Enum value

Description

sf::Style::None

The window doesn't have any decorations, and it cannot be combined with any other style.

sf::Style::Titlebar

This adds a titlebar.

sf::Style::Resize

This adds a maximize button. This also enables the window to be manually resized.

sf::Style::Close

This adds a close button.

sf::Style::Fullscreen

This launches the window in fullscreen mode. Note that this cannot be combined with any other style and requires a valid video mode.

sf::Style::Default

This combines Titlebar, Resize, and Close. This is the default style.

The way you can combine different styles is by using the bitwise OR operator. So in a case where we want a window with a titlebar and a close button, we write:

sf::Uint32 style = sf::Style::Titlebar | sf::Style::Close;

The only thing left to do here is to pass that style as the third argument of the Window construct.

ContextSettings

The final argument is an instance of ContextSettings. This structure is a collection of settings, which describes the desired rendering context. SFML uses OpenGL for rendering under the hood and thus those settings are directly relevant to it. The available context settings are as follows:

  • depthBits: This refers to the number of depth buffer bits

  • stencilBits: This refers to the number of stencil buffer bits

  • antialiasingLevel: This refers to the requested number of multisampling levels

  • majorVersion and minorVersion: These refer to the requested version of OpenGL

Each of these settings will be explained in more detail in Chapter 5, Manipulating a 2D Camera, where you will learn how to render things directly using OpenGL.

Disabling the mouse cursor

The Window class has a method which sets cursor visibility on or off—Window::setMouseCursorVisible(). This is useful for games that don't use a cursor, or when we want to change the image of the cursor to something different to the default.

 

The game loop


Every game needs a loop. This is what keeps it going. Otherwise the program will just end, and we will not be able to see much. Here is what a typical game loop looks like:

A typical game loop has three main stages:

  • Input handling

  • The update frame

  • The render frame

Input handling in SFML can be done either through capturing events, which have been dispatched by the window, or by directly querying input devices for their current state. Both methods have different uses. For example, we might want to close the window on a button press event, or to move our main character to the right as long as a certain key is pressed (direct key query).

We reach the update frame stage after we capture and use our events. This is the stage where we want to advance our game logic and update our world state.

The final stage of the loop comes right after we finish updating our objects. Here, we clear everything that has been drawn from the last time and render every object on the screen again.

Going back to the example of our game loop, it currently doesn't perform the things it's supposed to and, if we try to run the code, it becomes obvious that the window doesn't respond to inputs. This is because we don't perform the first of the three important steps in the loop—handling the input.

 

Event handling


Events can be polled from the window by bool Window::pollEvent(sf::Event& event). If there is an event waiting to be handled, the function will return true, and the event variable will be filled with the event data. If not, the function returns false. It is also important to note that there might be more than one event at a time; so we have to be sure to capture every event possible. Here is what a typical event loop looks like:

Running the code now produces more satisfactory results—we can move the window around, resize, and minimize it. However, there is still one problem—the close button doesn't work. SFML doesn't assume that the window should close after the user clicks on the close button. Maybe we want to save the player's progress or ask them if they are sure first. This means that we have to implement the close button functionality ourselves.

Before we proceed, it is important to note that the Event class in C++ contains a union. This means that only one of its members is valid. Accessing any other member will result in undefined behavior. We can get the valid member by looking at the type of the event.

The event types can be logically split into four sections, depending on what they are related to:

  • Window

  • Keyboard

  • Mouse

  • Joystick

Window related events

Enum value

Member associated

Description

Event::Closed

None

This event is triggered when the OS detects that the user wants to close a window—the close button, key combination, and so on.

Event::Resized

Event::size holds the new size of the window

This event is triggered when the OS detects that the window has been resized manually, or when Window::setSize() has been used.

Event::LostFocus

Event::GainedFocus

None

This event is triggered when the window loses or gains focus. Windows which are out of focus don't receive keyboard events.

Keyboard related events

Enum value

Member associated

Description

Event::KeyPressed

Event::KeyReleased

Event::key holds the pressed/released key

This event is triggered when a single button is pressed or released on a focused window.

Event::TextEntered

Event::text holds the UTF-32 unicode value of the character

This event is triggered every time a character is typed. This produces a printable character from the user input, and is useful for text fields.

Mouse related events

Enum value

Member associated

Description

Event::MouseMoved

Event::mouseMove holds the new mouse position

This event is triggered when the mouse changes its position inside the window.

Event::MouseButtonPressed

Event::MouseButtonReleased

Event::mouseButton holds the pressed/released button and the mouse position

This event is triggered when a mouse button is pressed inside a window. Five buttons are currently supported—left, right, middle, extra button 1, and extra button 2.

Event::MouseWheelMoved

Event::mouseWheel holds the delta ticks of the wheel and the mouse position

This event is triggered when the scroll wheel moves inside a window.

Joystick related events

Enum value

Member associated

Description

Event::JoystickConnected

Event::JoystickDisconnected

Event::joystickConnect holds the ID of the joystick just connected/disconnected

This event is triggered when a joystick connects or disconnects.

Event::JoystickButtonPressed

Event::JoystickButtonReleased

Event::joystickButton holds the number of the button pressed and the joystick ID

This is triggered when a button on a joystick is pressed. SFML supports a maximum of 8 joysticks with up to 32 buttons each.

Event::JoystickMoved

Event::joystickMove

holds the moved axis, the new axis position, and the joystick ID

This is triggered when an axis of a joystick is moved. The move threshold can be set via Window::setJoystickThreshold(). SFML supports up to 8 axes.

Using events

After we get the event by calling Window::pollEvent(), we can check its type by looking at Event::type. The event is of type Event::EventType, which is an enum inside the Event class. Here is how a typical close event can be implemented:

Here, the Window::close() function will take care of closing the window. If a window variable gets out of scope, the destructor is called, and the window is closed.

If we want to handle more than one event, it makes sense to use the switch statement, as it improves readability. Let's see how the keyboard key presses and releases work:

The code in the preceding figure demonstrates how we can capture events to change the the title of the window every time the Space key is pressed and released. Apart from that, when the Escape key is released, the window closes. Notice that Event::key contains a member called code, which is an enum of type Keyboard::Key. You can use this formula to handle the rest of the event types without much difficulty. However, the case with Event::EventType::TextEntered is a bit more interesting. A single key press/release can be detected and handled in a relatively straightforward manner. When it comes to certain specific characters though, things start to get a bit more complex. For example, if we want to detect when the ! symbol has been typed, we have to look up whether two individual keys have been pressed at the same time (Shift + 1 on most keyboard layouts). In such cases, SFML saves us a lot of work by providing the simple and easy-to-use TextEntered event. The event is only fired when a combination of keys representing a character are pressed; meaning that a single key (only Shift, for example) might not trigger the event. Of course, if K alone is pressed, the event will be fired normally, and will contain the character.

Take a look at this example where a string is assembled out of characters using the TextEntered event and, when the Enter (or Return) button is pressed, the text is set as the title:

Note that the string buffer that we are using is of type sf::String and not std::string. The sf::String class is created to automatically handle conversion between string types and encodings. As such, we do not have to worry about the language or symbols on a keyboard layout—it can store any character from any language.

To finish off event handling, it is important to mention that there is an alternative to how events are pulled from the window. Apart from using Window::pollEvent(), we can also use bool Window::waitEvent(Event& event), which blocks the thread until an event is received. It only returns false when something wrong occurs inside (an error or exception of some sort), otherwise it always returns true. This can be useful when we require the user to do something before the application can continue, or if we want to handle the input on another thread, for example. In the latter scenario, only that thread will be blocked, allowing the game loop to continue running. Now that we've discussed events, let's move on to something more interesting—rendering.

 

Shape rendering and transformations


Obviously, we wouldn't need windows if there weren't any objects to be rendered, and we wouldn't need events if we didn't want to use the input to animate those objects. SFML provides quite a few ways for us to render objects on the screen, and we will explore the main ones in this book. Before we proceed to rendering though, we need to make sure that our render cycle is in place and in correct order.

The render frame

Do you, remember the Window class? That isn't of much use now, since it doesn't provide an interface to draw SFML shapes. We have to use a class called RenderWindow to do that. This class is derived from the Window class and adds the drawing functionality. Don't worry though, it doesn't strip any functionality from its parent, it just adds more functionality on top of it. So we can still create it, poll events, and so on, in the same way we do with the base class Window. Here is an example of a game loop with a render cycle:

It is important to note here that the RenderWindow class is from the SFML graphics module, which means that we have to include <SFML/Graphics.hpp>, rather than <SFML/Window.hpp> . However, since it is derived from the Window class it can still be used in our code without changing anything but the variable type.

The render cycle will look quite straightforward, if you have any previous game programming experience. Basically it breaks down to this:

  • Clear the canvas you intend to draw on

  • Draw onto the canvas

  • Display the canvas

This render routine happens in every frame (the loop cycle). If you are not familiar with the rendering process, it might seem a bit odd and wasteful to throw away everything from the last frame and render all objects in the scene again (even those which haven't changed since the last time). However, graphics cards are well optimized to cope with this routine, and maximize efficiency and performance wherever possible. Avoid using any other structure, since it will only slow you down without bringing any major benefits.

Another thing to note is that the canvas which we are rendering on is double buffered. This is very common in rendering. The way this works is quite simple—the canvas has two sides that you can render on. Throughout the render frame, we work only on one of the sides—the one which is not shown on the screen. After we finish rendering, we flip the canvas and show what we've done. In the next frame, we work on the other side of the canvas, and so on. This technique allows us to show the scene only after we've finished rendering it. In SFML, we flip the canvas (it's also sometimes known as "swap the buffers") by calling Window::display().

Apart from that, the Window::display() method can put the to thread sleep for a calculated amount of time to achieve a target framerate (frames per second). We can set the desired framerate by calling Window::setFramerateLimit() once at the beginning of the program. The function doesn't guarantee the limiting of the framerate to the exact amount we pass it, but rather it makes a close approximation.

Window::clear() clears the canvas for redrawing. Notice that it takes a sf::Color argument, which is an RGBA representation of a color. We can initialize it manually by calling the constructor and passing each value individually, or we can use one of the preset colors. For example Color::Red, Color::Blue, Color::Magenta, and more.

Shape drawing

Now that we are familiar with the render routine, let's render some shapes on the screen. We will start with the basic shapes and explore the alternatives later on. When we want to draw a shape, we have to create the object first. Here is the initialization code for two shapes. Place it just before the game loop.

A few new classes make an appearance in this example—CircleShape, RectangleShape, and Vector2f.

You can probably guess what the Vector2f class is for—it is a 2D vector which holds two floats. There are also classes such as Vector2i (for integers), Vector2u (for unsigned integers), Vector3i (for 3D vectors which hold integers), and Vector3f (3D vectors which hold floats). We can even create our own 2D and 3D vectors, which hold custom types, by using the template classes sf::Vector2<class> and sf::Vector3<class>.

CircleShape, RectangleShape, and ConvexShape derive from the abstract class Shape, which is defined by a set number of vertices (points). The CircleShape is just a regular polygon with a set number of vertices. We can specify how detailed the circle should be with the second argument in the constructor, which is optional, with a default value of 30. On the other hand, RectangleShape always has four vertices. The constructors of both shapes take their dimensions—the radius of the circle and the width and height of the rectangle.

ConvexShape is a shape for which we have to specify the vertices explicitly. There isn't a restriction on the number of vertices, but they have to form a convex shape, otherwise the shape will not be drawn correctly.

Apart from that, shapes can have colors and outlines, which can be modified with Shape::setFillColor(), Shape::setOutlineColor() and Shape::setOutlineThickness(). This last one sets the number of pixels for the outline.

To render the preceding shapes, we can use the RenderWindow::draw() function. Here is how we can implement it into our render frame:

Running the code produces the following result:

We can immediately note that the render order makes a big difference. Background objects have to be rendered first, followed by anything in the foreground. In this example, the circle is rendered first and so it is in the background, whereas the rectangle sits on top of the circle in the foreground.

We can use ConvexShape by specifying the number of points with the ConvexShape::setPointCount() function, and set those points in order with ConvexShape::setPoint(). Here is an example with a triangle:

After drawing it in the window, we get a nice blue triangle:

There is no support for a concave shape in SFML. However we can still draw concave shapes by creating multiple convex ones and rendering them in the correct places. If triangles are used for the job, the method is called Polygon triangulation.

Shape transformation

We know how to draw shapes on the screen now and that's great. However, no matter how many of them we draw, they all seem to go on the top-left side of the screen. This means that we need to change the position of the shapes. This can be done with the help of a function called Shape::setPosition(). There are also functions for the Shape::setRotation() rotation and the Shape::setScale()scale. Actually, those functions are all part of sf::Transformable, which the Shape class derives from. As with most things in SFML, using these functions is extremely easy and intuitive:

Don't forget to render the shape after you get it initialized. Here is the result:

Note that we are creating a rectangle, which is actually a square, with a width and height of 50 pixels. However, we are scaling it at 2:1 and thus it is rendered longer than its original size. The next thing which we need to mention is that the rectangle is slightly tilted, which is expected, as we are rotating the rectangle by 30 degrees. In this example, we are setting the position directly to (50, 50). However there is an alternative method to move transformable objects and it's by calling Transformable::move() and passing a vector, which indicates how much we want to move the object from its current position.

Everything that we have created so far has been mostly static, so now let's add a little life to our objects. For that, we need to use the update frame, which we haven't been able to utilize yet. That's the part of the frame just before we start the render frame. Remember, typically a game frame (loop cycle) goes like this:

  • Handle input

  • The update frame

  • The render frame

It is important to update objects before we render them, otherwise their current state will not be rendered correctly—they will be rendered with their state from the last frame.

The next example shows how we use a combination of translation and rotation to create a simple animation:

A couple of lessons can be taken from this example. The first one is how and where to set the framerate limit—just after the initialization of the window. This will limit our game logic somewhere close to 60 frames a seconds. Keep in mind that this controls the upper limit of the framerate. If the frames start taking more than 1/60 seconds to complete (handle events, update objects, and render), then the framerate will drop below 60. However, with our simple code, that is extremely unlikely.

You've probably noticed a new function called RectangleShape::setOrigin(). The origin of an object determines how it should be rendered on the screen. It serves as a center point for the translation, rotation, and scale for the object. In the preceding example, we have a square with size 50 x 50. The center of that square is (25, 25), so we need to set that as the origin of the object. Otherwise, the object will start rotating around its default origin—(0, 0). One last thing to note about the origin is that it's part of the Transformable class and so all of its derived classes have access to it.

As far as our animation goes, the process is quite simple. In every frame, we rotate the square by 1.5 degrees and move it by 1 pixel to the right. By setting the framerate to 60 FPS, we can estimate that, after each second, the square will rotate by approximately 90 degrees (1.5 x 60) and move by 60 pixels to the right (1p x 60). However, performing the game logic in such a way (relying on the framerate) is extremely unreliable and dangerous. We will explore how to manage time while performing animations and game logic in Chapter 3, Animating sprites.

Now, let's look at how we can control shapes in real time.

Controlling shapes

One way to make shapes move is by using event handling. We start moving the object when the player clicks on a key, and we can stop moving the object when that key is released. Here is the screenshot of a code example:

The moving variable determines if we should move our object in the current frame. That variable's value is changed when we press or release the Right Arrow Key. However, that is a lot of code for essentially very little information—"is the Right Arrow Key currently pressed?" Fortunately for us, SFML provides a very simple interface to check the current state of the input devices (keyboard, mouse, and all joysticks).

Checking the state of a key is no harder than calling a single static function—Keyboard::isKeyPressed(). When we pass a key value as an argument, we get the status of whether that key is currently pressed. However, this function doesn't take into account the focus of the window. So imagine that a player minimizes the window and browses the internet. The function will still return true if the player presses the given key.

With this in mind, the given code can be very easily reworked to something much more pleasant:

As you can see, we don't need to store any key state in this case—SFML does that for us.

We can check the states of other input devices in a similar way. The mouse has functions to get its position, the state of any of its buttons, as well as the setting of its position relative to the desktop (not any of the windows). To get the position, you can use Mouse::getPosition(). To set the position, you can use Mouse::setPosition(). Finally, to check whether a button is pressed, call Mouse::isButtonPressed(). All of these work out of focus as well.

Finally, there are joysticks. Since all functions are static, we need to specify which joystick we are looking for with its argument Id. The functions are as follows:

Function

Arguments

Description

Joystick::isConnected()

ID

This function checks whether the joystick with the given ID is connected

Joystick::hasAxis()

ID, axis

This function checks whether the joystick has the specified axis

Joystick::getButtonCount()

ID

This function gets the number of buttons on the joystick

Joystick::getAxisPosition()

ID, axis

This function gets the value of an axis in the range [0, 1]

Joystick::isButtonPressed()

ID, button

This function checks whether a button on a given joystick is pressed

Let's now discuss one final example, which combines a lot of topics from this chapter. We have taken a very simple game where the player plays as a green square, and he should reach the blue square without touching anything red. The following is a helper function, which helps us initialize similar RectangleShape objects easily and without much code repetition:

The initShape() function is quite straightforward—it takes a shape, vector, color, and assigns them to the RectangleShape object. The function also sets the origin point of the shape to its center.

The next step is to initialize the objects:

The first thing we have to do is open a suitable window. Secondly, we need to set the framerate limit to the standard 60 frames per second. The next thing on the list is a sf::Vector2f variable, which we will use as a spawn point for the player. After we initialize the square of the player, we initialize the target, a blue square a bit further to the right in the world. The last shape is a square, which the player has to avoid. It stands somewhere in the middle.

The updated frame, where all the game logic happens, looks like this:

We can clearly see from the preceding code that the player always moves to the right—the player has no control over it. You can change that, so that the player has control of all four directions—out of personal preference. Currently, the only directions in which the player can move are up and down by using the arrow keys.

Apart from the input handling, we need to check whether the code has the logic for both win and lose conditions. We need a method to handle collision detection between these rectangles. Thankfully, all shapes in SFML have two functions called Shape::getGlobalBounds() and Shape::getLocalBounds(), which return sf::FloatRect, which represents the global or local bounds of the current shape. The bounding rectangle of a shape (sometimes called a bounding box) is the rectangle with the smallest possible surface area, which contains the whole shape. Global and Local refer to the transformation of the shape—if the shape is transformed in any way, the position, scale, and rotation are ignored in the local bounds; whereas in the global bounds, they are taken into consideration. Once we have the global bounds, we can use a function called FloatRect::intersect() that takes another FloatRect and returns if the two rectangles collide. Do not confuse RectangleShape with the FloatRect class though, they serve different purposes—the former is for drawing and the latter is a simple class for storing a rectangle's attributes (top, left, bottom, and right values).

The game logic itself is quite simple. If the player collides with the target square, the player wins (the player should exit the game.) If the player collides with a bad rectangle, the player loses (the player should restart the game.)

As you can imagine, there is nothing special about the render frame, you just have to draw all three shapes in any order you wish and run the code. The result will be similar to this:

Feel free to change the code as much as you like and experiment with different settings (add more shapes and build on the game logic maybe?).

 

Summary


You should feel good about yourself after completing this chapter. This means that you are ready for everything that this book offers. This chapter builds the very foundations on which your game will eventually stand. Although the examples here are simple to understand, they aren't supposed to be copied and used as they are. They are there to help you understand how SFML is built, and how you can use that knowledge to your advantage.

In the next chapter, we will cover textures, sprites and resource management. Stick around—there are treats to be had.

About the Author

  • Milcho G. Milchev

    Milcho G. Milchev has been a game programmer since he was in high school. He continues to work on games on a daily basis. Some of the games that he has worked on include Unholy Flesh and WhiteCity. Currently, he works for Dream Harvest, an indie game studio with a lot of talented people. They are developing their first strategy game. Milcho has also followed the development of SFML for several years and has developed his own game engine based on this library.

    Browse publications by this author

Latest Reviews

(5 reviews total)
Excellent
the book wasn't very useful, I couldn't load the programming software it required. I tried on two pc's a windows 7 pro pc and windows 10 pc and couldn't get either one to load the required software.
Excellent

Recommended For You

SFML Game Development By Example

Create and develop exciting games from start to finish using SFML

By Raimondas Pupius
SFML Game Development

If you’ve got a firm grasp of C++ with a secret hankering to create a great game, this book is for you. Every practical aspect of programming an interactive game world is here – the only real limit is your imagination.

By Jan Haller and 2 more
SFML Blueprints

Sharpen your game development skills and improve your C++ and SFML knowledge with five exciting projects

By Maxime Barbier
C++ Game Development By Example

Explore modern game programming and rendering techniques to build games using C++ programming language and its popular libraries

By Siddharth Shekar