Finite State Machines (FSM)
The Problem with "If/Else Spaghetti"
When creating a simple enemy, you might start with a bunch of boolean flags: isChasing,
isAttacking, isPatrolling.
Soon, your code looks like a nightmare of nested if statements.
The solution? Finite State Machines. An FSM ensures an enemy can only be in one state at a time, and clearly defines how to switch between them.
The State Pattern
Instead of one giant update function, we split behaviors into separate classes or objects. Each "State" handles its own update logic.
state-structure.js
const PatrolState = {
enter: (enemy) => {
enemy.color = 'green';
enemy.findNextWaypoint();
},
update: (enemy) => {
enemy.moveToWaypoint();
// Transition Condition
if (enemy.canSeePlayer()) {
return 'CHASE'; // Switch state!
}
},
exit: (enemy) => {
console.log("Stopped patrolling");
}
};
Interactive AI Demo
Move your mouse (the "Player") near the Bot. Observe how it switches behavior based on distance.
- Patrol (Green): Moves randomly between waypoints.
- Chase (Orange): Detects player and moves towards them.
- Attack (Red): Close range engage.
Enemy AI Simulation
Current State: PATROL
Distance: 0px
The State Machine Manager
To glue it all together, we need a simple manager that handles the current state and switching.
state-machine.js
class StateMachine {
constructor(initialState, owner) {
this.currentState = initialState;
this.owner = owner;
this.currentState.enter(this.owner);
}
update() {
// Run current state logic
const nextStateName = this.currentState.update(this.owner);
// Handle transition if a new state is returned
if (nextStateName) {
this.changeState(nextStateName);
}
}
changeState(newStateName) {
this.currentState.exit(this.owner);
this.currentState = States[newStateName];
this.currentState.enter(this.owner);
}
}