Opening a window
As you probably know, drawing something on screen requires a window to be present. Luckily, SFML allows us to easily open and manage our very own window! Let's start out as usual by adding a file to our project, named Main.cpp
. This will be the entry point to our application. The bare bones of a basic application look like this:
#include <SFML/Graphics.hpp>
void main(int argc, char** argv[]){
}
Note that we've already included the SFML graphics header. This will provide us with everything needed to open a window and draw to it, so without further ado, let's take a look at the code that opens our window:
#include <SFML/Graphics.hpp> void main(int argc, char** argv[]){ sf::RenderWindow window(sf::VideoMode(640,480), "First window!"); while(window.isOpen()){ sf::Event event; while(window.pollEvent(event)){ if(event.type == sf::Event::Closed){ // Close window button clicked. window.close(); } } window.clear(sf::Color::Black); // Draw here. window.display(); } }
Tip
SFML uses the sf namespace, so we have to prefix its data types, enumerations, and static class members with an "sf::
".
The first thing we did here is declare and initialize our window instance of type RenderWindow
. In this case, we used its constructor, however it is possible to leave it blank and utilize its create
method later on by passing in the exact same arguments, of which it can take as little as two: an sf::videoMode
and an std::string
title for the window. The video mode's constructor takes two arguments: the inner window width and height. There is a third optional argument that sets color depth in bits per pixel. It defaults to 32, which is more than enough for good rendering fitting our purposes, so let's not lose sleep over that now.
After the instance of our window is created, we enter a while loop that utilizes one of our window methods to check if it's still open, isOpen
. This effectively creates our game loop, which is a central piece of all of our code.
Let's take a look at a diagram of a typical game:
The purpose of a game loop is to check for events and input, update our game world between frames, which means moving the player, enemies, checking for changes, and so on, and finally draw everything on the screen. This process needs to be repeated many times a second until the window is closed. The amount of times varies from application to application, sometimes going as high as thousands of iterations per second. Chapter 2, Give It Some Structure - Building the Game Framework will cover managing and capping the frame rate of our applications as well as making the game run at constant speeds.
Most applications need to have a way to check if a window has been closed, resized, or moved. That's where event processing comes in. SFML provides an event class that we can use to store our event information. During each iteration of our game loop, we need to check for the events that took place by utilizing the pollEvent
method of our window instance and process them. In this case, we're only interested in the event that gets dispatched when a mouse clicks on the close window button. We can check if the public member type
of class Event
matches the proper enumeration member, in this case it's sf::Event::Closed
. If it does, we can call the close
method of our window instance and our program will terminate.
Tip
Events must be processed in all SFML applications. Without the event loop polling events, the window will become unresponsive, since it not only provides the event information to the user, but also gives the window itself a way to handle its internal events as well, which is a necessity for it to react to being moved or resized.
After all of that is done, it's necessary to clear the window from the previous iteration. Failing to do so would result in everything we draw on it stacking and creating a mess. Imagine the screen is a whiteboard and you want to draw something new on it after someone else already scribbled all over it. Instead of grabbing the eraser, however, we need to call the clear
method of our window instance, which takes a sf::Color
data type as an argument and defaults to the color black if an argument isn't provided. The screen can be cleared to any of its enumerated colors that the sf::Color
class provides as static members or we can pass an instance of sf::Color
, which has a constructor that takes unsigned integer values for individual color channels: red, green, blue, and optionally alpha. The latter gives us a way to explicitly specify the color of our desired range, like so:
window.clear(sf::Color(0,0,0,255));
Finally, we call the window.display()
method to show everything that was drawn. This utilizes a technique known as double buffering, which is standard in games nowadays. Basically, anything that is drawn isn't drawn on the screen instantly, but instead to a hidden buffer which then gets copied to our window once display
is called. Double buffering is used to prevent graphical artifacts, such as tearing, which occurs due to video card drivers pulling from the frame buffer while it's still being written to, resulting in a partially drawn image being displayed. Calling the display
method is mandatory and cannot be avoided, otherwise the window will show up as a static square with no changes taking place.
Tip
Remember to include SFML library .dll
files in the same directory as your executable relies, provided the application has been dynamically linked.
Upon compilation and execution of the code, we will find ourselves with a blank console window and a black 640x480 px window sitting over it, fewer than 20 lines of code, and an open window. Not very exciting, but it's still better than E.T. for Atari 2600. Let's draw something on the screen!