State Machine (FSM)
@esengine/fsm provides a type-safe finite state machine implementation for characters, AI, or any scenario requiring state management.
Installation
Section titled “Installation”npminstall@esengine/fsmQuick Start
Section titled “Quick Start”import { createStateMachine } from'@esengine/fsm';// Define state typestype PlayerState ='idle'|'walk'|'run'|'jump';// Create state machineconst fsm = createStateMachine<PlayerState>('idle');// Define states with callbacksfsm.defineState('idle', {onEnter: (ctx, from)=> console.log(`Entered idle from ${from}`),onExit: (ctx, to)=> console.log(`Exiting idle to ${to}`),onUpdate: (ctx, dt)=> { /* Update every frame */ }});fsm.defineState('walk', {onEnter: ()=> console.log('Started walking')});// Manual transitionfsm.transition('walk');console.log(fsm.current); // 'walk'Core Concepts
Section titled “Core Concepts”State Configuration
Section titled “State Configuration”Each state can be configured with the following callbacks:
interface StateConfig<TState, TContext> {name:TState; // State nameonEnter?:(context:TContext, from:TState|null)=>void; // Enter callbackonExit?:(context:TContext, to:TState)=>void; // Exit callbackonUpdate?:(context:TContext, deltaTime:number)=>void; // Update callbacktags?:string[]; // State tagsmetadata?:Record<string, unknown>; // Metadata}Transition Conditions
Section titled “Transition Conditions”Define conditional state transitions:
interface Context {isMoving:boolean;isRunning:boolean;isGrounded:boolean;}const fsm = createStateMachine<PlayerState, Context>('idle', {context: { isMoving: false, isRunning: false, isGrounded: true }});// Define transition conditionsfsm.defineTransition('idle', 'walk', (ctx)=> ctx.isMoving);fsm.defineTransition('walk', 'run', (ctx)=> ctx.isRunning);fsm.defineTransition('walk', 'idle', (ctx)=>!ctx.isMoving);// Automatically evaluate and execute matching transitionsfsm.evaluateTransitions();Transition Priority
Section titled “Transition Priority”When multiple transitions are valid, higher priority executes first:
// Higher priority number = higher priorityfsm.defineTransition('idle', 'attack', (ctx)=> ctx.isAttacking, 10);fsm.defineTransition('idle', 'walk', (ctx)=> ctx.isMoving, 1);// If both conditions are met, 'attack' (priority 10) is tried firstAPI Reference
Section titled “API Reference”createStateMachine
Section titled “createStateMachine”functioncreateStateMachine<TStateextendsstring,TContext=unknown>(initialState:TState,options?:StateMachineOptions<TContext>):IStateMachine<TState,TContext>Parameters:
initialState- Initial stateoptions.context- Context object, accessible in callbacksoptions.maxHistorySize- Maximum history entries (default 100)options.enableHistory- Enable history tracking (default true)
State Machine Properties
Section titled “State Machine Properties”| Property | Type | Description |
|---|---|---|
current | TState | Current state |
previous | TState | null | Previous state |
context | TContext | Context object |
isTransitioning | boolean | Whether currently transitioning |
currentStateDuration | number | Current state duration (ms) |
State Machine Methods
Section titled “State Machine Methods”State Definition
Section titled “State Definition”// Define statefsm.defineState('idle', {onEnter: (ctx, from)=> {},onExit: (ctx, to)=> {},onUpdate: (ctx, dt)=> {}});// Check if state existsfsm.hasState('idle'); // true// Get state configurationfsm.getStateConfig('idle');// Get all statesfsm.getStates(); // ['idle', 'walk', ...]Transition Operations
Section titled “Transition Operations”// Define transitionfsm.defineTransition('idle', 'walk', condition, priority);// Remove transitionfsm.removeTransition('idle', 'walk');// Get transitions from statefsm.getTransitionsFrom('idle');// Check if transition is possiblefsm.canTransition('walk'); // true/false// Manual transitionfsm.transition('walk');// Force transition (ignore conditions)fsm.transition('walk', true);// Auto-evaluate transition conditionsfsm.evaluateTransitions();Lifecycle
Section titled “Lifecycle”// Update state machine (calls current state's onUpdate)fsm.update(deltaTime);// Reset state machinefsm.reset(); // Reset to current statefsm.reset('idle'); // Reset to specified stateEvent Listeners
Section titled “Event Listeners”// Listen to entering specific stateconst unsubscribe = fsm.onEnter('walk', (from) => {console.log(`Entered walk from ${from}`);});// Listen to exiting specific statefsm.onExit('walk', (to)=> {console.log(`Exiting walk to ${to}`);});// Listen to any state changefsm.onChange((event)=> {console.log(`${event.from} ->${event.to} at ${event.timestamp}`);});// Unsubscribeunsubscribe();Debugging
Section titled “Debugging”// Get state historyconst history = fsm.getHistory();// [{ from: 'idle', to: 'walk', timestamp: 1234567890 }, ...]// Clear historyfsm.clearHistory();// Get debug infoconst info = fsm.getDebugInfo();// { current, previous, duration, stateCount, transitionCount, historySize }Practical Examples
Section titled “Practical Examples”Character State Machine
Section titled “Character State Machine”import { createStateMachine } from'@esengine/fsm';type CharacterState ='idle'|'walk'|'run'|'jump'|'fall'|'attack';interface CharacterContext {velocity: { x:number; y:number };isGrounded:boolean;isAttacking:boolean;speed:number;}const characterFSM = createStateMachine<CharacterState, CharacterContext>('idle', {context: {velocity: { x: 0, y: 0 },isGrounded: true,isAttacking: false,speed: 0}});// Define statescharacterFSM.defineState('idle', {onEnter: (ctx)=> { ctx.speed=0; }});characterFSM.defineState('walk', {onEnter: (ctx)=> { ctx.speed=100; }});characterFSM.defineState('run', {onEnter: (ctx)=> { ctx.speed=200; }});// Define transitionscharacterFSM.defineTransition('idle', 'walk', (ctx)=> Math.abs(ctx.velocity.x) >0);characterFSM.defineTransition('walk', 'idle', (ctx)=> ctx.velocity.x===0);characterFSM.defineTransition('walk', 'run', (ctx)=> Math.abs(ctx.velocity.x) >150);// Jump has highest prioritycharacterFSM.defineTransition('idle', 'jump', (ctx)=>!ctx.isGrounded, 10);characterFSM.defineTransition('walk', 'jump', (ctx)=>!ctx.isGrounded, 10);// Game loop usagefunctiongameUpdate(dt:number) {// Update contextcharacterFSM.context.velocity.x=getInputVelocity();characterFSM.context.isGrounded=checkGrounded();// Evaluate transitionscharacterFSM.evaluateTransitions();// Update current statecharacterFSM.update(dt);}ECS Integration
Section titled “ECS Integration”import { Component, EntitySystem, Matcher } from'@esengine/ecs-framework';import { createStateMachine, type IStateMachine } from'@esengine/fsm';// State machine componentclassFSMComponentextendsComponent {fsm:IStateMachine<string>;constructor(initialState:string) {super();this.fsm=createStateMachine(initialState);}}// State machine systemclassFSMSystemextendsEntitySystem {constructor() {super(Matcher.all(FSMComponent));}protectedprocessEntity(entity:Entity, dt:number):void {constfsmComp = entity.getComponent(FSMComponent);fsmComp.fsm.evaluateTransitions();fsmComp.fsm.update(dt);}}Blueprint Nodes
Section titled “Blueprint Nodes”The FSM module provides blueprint nodes for visual scripting:
GetCurrentState- Get current stateTransitionTo- Transition to specified stateCanTransition- Check if transition is possibleIsInState- Check if in specified stateWasInState- Check if was ever in specified stateGetStateDuration- Get state durationEvaluateTransitions- Evaluate transition conditionsResetStateMachine- Reset state machine
Documentation
Section titled “Documentation”- API Reference - Complete API documentation
- Examples - Character FSM, AI behavior, ECS integration