05 — Monster Behaviour

Putting something in the dark

An empty dungeon is a diorama. The moment a skeleton shuffles across a room, it becomes a place. You don’t need pathfinding or goals for that feeling — just a two-state loop and the room each monster calls home.

A two-state loop

Each skeleton is bound to a single room and remembers which of that room’s cells are walkable floor. Its entire mind is two states:

That’s the whole behaviour — the classic roguelike pick a reachable cell → walk → pause → repeat:

update(dt) {
  if (state === 'idle') {
    timer -= dt;
    if (timer <= 0) { pickTarget(); state = 'walk'; }
    return;
  }
  // walk: step toward target; on arrival, idle again
  if (dist(pos, target) < 0.35) { state = 'idle'; timer = rand(0.8, 3.0); return; }
  pos += direction(pos, target) * SPEED * dt;
  face(direction(pos, target));
}
Why it works: because targets are only ever cells of the monster’s own room, skeletons never clip through walls or wander off — no collision code, no navmesh. The room is the constraint. Staggered random timers keep a crowd from moving in lockstep.

Watch the dungeon come alive

Below is a real generated floor, top-down, with skeletons patrolling. Toggle the targets and paths to see each one’s current intention; nudge the speed and population. This is the exact logic the 3D generator runs on animated KayKit skeletons.

And that’s the whole machine — from a 17-tile alphabet to a living, walled, populated dungeon. Go turn the crank in 3D ↗, or revisit any stage from the chapter list.