Finite State Machines

Purpose:

  • To simply organise and present the logic associated with a certain state of an object.

Description:

  • A state machine has a set number of states.
  • It can only be in any one state at a time.
  • States may transition to another state if certain conditions are met.

Example (enum and switch):

  • There are multiple ways to go about implementing state machines. The first way we will cover uses Enums and Switch statements.
  • Imagine you have a car. This car can:
    • Accelerate – after decellerating or breaking.
    • Decellerate – after Accelerating.
    • Break – after accelerating or decellerating.
  • An if-else block to adhere to these rules may look like this:
if(ButtonPressed("A"))
{
    if(!isBreaking && !isDecellerating)
    {
        // Accelerate
    }
}
else if(ButtonReleased("A"))
{
    if(isAccelerating)
    {
        // Decellerate
    }
}
else if(ButtonPressed("B"))
{
    if(isAccelerating || isDecellerating)
    {
        // Break
    }
}
  • Which is simple enough for now but as we add more features (maybe our car can rocket jump, has a boost option etc), this block will become more and more complicated with more and more flags.
  • This is where the Enum and Switch state machines come in handy:
switch(state)
{
case States.ACCELERATING:
    if(ButtonReleased("A"))
    {
        state = States.DECELLERATING;
    }
    else if(ButtonPressed("B"))
    {
        state = States.BREAKING;
    }
    break;

case States.DECELLERATING:
    if(ButtonPressed("A"))
    {
        state = States.ACCELERATING;
    }
    else if(ButtonPressed("B"))
    {
        state = States.BREAKING;
    }
    break;

case States.BREAKING
    if(ButtonPressed("A"))
    {
        state = States.ACCELERATING;
    }
    break;
}
  • Based on this code, it should now be a lot easier to see which states can be transitioned to from the other states.

This approach of using a switch statement is all fine and dandy for relatively simple cases, like controlling the behaviour of a character…but what happens when you need to handle something bigger, like say the state of the actual game?

Think about a modern strategy game, when you first open the game it logs you in using facebook, google play game services, game center or some other method. Next it takes you to your home world, where your base is. From there you can choose to battle someone, watch a replay of a previous battle or do a multitude of other things. In this case, having a switch statement in an update method that has a case for each state mentioned above could result in a massive amount of code sitting in one class.

This is where the State Pattern comes in.

Basically, each state is put into its own class so a state becomes an object and we can contain all the code specific to that state within its class:

interface IState
{
    void Enter(States fromState);
    void Exit();

    void HandleInput();
    void Update();
}

and then we can implement the actual states:

class LoggingInState : IState
{
    void Enter(States fromState)
    {
        // Ask user to login
    }

    void Exit()
    {
    }

    void HandleInput()
    {
    }

    void Update()
    {
        if(userDidLogin)
        {
            _stateManager.SetState(States.HOME);
        }
    }
}

class HomeState : IState
{
    void Enter(States fromState)
    {
        // Load the player's base
    }

    void Exit()
    {
        // Save the player's base
    }

    void HandleInput()
    {
        if(userClickedBattle)
        {
            _stateManager.SetState(States.BATTLE);
        }
        else if(userClickedBattle)
        {
            _stateManager.SetState(States.REPLAY);
        }
    }

    void Update()
    {
        // Update the player's base
    }
}

class BattleState : IState
{
    void Enter(States fromState)
    {
        // Load battle
    }

    void Exit()
    {
        // Save outcome
    }

    void HandleInput()
    {
        // Follow user's commands
    }

    void Update()
    {
        // Update the battle
        if(battleIsOver)
        {
            _stateManager.SetState(States.HOME);
        }
    }
}

class ReplayState : IState
{
    void Enter(States fromState)
    {
        // Load battle
    }

    void Exit()
    {
        // Don't save the outcome
    }

    void HandleInput()
    {
        // Read in saved commands so battle
        // is a replay of what originally happened.
    }

    void Update()
    {
        // Update the battle
        if(replayIsOver)
        {
            _stateManager.SetState(States.HOME);
        }
    }
}

with a state manager class to keep track of the current state and facilitate the state switching:

class StateManager
{
    IState _currentState;

    public StateManager()
    {
        _states[States.LOGGING_IN]  = new LoggingInState();
        _states[States.HOME]        = new HomeState();
        _states[States.BATTLE]      = new BattleState();
        _states[States.REPLAY]      = new ReplayState();
    }

    public void SetState(States newState)
    {
        _currentState.Exit();
        _currentState = _states[newState];
        _currentState.Enter();
    }

    public void HandleInput()
    {
        _currentState.HandleInput();
    }

    public void Update()
    {
        _currentState.Update();
    }
}

This pattern can also be used on characters if need be, a use may be where the character’s states differ greatly, say you had a car that could transform into an airplane. In this case, because the car state and the airplane state are wildly different, it might make sense to separate the CAR state and AIRPLANE state into separate classes.

But remember, always try keep it as simple as possible. If you’re not sure which to use, try the switch statement and then move onto the state objects if you find each switch case contains too much code to read easily.

Sources: