Lam Cao's Blog

A Journey in Game Dev

How I learned to use State Design Pattern in my game

I learned finite state machine (FSM) in model of computation class at a university. I’ve loved it ever since. It turns out to be one of the most commonly used patterns in game development. State design pattern is not only easy to understand and implement but also very flexible. I think every game programmer should know about it, and ideally how to employ it.

I believe the best way to learn programming patterns is to apply them. In a recent game project, I learn faster by doing than reading or watching even though I enjoy good reads and tutorials from time to time. In a recent project, I had a chance to look closely at and evaluate (many of) the design choices that make FSM either very efficient or extremely painful to debug and maintain.

The Case

I think I should add some context here as I believe every design choice is context specific. I also want something beyond common applications like user inputs and animation controllers. Therefore, I will take my own project as an example.

The first game I ever made beyond education is called Goodie Assembly (GA). It is inspired by the classic puzzle game Tetris and … pepperoni pizza, which results in a circular ‘slicy’ Tetris instead of the rectangular blocky one.

State machine in Goodie Assembly

I’m not going to bore you with mechanic details here. Basically, I have 5 essential states in a game session.

  • Spawn: spawns new slices. Transition to ‘Normal’ if the newly spawned slices are not blocked.
  • Normal: waits until the slices move forward and get blocked or for player’s input to rotate.
  • Rotate: rotation is done here. Transition to ‘Normal’ once finished.
  • Assemble: checks if 8 slices have reached the center, and if true, a ‘goodie’ is complete and gets removed like the lines in Tetris, Finally, transitions to Spawn.

Enum vs Class-based State Design Pattern

In the first builds of GA, I used enum for states. I thought it would be adequate. And it worked fine for a prototype. Then I found that some other classes besides the context class would need to access and change states in order to make sure the game works properly. That was when things started to get messy. It became very difficult to debug as the states could be changed in multiple places. I gently slapped myself in the face, created a new branch, removed all the bad code and designed my game state machine with state pattern from the ground up.

public enum State 
{
    Normal, Assemble, Rotate, Spawn, Lost
}
public class GameManager 
{
    //a lot of stuff going on.....
    public State currentState { get; private set; }
    public void ChangeState(State newState) 
    {
        State oldState = currentState;
        currentState = newState;
        OnStateChanged.Invoke(oldState, currentState);
    }
    public void OnStateChangedEventHandler(State oldState, State newState) 
    {
        //do stuff
    }
}

I learned that enum and state pattern approaches are mutually exclusive. I can’t imagine a good mix of both for an FSM (not multiple/nested FSMs) that won’t confuse someone. Switching from one state to another is just as horrible as implementing a whole feature all over again.

Enum is short and simple. I can define and implement all the states and their behaviors clearly in one place, which I love. This is an advantage of using enum at least until scaling becomes a problem.  An important note is that the behaviors of an enum state are not really attached to the state itself, meaning that the context class gets to decide what to do with each state and transition. This is because it is not possible to define methods in an enum type in C# (Unity) or C++ (Unreal). As you may see already, it will become harder and harder to manage as the states grow and their behaviors get more complex.

State Design Pattern UML Diagram. Source: Wikipedia.org

Class-based State design pattern can solve the problems I have using enum. Essentially, each state encapsulates their behaviors within their own class. This way the context object no longer needs to know what happens during a given state, which eliminates a lot of dependencies. The downside is obviously the class explosion! Having 20 states? You’re going to need at least 21 classes. Well, I don’t consider this a bad thing because I feel like the design satisfies SRP better. Besides, I’m not a fan of reading a mega class with several hundred lines of code.

public interface IState 
{
    void Enter();
    void Execute();
    void Exit();
    void Transition(IState state);
}
public abstract class AbstractState : IState 
{
    public GameFSM FSM { get; private set; }
    public AbstractState(GameFSM gFSM) 
    {
        FSM = gFSM;
    }
    public virtual void Enter() {}
    public virtual void Update() {}
    public virtual void Exit() {}
    public virtual void Transition(IState newState) 
    {
        FSM.ChangeState(newState);
    }
}
public interface FSMContext 
{
    //stuff that the states need to do their jobs.
}
public class GameFSM 
{
    public IState CurrentState { get; private set; }
    public FSMContext Context { get; private set; }
    public GameFSM(FSMContext context) 
    {
        Context = context;
    }
    public void Update() 
    {
        if (CurrentState != null)
            CurrentState.Update();
    }
    public void ChangeState(IState newState) 
    {
        if (CurrentState != null)
            CurrentState.Exit();
        
        CurrentState = newState;
        CurrentState.Enter();
    }
}

The pattern greatly extends states’ capabilities compared to the enum approach. I can add methods like Enter/Update/Exit which make the states pretty close to regular game objects. I also have no problem

I still think there is a place for the enum approach because I love to keep things as simple as possible. Personally, I would use enum if the following are true.

  • There are some very complex tasks to perform.
  • The FSM is local and relevant only to the class/objects where it is defined.

Or,

  • “It’s just a crappy prototype.”

Who defines state transitions?

The next thing I needed to figure is how and where to define the transitions. For enum, the context object (or whoever declares enum states) decides when to switch between states. There isn’t much to discuss. The State pattern, on the other hand, offers much more freedom. There are 2 common options.

The first option is to implement all of the transitions within the context or FSM wrapper class. The context class knows all the states that exist, and the states generally don’t know anything about each other. This is one way of how it may work for me: the currentState will raise an “I’m done” event OnStateFinished.Invoke(this)and the context object will handle the event with something like this:

public void OnStateFinishedEventHandler(IState stateFinished) 
{
    //switch case statement
    //do stuff depending on which state "stateFinished" is.
}

The other option which is to let each state handle their transitions by calling void Transition(IState newState) A state might not need to know about previous states; however, it does need to know about the next one and exactly when to make the transition. This means that there will be some dependencies between states, which is likely to cause some troubles if I decide to add or remove a state in the future.

I think both approaches are valid. If the transitions are simple and straightforward, the first option will be just fine. That being said, I often find it more appropriate to go for the latter. In GA, the transitions rely on very different things. For instance, the game switches from ‘Normal’ state to ‘Rotate’ on the player’s input and back once the rotation stops. ‘Spawn’ state transitions to the next right after it’s finished its job – spawn a new slice. Therefore, it makes perfect sense for me to define the transitions in states, rather than in the context class.

Bonus

State Design Pattern Revisited is a good read for those who want to learn more about the state pattern. The chapter has a section about pushdown automaton which is essentially FSM that uses a stack, which I found very interesting. Transitions can be done implicitly with stack’s pop/push mechanics.  It’s definitely great for games that have a fixed set of states. I haven’t tried this myself but I guess I can also get creative and employ a queue instead. Oh wait how about a randomized queue? *going crazy again*

Have fun programming.

Leave a Reply

Your email address will not be published. Required fields are marked *