This is part of an ongoing series where we write a complete 2D game engine in C++ and SFML. A new tutorial is released every Monday. You can find the complete list of tutorials here and download the source code from the projects GitHub page.
In this tutorial we will extend our game engine by writing a scene management system. If you’ve used Unity (or a similar game engine), you’ll have noticed how you can separate the game logic into different scenes. Each scene has its own unique properties and objects and will contain the logic for a specific part of our game.
Most, if not all games, employ a number of scenes; a main menu and a game scene at the very least. Typically, a game will also have a splash screen; as well as a pause, game over, and credits scene. You can also create separate scenes for different levels of your game.
There’s a lot of code to get through so lets start by creating a new class called Scene. This will be the parent class of all our scenes. This is an abstract class that wont be instantiated, instead we’ll create specific scenes that inherit from this class.
We’ll implement OnCreate and OnDestroy as pure virtual functions to ensure that this class is not instantiated. These two methods will need to be implemented in any class that inherits from Scene. If you find that you are implementing any other method (or combination of methods) more often, then it may be wise to change those methods to pure virtual functions instead.
To manage our scenes we will write a finite-state machine (FSM). A FSM stores one or more states (scenes in this instance) with only a single state active at one time. A FSM also has the ability to transition between states, which suits our needs perfectly.
You can find more information on FSMs here. Our FSM will be very simple (we’ll build on it in future), so if you’ve never implemented a FSM before it shouldn’t be a issue. I will also be discussing FSMs in more detail in a future tutorial on game AI.
Create a SceneStateMachine class. This will be FSM responsible for maintaining our scenes.
Lets implement the more straightforward methods first.
These methods simply call the relevant methods in the current scene (if there is one).
We need to be able to add and remove scenes to our state machine. This is relatively simple as all we need to do is add/remove the item from our map and call our OnCreate and OnDestroy methods after creation and before destruction.
When we add a scene to the state machine we return an unsigned int. This is the scenes id and is currently required to remove the scene from the state machine. Using an unsigned int as the maps keys gives us the ability to have a maximum of 4,294,967,295 scenes. This should be more than enough for our purposes (or nearly anyones I would imagine). I have consciously not added any overflow protection here yet as this would just complicate the code and I don’t think we are in any danger of inserting more scenes than we have space allocated. You can also add duplicate scenes and retrieve different ids, which is something we can improve in future.
It is worth noting that the actual size of the int can vary depending on the compiler used. This will not be an issue for us as we will still be able to create a larger number of scenes that we are likely to ever use. If we wanted to ensure the size of the int is the same regardless of the system we are compiling on, then we would use fixed-sized data types (which we did when writing our bit mask class.
Removing scenes is also relatively simple. As previously stated, it requires passing in the scene id that is provided when we add a scene to our state machine.
This method attempts to find a key with the provided id and if found it is removed from our map after we call the scenes OnDestroy method so it can perform any cleanup.
Lastly we can create the SwitchTo method that transitions to a scene based on its id.
Now we have the backend, lets create a couple of scenes for testing. Lets start with a splash screen, where we will briefly display our logo before loading another scene (which we will create shortly). Create a new class called SceneSplashScreen. We’ll start all scenes with the word ‘scene’ so we can easily find them. Of course, you do not have to follow this convention.
We’ll inherit the methods that we have to (OnCreate and OnDestroy) and want to (OnActivate, Update and Draw).
Lets also create the game scene. Theres nothing new here, we are just moving all the logic from the Game class to our new scene.
The final changes we need to make are to the Game class. Our Game class will be responsible for creating the scenes and updating our SceneStateMachine. We also need to remove the logic that is now implemented in the SceneGame class.
We also no longer need a reference to Input as this is now implemented in our game scene.
In our Game class constructor, we will create two scenes, the splash and game scene.
1. This creates a smart pointer to a splash screen scene. And the next line does the same but for a game scene.
2. We add our newly created scenes to the state machine. This returns the scenes id within the state machine.
3. Now that we have our game scenes id we can set the splash screen to transition to the game scene.
4. We want the game to start at the splash screen, so we transition to that scene using its id.
We then need to call the relevant update and draw methods on our scene state machine.
So now when you run the game you’ll be greeted with a splash screen for a few seconds before the game is loaded.
In future we may convert our scene system to make use of components; where each scene, rather than being its own class, will consist of a collection of ‘components’. These components are smaller logical units that can be mixed and matched to create scenes. This topic will be covered further in the next tutorial when we look at implementing an Entity Component System for our player object.
As always, if you have any suggestions for what you would like covered or are having any trouble implementing a feature, then let me know in the comments and I’ll get back to you as soon as I can. Thank you for reading 🙂