Creating a City Building Game with SFML Part 1: State Manager
Get started with this tutorial series here!
Let’s begin! The core of almost any game is a state manager, which unsurprisingly handles each of the states the game can be in (e.g. the main menu, the game world, or the options menu). By using a state manager we can nicely partition the game into these states, instead of them just being nice logical blocks that remain solely in our head. We will do this by creating a base
GameState class which our actual states will inherit from, and then creating a
Game class that will handle the changing of the states as well as storing the information that is universal to (almost) every state. Creating a
game_state.hpp file then,
virtual void draw(const float dt) = 0;
virtual void update(const float dt) = 0;
virtual void handleInput() = 0;
#endif /* GAME_STATE_HPP */
#include for a non-existent file (we’ll fix that soon) we have a small class definition.
game is just a pointer back to the state manager mentioned earlier, the functions are the interesting part. They are all pure virtual functions and so are not defined within the
GameState class; they must be overridden by an inherited class in order for them to be used. This is why we haven’t defined or even declared a constructor, the compiler wouldn’t allow you to create an instance of this class! The functions themselves are pretty self-explanatory, they’ll just be called by the state manager in order to draw the state to the screen, update its logic, or handle its input. We’ve used virtual functions like this so that the state manager does not need to know what kind of state it has active, whatever kind it is (main menu, game world, or otherwise) it will just call the same functions.
Fixing that preemptive
#include, create a
void pushState(GameState* state);
void changeState(GameState* state);
#endif /* GAME_HPP */
Here you might note that we have a bit of a loop with our class usage;
Game needs to know about
GameState needs to know about
Game! This would be a problem except for the little declaration of the
GameState class within the file, which tells
GameState‘s existence and allows us to include
game_state.hpp and fix the cycle! Looking in the class itself, there’s an
std::stack data structure for storing the states and a few helper functions to push and pop states to and from the stack. We then have the
gameLoop function, which will act much like
main in our program. This is purely a design decision, the game loop (which will call those virtual functions) could be placed in
main if you wanted. We then have a simple constructor and destructor and finally, our first bit of SFML code! Well, if you count a variable declaration of course… As the name implies
window is just the window that our game will be drawn in. With the header file defined, let’s create the source file for the
void Game::pushState(GameState* state)
void Game::changeState(GameState* state)
if(this->states.empty()) return nullptr;
sf::Time elapsed = clock.restart();
float dt = elapsed.asSeconds();
if(peekState() == nullptr) continue;
this->window.create(sf::VideoMode(800, 600), "City Builder");
The state changing functions are all similar and simple;
pushState takes a pointer to a
GameState(or one of its derived classes) and pushes it on to the state stack,
popState removes the top state from the stack,
changeState pops the previous state (if there was one) and then pushes the new one on, and finally
peekState returns a pointer to whatever state is on top of the stack. But why do we use a stack, and not just a
currentState pointer, or similar? Well with a stack we can add new states over the top of existing ones whilst still keeping track of the old one, so we could add a pause game state which can be removed when the game is unpaused, but which keeps the progress of the game itself! Very handy, and I think worth the few extra lines it takes. The destructor is also state related, and just deletes all the states on the stack. Because of this, any states the state manager handles should be allocated using
Finally we get into some ‘proper’ SFML code! In the constructor we create a new render window (
sf::Window cannot be drawn to, so we use
sf::RenderWindow instead) which is 800 pixels wide, 600 pixels high, and is named “City Builder” (creativity is not my strong suit…). We then cap the framerate to 60fps, to stop the game from running too fast and eating too many cpu cycles!
Now let’s look at the
gameloop function. Our game will use a variable timestep, here called
dt, in order to make everything work at a smooth rate. There are many different ways of controlling the speed of the logic in a program, but since we want time to progress at the same rate inside the game, regardless of the framerate, we will use this method. (We could just assume that the game was running at 60fps, but that’s not a very safe assumption.) It works by starting a timer at the beginning of each frame, and stopping it at the end, before storing the time elapsed inside a timestep variable. This is then passed to any functions that need to know the speed of the game. So if we want a ball to move at 100 pixels per second, and the frame took 0.1 seconds, then the ball needs to move 10 pixels this frame. If the next frame takes twice as long, the ball should move twice as far.
clock.restartconveniently returns the time since
clock started, so we can combine the stopping and starting into one line. We then use the virtual functions that we defined earlier, making sure that we clear the screen before drawing the new one and updating the window.
We have one more file to define for now,
main.cpp! It’ll be rather simple (our simplest file even) since the heavy lifting is done by the
Make the state manager, and then start the loop, trivial! We’ll be back later to add another line or too, but for now we’ve finished our state manager! If you run the program, an empty window should appear, but sadly you’ll have to close the poor thing using a task manager or equivalent — hitting close will do nothing until we actually create a state!
Author: Daniel Mansfield