Game AI Basics
Game AI for behaviour usually splits into perception (what the agent sees), decision (what to do about it), and action (how to do it). Finite state machines are the workhorse for low-complexity agents; behaviour trees scale better as you add reactivity. This example shows an FSM-driven guard NPC in plain JS for clarity.
A guard NPC with patrol / chase / search states
EXAMPLE
// Tiny FSM with on-enter, on-update, on-exit hooks
class FSM {
constructor(states, initial) {
this.states = states;
this.current = initial;
this.states[initial].onEnter?.(this);
}
transition(next) {
if (next === this.current) return;
this.states[this.current].onExit?.(this);
this.current = next;
this.states[next].onEnter?.(this);
}
update(dt, ctx) { this.states[this.current].onUpdate?.(this, dt, ctx); }
}
const guard = { pos: {x: 0, y: 0}, lastSeen: null, patrolIdx: 0 };
const waypoints = [{x:0,y:0},{x:10,y:0},{x:10,y:10},{x:0,y:10}];
function moveToward(p, target, dt, speed=2) {
const dx = target.x - p.x, dy = target.y - p.y;
const d = Math.hypot(dx, dy);
if (d < 0.1) return true;
p.x += dx / d * speed * dt;
p.y += dy / d * speed * dt;
return false;
}
const states = {
patrol: {
onUpdate(fsm, dt, ctx) {
const wp = waypoints[guard.patrolIdx];
if (moveToward(guard.pos, wp, dt)) {
guard.patrolIdx = (guard.patrolIdx + 1) % waypoints.length;
}
if (ctx.canSeePlayer) fsm.transition('chase');
},
},
chase: {
onUpdate(fsm, dt, ctx) {
if (ctx.canSeePlayer) {
guard.lastSeen = { ...ctx.player };
moveToward(guard.pos, ctx.player, dt, 4);
} else {
fsm.transition('search');
}
},
},
search: {
timeLeft: 0,
onEnter() { this.timeLeft = 5; },
onUpdate(fsm, dt, ctx) {
this.timeLeft -= dt;
if (guard.lastSeen) moveToward(guard.pos, guard.lastSeen, dt, 2.5);
if (ctx.canSeePlayer) fsm.transition('chase');
else if (this.timeLeft <= 0) fsm.transition('patrol');
},
},
};
const fsm = new FSM(states, 'patrol');
// Game loop calls fsm.update(dt, { canSeePlayer, player }) each tick.
Why it matters
Per-state data (timers, counters) lives on the state object itself, not on the agent — that way the state owns its lifecycle and you do not have to remember to reset agent fields on transition. As the agent grows past five or six states, migrate to a behaviour tree before the transition graph becomes a hairball.
Tip: Tweak the snippet with Try it Yourself », then sit the quiz at the bottom of the page.
Example
Example
// FSM, Behaviour Tree, GOAP, Utility AI — pick by complexity. // FSM is the right answer 80% of the time.Try it Yourself »
Discussion
Loading…