Summary

The AI state machine pattern applies the Gang of Four State design pattern to NPC/agent behaviour. Each behaviour (Idle, Attack, Flee, Patrol) is encapsulated in its own class. A central StateMachine holds a reference to the current state and delegates to it — the current state runs its logic and decides whether to transition to another state. This gives you a modular, testable, extendable AI architecture. It remains one of the core teaching architectures in classic game-AI texts. (Millington, Artificial Intelligence for Games, see source-artificial-intelligence-for-games; Buckland, Programming Game AI by Example, see source-programming-game-ai-by-example)

(Prof Charles, course lecture series, see source-game-ai-lectures)


Why not a switch statement?

The naive approach — a large switch on a currentState enum — works until you need to add states. Adding a new state means editing the switch, risking regressions in existing branches. The State pattern instead puts each behaviour in its own class: new states are new classes, not new branches.

ApproachAdding a stateTesting a stateTransitions defined by
switch on enumModify existing switchHard to isolateCentralised in switch
State patternNew class, no edits to machineEach class independentlyThe state class itself

Implementation

The interface

public interface IState
{
    void Enter(StateMachine context);
    void Update(StateMachine context);
    void Exit(StateMachine context);
}

Enter and Exit allow setup/teardown when a state is activated or left — play an animation, reset a timer, etc.

The state machine

public class StateMachine : MonoBehaviour
{
    IState currentState;
 
    public void SetState(IState newState)
    {
        currentState?.Exit(this);
        currentState = newState;
        currentState.Enter(this);
    }
 
    void Update()
    {
        currentState?.Update(this);
    }
}

The machine holds no behaviour itself. It delegates entirely to currentState.

Concrete states — Unity example

public class IdleState : IState
{
    public void Enter(StateMachine ctx)  { /* play idle animation */ }
    public void Exit(StateMachine ctx)   { }
 
    public void Update(StateMachine ctx)
    {
        if (ctx.CanSeeEnemy())
            ctx.SetState(new AttackState());
    }
}
 
public class AttackState : IState
{
    public void Enter(StateMachine ctx)  { /* play attack animation */ }
    public void Exit(StateMachine ctx)   { }
 
    public void Update(StateMachine ctx)
    {
        if (!ctx.CanSeeEnemy())
            ctx.SetState(new IdleState());
        else if (ctx.HealthLow())
            ctx.SetState(new FleeState());
        else
            ctx.AttackTarget();
    }
}
 
public class FleeState : IState
{
    public void Enter(StateMachine ctx)  { /* play flee animation */ }
    public void Exit(StateMachine ctx)   { }
 
    public void Update(StateMachine ctx)
    {
        ctx.MoveAwayFromEnemy();
        if (ctx.IsSafe())
            ctx.SetState(new IdleState());
    }
}

State transition diagram

The three states map directly onto the concrete classes above: IdleState transitions to AttackState on CanSeeEnemy(), AttackState returns to idle when the enemy is lost or drops to FleeState on low health, and FleeState returns to idle once IsSafe(). Each transition is decided inside the state’s own Update, never by the machine.


Trade-offs

Use this pattern when:

  • The agent has 3+ distinct behaviours with different update logic
  • You expect to add new behaviours during development
  • You want to test individual behaviours in isolation
  • Transitions have meaningful side effects (animation changes, sound cues)

Alternatives to consider:

  • Behaviour Trees (see game-ai-agent-design) — better for complex hierarchical decisions and reactive priority systems; more composable than state machines but heavier to implement
  • Simple enum + switch — acceptable for 2–3 states with simple logic and no anticipated extension
  • Unity Animator Controller — handles animation state machines but is not general AI logic (see unity-animator-scripting)

OOP benefits

The pattern relies on three OOP principles (Prof Charles, course lecture series):

PrincipleHow it applies
EncapsulationEach state contains its own behaviour and transition logic
Inheritance / InterfaceAll states implement IState, enforcing a uniform API
PolymorphismThe machine calls currentState.Update() without knowing which state it is

This means states can be:

  • Unit tested independently (inject a mock StateMachine)
  • Added without touching the machine or other states
  • Combined across different game objects with different machine configurations

Worked example

A ghost driven by a finite state machine in the Pac-Man-lite teaching example (see overview-arcade-classics-as-learning-projects):

  • Ghost.cs — Scatter, Chase, and Frightened states, each choosing a different movement target