Prerequisites
You need conditions, functions, and a simple enumeration. Use a character that can move, dash, and die. The useful outcome is a transition table that explains which actions are allowed at each moment.
The principle
Flags such as isDashing, isDead, and canMove can contradict each other. A finite state machine keeps one state for a chosen system and changes it through explicit events. Transitions may have guards, while entering and leaving a state can trigger one-time actions. Robert Nystrom explains the underlying pattern in State.
Do not put the entire game into one enumeration. Movement and menu visibility may be independent concerns. Start with a boundary: this machine controls the character’s available actions. Health, position, and remaining dash duration remain ordinary data.
Practical example
A short dash
Assume a dash lasts 0.18 seconds and death interrupts it immediately. These are exercise parameters, not universal recommendations for responsive controls.
| Current state | Event and guard | Next state |
|---|---|---|
| ready | dashPressed and charge available | dashing |
| dashing | dash time expires | ready |
| ready or dashing | health reaches zero | dead |
| dead | restart | ready after resetting data |
update(dt, events):
if state == dead and events.restart:
resetCharacterData()
transitionTo(ready)
return
if health <= 0:
transitionTo(dead)
return
if state == ready and events.dashPressed and chargeReady:
transitionTo(dashing)
else if state == dashing:
dashRemaining -= dt
if dashRemaining <= 0:
transitionTo(ready)
transitionTo does nothing when the requested state already matches the current state. For a real transition, it runs exit logic, changes state, and then runs entry logic. Spend a charge when entering dashing, rather than on every frame. You can also capture the dash direction at that point.
Notice the ordering: death is checked first and ends the update. This gives simultaneous events a defined priority. A different game may choose another ordering, but the decision should be written down.
Common mistakes
An animation callback, a function run after an animation event, must not accidentally revive a dead character by reporting that a dash has finished. Accept an event only in a compatible state. For delayed callbacks, checking the identity of the action that created them can also be useful.
Avoid building a framework for dozens of imagined future states. Start with an enumeration and a small transition function. If the table fills up with combinations such as running-with-weapon and running-without-weapon, check whether independent systems have been combined unnecessarily.
Assigning ready is not a complete restart. Health, charges, timers, and subscriptions must return to their intended initial values too. List that reset data explicitly and exercise the scene a second time.
Verify it
- A single dash request spends exactly one charge.
- Death interrupts a dash and prevents new actions.
- A late animation event cannot change a dead character’s state.
- Every row of the transition table has a focused check.
- Illegal events have a defined outcome, such as being ignored or logged.
- Restarting restores the full initial data set.
Prompt for an AI agent
Find the flags controlling dash and death. First produce a table of legal transitions and the priority of simultaneous events. Then propose a small state machine without a general framework. Check double presses, death during a dash, a late callback, and a second scene start. Explain which data remains separate from the current state.