A Finite State Machine in Unity
Split what a game object does into states with enter, update and exit steps, with a small generic state machine you can reuse in any MonoBehaviour.

Quest objectives
- Know when a state machine beats an enum and a switch
- Write a generic state and state machine
- Build an entity that waits and wanders, with two states
- Move to a target without overshooting at low frame rates
A finite state machine (FSM) is always in exactly one of a fixed set of states. The current state decides what the object does, and when it hands over to another state. An enemy that patrols, chases and attacks, a door that is open, closing or locked, a player who walks, jumps or climbs: all of them are state machines, whether they are written as one or not.
For two or three simple states, an enum and a switch in Update will do. Once the states need data of their own, or something has to happen exactly once when a state starts or ends, the switch grows into one long method that knows everything. A class per state keeps every state short and on its own.
The state
Every state derives from one base class. It has the methods the state machine calls: Enter once when the state starts, Update, FixedUpdate and LateUpdate every frame, and Exit once when it ends. A state overrides only the ones it needs.
// The base of every state. TOwner is the class the states belong to, such as Entity.
public abstract class State<TOwner> {
protected readonly TOwner owner;
protected readonly StateMachine<TOwner> machine;
protected State(TOwner owner, StateMachine<TOwner> machine) {
this.owner = owner;
this.machine = machine;
}
public virtual void Enter() { }
public virtual void Update() { }
public virtual void FixedUpdate() { }
public virtual void LateUpdate() { }
public virtual void Exit() { }
}TOwner is the class the states belong to. Because the base class knows it, every state gets owner with the right type and can use its fields without a cast. machine is the state machine, which a state needs to hand over to the next one.
The state machine
The state machine holds the current state, passes every frame on to it and switches states:
// Holds the current state and switches between states. A plain C# class,
// so any MonoBehaviour can have one, or two.
public class StateMachine<TOwner> {
public State<TOwner> CurrentState { get; private set; }
public void ChangeState(State<TOwner> next) {
CurrentState?.Exit();
CurrentState = next;
CurrentState?.Enter();
}
public void Update() => CurrentState?.Update();
public void FixedUpdate() => CurrentState?.FixedUpdate();
public void LateUpdate() => CurrentState?.LateUpdate();
}ChangeState always takes the same three steps: Exit on the old state, the switch, Enter on the new one. That is what makes the pattern work: a state can rely on its Enter running before its first Update, and on its Exit running after its last.
The state machine is a plain C# class, not a MonoBehaviour. Any component can have one without deriving from it, and a component that needs two, say one for moving and one for fighting, simply has two.
An entity that wanders
As an example, an entity that waits for two seconds, walks to a random point near where it started, waits again, and so on. The component holds the settings, creates its states and passes Unity's update methods on to its state machine:
using UnityEngine;
// Waits, walks to a random point near where it started, waits again, and so on.
public class Entity : MonoBehaviour {
public float moveSpeed = 5f;
public float idleTime = 2f;
public float wanderRadius = 10f;
// Only there so you can watch the state in the Inspector
[SerializeField] string currentState;
public Vector3 Home { get; private set; }
public EntityIdleState IdleState { get; private set; }
public EntityMoveState MoveState { get; private set; }
readonly StateMachine<Entity> machine = new StateMachine<Entity>();
void Awake() {
Home = transform.position;
IdleState = new EntityIdleState(this, machine);
MoveState = new EntityMoveState(this, machine);
}
void Start() {
machine.ChangeState(IdleState);
}
void Update() {
machine.Update();
currentState = machine.CurrentState?.GetType().Name;
}
void FixedUpdate() => machine.FixedUpdate();
void LateUpdate() => machine.LateUpdate();
}The states are created once in Awake and reused, so switching between them allocates nothing. The first state starts in Start, when every component of the object has run its Awake. currentState does nothing for the game. It shows the name of the current state in the Inspector, which helps a lot when a state machine doesn't do what you expect.
The idle state notes when it started and hands over to the move state when the time is up:
using UnityEngine;
public class EntityIdleState : State<Entity> {
float enteredAt;
public EntityIdleState(Entity owner, StateMachine<Entity> machine) : base(owner, machine) { }
public override void Enter() {
enteredAt = Time.time;
}
public override void Update() {
if (Time.time - enteredAt >= owner.idleTime) {
machine.ChangeState(owner.MoveState);
}
}
}The move state picks a point within wanderRadius of home when it starts, and walks there:
using UnityEngine;
public class EntityMoveState : State<Entity> {
Vector3 target;
public EntityMoveState(Entity owner, StateMachine<Entity> machine) : base(owner, machine) { }
public override void Enter() {
// A random point around home, at the entity's own height
Vector2 offset = Random.insideUnitCircle * owner.wanderRadius;
target = owner.Home + new Vector3(offset.x, 0f, offset.y);
}
public override void Update() {
Transform body = owner.transform;
// MoveTowards never goes past the target, so the entity lands on it exactly
body.position = Vector3.MoveTowards(
body.position,
target,
owner.moveSpeed * Time.deltaTime
);
if (body.position == target) {
machine.ChangeState(owner.IdleState);
}
}
}Vector3.MoveTowards moves by at most the given distance and never past the target, so the entity ends up exactly on it and the check with == can't miss. A step along the direction followed by a check like "closer than 0.1" can: at a speed of 5 and less than 25 frames per second, one step is longer than 0.2, and the entity can jump back and forth across the target without ever getting that close.
Trying it out
Create a cube with GameObject > 3D Object > Cube.
Add the
Entitycomponent to it.Press Play.
The cube waits, slides to a random point, waits, and so on, and Current State in the Inspector switches between EntityIdleState and EntityMoveState.
Where to go from here
Every new behaviour is a new class. A chase state could follow the player, with the idle state checking the distance and handing over to it when the player comes close. Neither the state machine nor the other states have to change for that. These are the files so far:
Assets/
└── Scripts/
├── StateMachine/
│ ├── State.cs
│ └── StateMachine.cs
└── Entity/
├── Entity.cs
├── EntityIdleState.cs
└── EntityMoveState.cs