Game State Management

When you design a game, even if it only has the scope of Pac-Man,
your game invariably switches between different, unrelated modes. At one point, it’s
drawing the main menu, at another the player is steering his avatar through the game world,
eventually it is rolling the credits.

These different modes can be implemented directly into the main loop like so:

But that quickly becomes unwieldy because first, about ten dozen additional lines will
quickly sneak themselves into those if blocks and just when you reign them in
by moving each case into a separate method, overlapping resource management and references
to just about every other header in the entire project ruin the fun.

The tried-and-true solution to this problem is the State Pattern. It works like so:

Designing a Game State Manager

Managing the active game state in the main loop or Game class
is still no good because then any GameState that wants to
switch to another game state would need a pointer to the Game
instance or some global method.

This kind of upstream dependency is never desirable (the purpose of
the Game class is to run the game, not to serve as a facility for
switching between game states), so the right thing to do is to move game state
management into its own class.

Up to this point, it’s all common knowledge. What motivated me to write this
article and to roll my own game state system was that I’ve seen countless
implementations try to wed the concept of game state management with completely
unrelated things:

Undesirable Attributes

Providing an enum/string to game state mapping – This can be
done externally without locking the game state manager to one specific use case.

Creating new game states – Now either the game state manager
has to know about all the subsystems any game states may ever want to consume,
or all of the subsystems need to be singletons.

Usage-specific methods in the state interface – Methods like
Resume()/Pause(),
Draw()/Update() or *shudder*
Init()/Shutdown() (the dreaded two-stage
construction anti-pattern forced down the throat of any game state)

So let’s start with a design that excludes all this clutter:

That’s wondefully simple. The game state manager simply stores the active game state
and lets us switch to another game state. This can be either a game state known to the other
game state (eg. the game play state might know the inventory screen state) or a game state
provider could be used that serves as an interface to a state repository or factory.

Stacked Game States

Let’s extend this concept into something more useful: a stacked game state manager that
lets you stack game states on top of each other. This is a popular concept which lets
game states return to the previous game state without requiring knowledge about which
game state they’re returning to. Examples are things like aforementioned inventory
screens, maps, option menus or cutscenes.

As you can see, the GameState now has four methods:

Entered() – called after the game state has been placed in
the game state manager

Exiting() – as indicated by using present tense, called right
before the game state is removed from the game state manager

Obscuring() – also present tense, this method is called right
before another game state is stacked on top of this one

Revealed() – called after the game state has become the topmost
game state on the stack again

Also notice that this stacked game state manager is a direct extension of
the minimal game state manager presented earlier. In fact, you could derive a
StackedGameState from the earlier GameState class
and a StackedGameStateManager from the earlier
GameStateManager class without any problems.

Usage-Specific Methods

That lack of methods like Draw() and Update() in
this design are intentional. If you added such methods to the game state manager,
it would expose them to places where they aren’t supposed to be used: a state
could call Draw() on the game state manager, causing either
redundant drawing to occur or producing a stack overflow when it is part of
the stack.

They are also highly implementation-specific: one game might have
an Update method that is passed the elapsed time as a float,
another may just perform a fixed time step. One game might provide some graphics
drawing interface in the Draw() method, while another passes
a camera from which the view is being rendered while yet another doesn’t have
a Draw() method at all but expects the game state to update the contents
of a scene graph instead.

It is possible to keep a clean interface that only lets game states do what they
are supposed to do while still providing the ability to update and render game
states by deriving the usage-specific part of the game state manager from
the general purpose interface. The only portion of the game that has access to
these methods (by knowing the game state manager as its implementation class) can
be the game class:

As you can see, the Update() and Draw() methods are now contained
in separate interfaces than can be implemented – optionally – by game states.
This requires a dynamic_cast in the game state manager to find out if
a game state actually is drawable or updateable.

But isn’t dynamic_cast slow? No, that’s just the yelling of micro-optimizing
novice programmers. A dynamic_cast takes about as much time as 5 to 10 method
calls, which is hardly relevant for something that happens once per frame. Still, by
trading against a few more bytes of memory, we can make this dynamic_cast
faster even than adding the Update()/Draw() methods into
the GameState class would be:

Instead of doing the dynamic_cast inside the
DirectRenderingGameStateManager::Draw() and
DirectRenderingGameStateManager::Update() calls, we can do it when
the game state is added to the game state manager. During drawing or updating,
all the game state manager has to do is go over the list of drawables or
updateables once. No casts:

This is 100.0% as fast as it would be if the Draw() and Update()
methods were part of the GameState class and faster if you have states
that do not implement the Drawable or Updateable interfaces
since it won’t have to call the empty Update() or Draw() method.

I really like your approach. There is one minor thing, though, that I’m concerned about:

If there are no exclusive states on the stack (ok, that’s no realistic assumption ;)), your rebuildUpdateableAndDrawableQueues() method will decrement index until it is -1. I think this would lead to an out of bounds error in the following while loop, because activeStates is being read at that index.

Very good tutorial.
In the downloaded code DefaultGameStateManager.cpp the methods nofifyObscuredStates() and notifyRevealedStates() need to increment the ‘index’ in their “Now go forward” loops, to avoid infinite loops.

I’ll start by saying all I’ve done so far is look at the code, but it appears I’m not aloud to do something interesting. Any ideas on how I can modify this framework to allow for multiple non-exclusive states under an exclusive state to update correctly when the exclusive is poped off?

Example:
– I have a Game(Obsuring/Exclusive)
– I push back a Minigame(Obscuring/Inclusive – Game time still runs)
– A cutscene starts and I want to stop game time and also keep the minigame active
– Push back Cutscene(Obscuring/Exclusive – Removes Game and Minigame From update/draw)
– Cutscene ends – Pop off and only the minigame resumes – Game state does not update until minigame is popped off.

As it stands, any time an exclusive state is pushed, all other states are removed from the update/draw lists.

Edit: As I’m writing this, I just though of a Child/Parent State hierarchy, where a Child State(Mini-game) could pull the parents modality(Game-Exclusive) thru to the state manager. I’m going to work on expanding this Idea!

Loved this tutorial! I think I found a very small mistake in the code though:

In the DefaultGameStateManager.cpp file, there seems to be a couple of endless loops! Both the “notifyObscuredStates()” and “notifyRevealedStates” methods have the following lines of code at the end:

// Now go forward and notify all revealed state
while(index activeStates.size()) {
this->activeStates.at(index).first->Revealed(); //Obscured is the same way, just with the word Obscured instead of Revealed
}

The index variable (which is just an int in this case) is never increased. Simply add the missing ++index in the while loop and you’ll be good to go! 🙂