doc 1 of 5

State Machines — stop your boolean flags from fighting each other

Every character you write starts the same way. One boolean: is_jumping. Then is_attacking. Then is_dashing, can_move, is_hurt, is_dead. Six booleans have sixty-four combinations, and you meant about five of them. The bug report that finally breaks you reads "if you attack at the exact frame you land while holding down, you can dash through walls forever."

A state machine is the fix, and it is much smaller than its reputation. The whole idea: the character is in exactly one state at a time, and only certain moves between states are legal. That's it. Everything else is bookkeeping about where to put the code.

What this shelf teaches#

Four parts, each a working thing you can run. They form a ladder — each rung is the right answer for some games and overkill for others, and knowing which rung you're on is the real skill.

PartYou buildRight when
01A deliberately flag-based character, then the exact bug that kills itYou've never felt the pain and don't trust that it's real
02One enum, one match, one _set_state() — a complete FSM in ~40 lines3–6 short states. Most 2D characters. Start here.
03A StateMachine node with one child node per statePer-state logic outgrew one file — complex players, pattern bosses
04A state stack: interrupt with dialogue or stun, then returnYou catch yourself writing var previous_state

The rule nobody tells you#

Do not start at part 03. Node-based state machines photograph well and get recommended constantly, but for a character with four states they add five files and a layer of indirection to solve a problem you don't have yet. The escalation is the point:

  1. Plain ifs — a character that only walks and jumps. Genuinely fine. Don't architect a prototype.
  2. enum + match — the moment flags multiply. This is where most games should live and stay.
  3. State nodes — when a single state's logic is long enough that scrolling past it annoys you, or when you want per-state values tunable in the Inspector.
  4. A stack — when states need to interrupt and resume rather than replace.

Climb only when the current rung hurts. "I might need it later" is not a hurt.

What a state machine actually buys you#

Three specific things, worth naming because they're the reason to bother:

Invalid states stop existing. With flags, is_dashing = true and is_dead = true can both be set — nothing forbids it. With one state variable holding one value, "dashing and dead" cannot be written down. Whole categories of bug become unrepresentable rather than merely unlikely.

Transitions get a single home. Every entry into and exit from a state runs through one function, so "start the dash timer" is written once. With flags, the code that sets is_dashing = true is scattered across every place a dash can begin, and one of them forgets the timer.

You can draw it. A state machine you can sketch on paper — circles and arrows — is one you can reason about at 2am. If you can't draw it, it isn't a state machine yet; it's flags with extra steps.

What it does not buy you#

State machines don't make behavior simpler; they make it explicit. A character with sixteen genuinely different states has sixteen states no matter how you store them. If your diagram turns into a dense mesh where everything connects to everything, that's a signal to decompose the entity — separate the movement machine from the weapon machine and let them run side by side — not to add a bigger framework.

Prerequisites#

You should be comfortable with typed GDScript, enum, and match before part 02, and with scenes/nodes and signals before part 03. If match is new, the GDScript basics page covers it in five minutes; the typed GDScript rules apply to every line here.

Where this shows up in the projects#

The farming game builds an enum machine for the player in stage 05 — that's part 02 applied.

The action RPG is the one to watch if you want to see the pattern used twice in one project: a player machine from stage 02, and a separate enemy-AI machine in stage 05. Two machines, running side by side, neither knowing about the other — which is also the honest answer to "what if two things need to be in states at once."

The platformer needs the node-based version by stage 05, because a Celeste-style character has enough per-state behavior that one file stops being readable.

Reading this shelf first makes those stages faster; reading it after makes them click.