iwantcoding.com
🔥 Daily 👥 Rooms 🏆 Top Log in Sign up

State Machines

A state machine models gameplay state explicitly: idle, running, jumping, attacking. Each state knows its own transitions. The discipline keeps physics, animation, and input logic from devolving into a soup of ifs.

A character FSM

EXAMPLE
// 1) Hand-rolled FSM — fine for most games
const states = {
    idle: {
        enter()       { player.sprite.play('idle'); },
        update(dt)    {
            if (input.x !== 0)      transition('run');
            if (input.jumpPressed)  transition('jump');
        },
    },
    run: {
        enter() { player.sprite.play('run'); },
        update(dt) {
            if (input.x === 0)      transition('idle');
            if (input.jumpPressed)  transition('jump');
            player.x += input.x * RUN_SPEED * dt;
        },
    },
    jump: {
        enter() {
            player.vy = JUMP_VY;
            player.sprite.play('jump');
        },
        update(dt) {
            player.vy += GRAVITY * dt;
            player.y  += player.vy * dt;
            if (player.onGround) transition(input.x === 0 ? 'idle' : 'run');
        },
    },
    attack: {
        enter() {
            player.sprite.play('attack');
            player.attackTime = 0.4;
        },
        update(dt) {
            player.attackTime -= dt;
            if (player.attackTime <= 0) transition('idle');
        },
    },
};

let current = states.idle;
function transition(name) {
    current = states[name];
    current.enter?.();
}
function update(dt) {
    current.update(dt);
}

// 2) Hierarchical FSM — &ldquo;in air&rdquo; super-state with sub-states (jump, fall)
//    Lets you handle attacks DURING jump without duplicating transitions.

// 3) For complex AI, reach for a behaviour tree (Behavior3, Owyl)
//    or GOAP — they scale where flat FSMs would explode.

// 4) When you have async / parallel states, look at the actor model (XState)
//    npm i xstate
import { createMachine, interpret } from 'xstate';

const characterMachine = createMachine({
    id: 'char',
    initial: 'idle',
    states: {
        idle:    { on: { MOVE: 'run', JUMP: 'jump' } },
        run:     { on: { STOP: 'idle', JUMP: 'jump' } },
        jump:    { on: { LAND: 'idle' } },
    },
});

const svc = interpret(characterMachine).start();
svc.send('MOVE');   // idle → run

Why it matters

FSMs cap the “input + animation + physics” explosion. The most-debugged player controller in any game becomes the easiest to extend — new state, new transitions, same shape.

Tip: Tweak the snippet with Try it Yourself », then sit the quiz at the bottom of the page.

Example

Example
// FSM keeps gameplay state predictable.
let state = 'idle';
function tick(dt) {
    switch (state) {
        case 'idle': if (input.move) state = 'run'; break;
        case 'run':  if (input.jump) state = 'jump'; break;
        case 'jump': if (onGround)   state = 'idle'; break;
    }
}
Try it Yourself »

Exercise

Acronym for the classic AI pattern.

Discussion

Loading…