Overview
A state machine keeps your game organised by separating distinct phases — title screen, gameplay, pause, game over — into independent states. Each state handles its own input, updates, and rendering. Transitions between states are explicit and controlled.
This pattern works identically on any platform. The concept is the same whether you’re coding in 6502, Z80, or 68000.
Pseudocode
; Game states (constants)
STATE_TITLE = 0
STATE_PLAYING = 1
STATE_PAUSED = 2
STATE_GAMEOVER = 3
; Current state variable
game_state: byte = STATE_TITLE
; Main loop
game_loop:
switch (game_state)
case STATE_TITLE:
call title_update
case STATE_PLAYING:
call playing_update
case STATE_PAUSED:
call paused_update
case STATE_GAMEOVER:
call gameover_update
goto game_loop
; State handlers
title_update:
draw title screen
if fire pressed:
game_state = STATE_PLAYING
call init_game
return
playing_update:
read input
update player
update enemies
check collisions
draw game
if player_dead:
game_state = STATE_GAMEOVER
if pause pressed:
game_state = STATE_PAUSED
return
paused_update:
draw "PAUSED"
if pause pressed:
game_state = STATE_PLAYING
return
gameover_update:
draw "GAME OVER"
draw final score
if fire pressed:
game_state = STATE_TITLE
return
Implementation Notes
6502/Z80 (jump tables):
; Load state, multiply by 2, use as index into jump table
lda game_state
asl ; * 2 for 16-bit addresses
tax
jmp (state_table,x)
state_table:
.word title_update
.word playing_update
.word paused_update
.word gameover_update
68000 (jump tables):
; States are word offsets
move.w game_state,d0
add.w d0,d0 ; * 2 for word table
lea state_table,a0
move.w (a0,d0.w),d0
jmp (a0,d0.w)
Trade-offs
| Aspect | Cost |
|---|---|
| CPU | Minimal — one table lookup per frame |
| Memory | ~20-50 bytes for jump table + state variable |
| Complexity | Low — very readable and maintainable |
When to use: Any game with multiple screens or phases.
When to avoid: Extremely simple single-screen games where a state machine adds unnecessary complexity.
Benefits
- Clean separation — each state is self-contained
- Easy debugging — you always know which state you’re in
- Simple transitions — just set the state variable
- Extensible — adding new states is trivial