Each state is an object with behavior
A "state" isn't just a name, it's an object (we met objects in the Objects group) with behavior attached. It typically has:
- an
onEnter(): run when you arrive, - an
onExit(): run when you leave, - an
update(dt, input): do the work every frame while in this state, - a
draw(ctx): draw the screen while in this state.
Here's the shape of the game's PlayState:
const PlayState = {
onEnter() { newLevel(); score.reset(); },
update(dt, input) { movePlayer(dt); spawn.update(dt, input); score.update(); },
draw(ctx) { drawEverything(ctx); },
};
The state machine just asks the current state to update and draw every frame. Everything else stays inert until it becomes current. That separation is what keeps a multi-state game from turning into tangled if spaghetti.
Why a state machine beats if/else
You could write the whole game as one giant if / else if chain. You could. But as states grow (menu → play → pause → game over → ...), the chain becomes impossible to read, and a transition has to be handled in one place while its setup lives in another.
A state machine fixes both problems:
- Each state is self-contained. All of
PlayState's code, setup, update, draw, lives in one object. - Transitions are centralized.
setStatecallsonExiton the old state andonEnteron the new one, so cleanup and setup are guaranteed to run.
The game's main loop becomes a beautiful four lines:
function loop() {
const dt = timer.tick();
sm.update(dt, input); // forwards to the current state
sm.draw(ctx);
requestAnimationFrame(loop);
}
We build that loop in the Advanced tier. For now, the idea to carry forward: a program's behavior is "which state are we in."