This week I have accomplished a couple of things.
I changed what the Object-Manager stores. Previously it stored GameObjects but now it stores pointers to GameObjects. This prevents the object you want to store inside the Object-Manager from being destroyed when it gets copied over to the map container. Due to this change I also managed to fix a couple of memory leaks concerning the objects sprites and colliders not getting destroyed. Another thing I did was making the game scale according to what resolution you have. This change prevents the users who plays the game at higher resolution from having an advantage due to the increased field of view. But the main thing I did this week was the revamp of the State-Manager.
Previously we had no way of pausing the game. If you wanted to take a break from the game you’d have to restart from the beginning again in-case you lost while you were gone. And that’s not acceptable. So now instead I have changed the way states are handled and gave each state a couple of new functions and member variables.
New member variables
- bool m_paused
- bool m_base
How it works
- The function “Pause()” executes what need to be done whenever a state gets paused. It also sets the member variable “m_paused” to true.
- The “Resume()” function performs all the tasks which need to be done when a state gets resumed. It also (as you probably guessed) sets the member variable “m_paused” to false.
- “isPaused()” just returns the value of the member variable “m_paused”.
- And finally “isBase()” returns the value of the member variable “m_base”. I’ll explain what this one’s for later.
Now we have all the functions and member variables which are needed to transition seamlessly between states. But how do we use them?
The way they’re used
In order to be able to use all the states you’ve created, you need to have a “State-Manager” which takes care of all the execution of state-functions. The State-Manager in Haunted Light have two member variables of the type vector.
- std::vector<State*> m_states
- std::vector<State*> m_current
The first member variable “m_states” holds all the states which the game has. The second one; “m_current” holds all the states which are active.
When I want to add a state to the game I use the state-managers “Attach() function. This function takes one argument of the type State pointer. It then adds the state to the StateManagers vector container by writing e.g:
( The argument “m_system” is a pointer of System class that holds all the necessary parts of the application which all the states need to have access to). But in order to play the game you want to have a “starting state”. To set the initial state I use the state-managers “SetState” function. This function also takes one argument but of the type std::string. When called this function goes through all the states inside “m_states” and call each states isType() function. isType whether the State is of the type that’s defined in the argument to the function.
When the correct state is found then its “Enter()” function gets executed. From here the StateManager alternates between the current states Update() and Draw() function until the Update() function returns true. That means that it’s time to change state. Before the function returns true, then it can call the Pause() function to indicate that it is a “pause call” and through the member variable “m_next” define what the next state will be.
Now, when the StateManager is going to change a state then it can do so in three different ways.
The three ways
- 1. Return to the previous state.
This gets performed whenever the current states member variable “m_next” equals and empty string: “”. If the previous state returns true on isPaused() then the function Resume() gets called on that state before resuming with the game.
- 2. Pause current state
When the Pause() function of the current state gets called, then it changes state but keeps the previous one in idle. Only drawing what’s on it and not updating it.
- 3. Leave to another state
When the current state don’t calls the Pause() function then the StateManager calls each active states Exit() function and clears all the states inside the member variable “m_current”.It then uses the SetState() function to reset the game to the specified state.
The last thing I’m going to cover is the way states are rendered. Whenever the Draw() function of the StateManager gets called. Then it goes thorugh each state inside the member variable “m_current” from the back. The StateManager then checks whether the state returns true on isBase(). If it does, then all the forthcoming states Draw() function gets called. This enables the game to be frozen in the background when you have a pause screen active.
Now you know how our state-manager works in Haunted Light and next week I’ll cover another part of our development and to finish things of, here’s an up-to-date screen-capture of Haunted Light
~Lead Programmer, Per “Gimmic” Johansson