In this coding scar we’ll talk about one of the Gang of Four’s object-oriented patterns: the State pattern.
You are programming a RTS videogame with some AIs. One of those is a basic unit: the harvester. You decide to find inspiration in other videogames, and you come across StarCraft.
The original StarCraft Terran’s worker unit (the SCV) can store resources (minerals and gas). It can move through the map, gather those resources, build new structures, …
We’ll simplify it so we can illustrate the concepts. This is the behavior we’re interested in, for now:
- Idle: just standing, doing nothing.
- Moving to a point: the player has commanded it to move to a point, and it’s on the way.
- Moving to a target: very similar to the previous one, but will interact with the target when it arrives to it.
- Gathering mineral/gas: resources are extracted periodically when gathering. The SCV will continue doing this until it reaches its full load.
- Deposit resources: once the SCV reaches the correct building the loaded resources are periodically removed from the unit’s tank.
Now you know what your unit will be doing, and because you think you know how to implement it, you start your favourite IDE and start coding right away.
Simple game loop
First of all, we need a game loop in which our SCV can live. We’re oversimplifying it, so assume we’ve got this one:
And let’s say we define our SCV as:
Easy peasy. What’s next?
You believe you can code this unit’s behavior without designing it first, so you start thinking about the parts of the behaviour one by one.
Arguably the easier part: the SCV can be standing still doing nothing. Okay, so we fill in our
SCV::update function like so:
Phew, not bad.
Moving to a point
Since we’re simplifying it, say we’ve got a way to define points in our world and a way for the unit to move there. You throw some code in and you’ve got:
Let’s assume methods
moveTowards do what their names imply, performing the necessary calculations that we’re skipping so we stay in track.
Okay, so now the unit can move and stay idle. You are on fire. Who needs to design their systems, anyway? What’s next?
Moving to target
Assume our target is an entity in our hypothetical world. The resources this unit has to collect and the buildings where those resources can be deposited would be those entities. We don’t mind if our architecture is component-based or hierarchy-based or whatever. We’re assuming our SCV can
interact with one of these
Entity. One could then have:
This is starting to smell a little, huh? But you are tough and a little smell isn’t turning you out.
Gathering and depositing resources
Okay, we’ve got our SCV moving through the world and interacting with stuff. Let’s say it can interact with two types of entities: resources and buildings to deposit them. Let’s also assume it takes some time for the unit to fill its tank up and to empty it out. Something like:
Take a look at this function. It isn’t close to being beautiful: it’s long, performs a lot of different logic, it’s got a lot of nesting… We can do better! But before you can think of a better way, you realize you can’t command your unit to do anything.
Say our unit has a way of taking commands and we don’t mind where those commands come from (user interaction, other systems within the game, the unit itself, …). You roll up your sleeves and create this sample hierarchy:
And now the method in your unit to execute these actions:
For clarification, we cast the action we’ve been passed to get the
Action we’re interested in (suppose rtti_cast is your very own RTTI implementation, or just uses dynamic_cast, we don’t mind).
That’s just one of the actions the unit can take! It can also
MoveToEntity or even
FleeFromTarget if an enemy is coming. Now you take a deep breath and remember your old self not designing the system with some pen and paper.
And then you remember that you could use a
switch statement to improve this mess!
Working with enumerations
Alright, you throw an enumeration in your code like so:
And add a new member in the SCV that will replace those dirty booleans you used to know which state you were in:
What’s the new look of the
Well, it’s better than nothing. But, sooner or later, this method will be monstruous enough to be unmaintainable.
Let’s start again. For the most part.
Gang of Four’s State pattern defines that a given class has an instance of a
State to which the logic is delegated. But before we implement the real object-oriented pattern, let’s create a hybrid with an imperative approach. While it might not look as useful for now, it will help us to better understand the program’s flow.
executeAction functions were tangled, so our goal is to basically keep the same functions but cut into different pieces.
Let’s define a
Wait, what is THAT?!
Okay, okay! Let’s break it down into pieces and understand what’s going on.
typedef in C++ you declare a typed alias of another type, usually a somewhat convoluted one. These two statements are, then, equivalent:
It’s a pain to write the full type twice or more times. Imagine you had written it several times and then need a new parameter to the function: you’d have to change it in a lot of places! If you
typedef it, you just change the definition of the type in one place.
And now for the hard part:
std::function is a function wrapper that can store any function. Its definition is:
So, for our example, we’re defining a wrapper that points to a function whose return type is
void, which has two parameters and their types are
Take this other example:
I guess this example looks useless, but it shows the idea.
Going back on track! That is a stateless state: it doesn’t hold any data, just a pointer to its
executeAction functions provided during construction. The only thing we’re missing is the delegation of the
SCV::executeAction functions to the current state’s:
Alright! And how do we change states? We could define a simple method that sets them, nothing fancy:
Now we can create and set states with different functions to control flow. Something like:
You get the point.
Okay, but still this code has a major flaw we want to solve: all member variables are potentially shared between states (not to mention creating and destroying states everytime!).
What if we explore the real Gang of Four’s State pattern already?
As with all of the Gang of Four’s patterns, we’re going for a full object-oriented design: each state will become a class on its own that handles the logic in one part of the behavior. As before, we’ll have a single state as a member object in the SCV class.
All states will have a common interface: the
executeAction functions, same as before.
The superclass for all of the states would be:
Let’s now define some of the states we’ve already mentioned.
Way better, don’t you think?
moveTowards function? It implicitly changes the state of the SCV. It could be implemented like:
Do you start to see the benefits of using stateful states? Our SCV class would be reduced to having the
State it’s in and whatever other properties that are relevant for the unit (i.e. the amount of a loaded resources). We don’t have common properties to hold logic that is specific to one state.
State, then, is responsible for taking care of performing its own logic with its own properties, separatedly from other
States. This helps readability and maintainability as execution flow is held in the currently active state and we can only access the state’s data or SCV’s common one (i.e. previously mentioned loaded resources).
Setting state revisited
Alright, so now that we know how we’re structuring our code, let’s think about code flow once again. Since we’re keeping all of the logic that’s related to a state in itself, we’d like to detect when we’re entering/exiting a state so we can have extra logic (i.e. setting an animation, playing a sound, notifying other systems, …).
For that, we’ll have to modify our
State to have two extra methods:
And we’ll have to modify our
setState method to account for these functions:
Of course, we could’ve passed our
SCV instance in the
State constructor rather than each function, but for the sake of simplicity we’ve kept it this way.
Now, we could set some animation when we enter the
Idle state, hide the unit when entering the
Depositing state and showing it again when exiting it, or having different logic when we are coming from/going to certain states (i.e. drop currently loaded resources if going to
FleeingFromEntity because we’re scared but keeping them if going to
Bonus: other applications
Keeping state using this pattern isn’t restricted to videogames and AI only, but it isn’t a silver bullet either! Let’s have a look at a couple of interesting uses:
This can also be applied to fingers in a touch screen. You can separate your logic into two states:
Dragging. Maybe you can have:
Say you’re performing net requests and want to keep track of its state. You could control it with some states:
Maybe some of them keep a timer to check for timeouts, or keep request/response communication progress. You name it!
Have you ever thought about user navigation between menus? User is presented with the main one, then interacts to go to some other menu. Yes, those are
States as well! Each one manages its logic and has its own properties, so it fits our pattern.
But, what about going back to the previous menu? You could have a hardcoded graph of which menu is the previous of another one. Or you could have a Pushdown Automaton!
In a nutshell, it has a stack of
States so you push one when you enter a new menu and then pop it when you exit it and you are again in the previous one. But that’s for a future coding scar, don’t rush it :)
Thank you for reading!