State Machine
StateMachine registers named State instances, selects one state at a time, and forwards each frame’s update and render calls to the selected state. Game owns the shared state machine used by application states.
Quick Start
Section titled “Quick Start”Register states before selecting one. A transition first exits the current state, then enters the next one with its transition data. Game.addState() can construct and register a state when you are using Game.
const stateMachine = new StateMachine();
stateMachine.add('home', new HomeState(game));stateMachine.add('dialog', new DialogState(game));
stateMachine.change('home');stateMachine.change('dialog', { message: 'Saved' });sequenceDiagram
participant Code as Application code
participant Machine as StateMachine
participant Current as Current State
participant Next as Next State
Code->>Machine: change("dialog", props)
Machine->>Current: exit()
Machine->>Next: enter(props)
Machine->>Machine: currentState = next state
loop Every frame
Machine->>Next: update(dt)
Machine->>Next: render(context)
end
Properties
Section titled “Properties”states
Section titled “states”/** @type {Map<string, State<unknown>>} */states;A Map from transition names to registered state instances. Use add() to register states instead of changing this map directly.
currentState
Section titled “currentState”/** @type {State<unknown> | undefined} */currentState;The selected state, or undefined before the first successful change() call.
Methods
Section titled “Methods”new StateMachine()
Section titled “new StateMachine()”Creates an empty state machine with no selected state.
add(stateName, state)
Section titled “add(stateName, state)”stateMachine.add(stateName, state);Registers a State instance under a string transition name. Names are usually lowercase kebab case, such as "main-menu" or "game-over".
stateMachine.add('settings', new SettingsState(game));Register every state before transitioning to it. Adding a duplicate name replaces the previously registered state because Map.set() replaces an existing value.
change(stateName, stateContext = {})
Section titled “change(stateName, stateContext = {})”stateMachine.change(stateName, stateContext);Selects a registered state. If a state is already selected, the machine calls its exit() first. It then finds stateName, stores that state as currentState, and calls enter(stateContext) on it.
stateMachine.change('dialog', { message: 'Your changes were saved.',});Use an object for transition data. The default is {}, which is suitable for a state that expects no props.
update(dt)
Section titled “update(dt)”stateMachine.update(dt);Forwards the elapsed time in seconds to currentState.update(dt). See delta time for how this makes movement and animations frame-rate independent.
render(context)
Section titled “render(context)”stateMachine.render(context);Forwards the drawing context to currentState.render(context). Game.render() clears the canvas before asking the state machine to render.
The Unknown Type
Section titled “The Unknown Type”A State can have a different props type from every other state:
/** @typedef {{ message: string }} DialogProps *//** @extends {State<DialogProps>} */class DialogState extends State {}
/** @typedef {{ userId: number }} ProfileProps *//** @extends {State<ProfileProps>} */class ProfileState extends State {}One StateMachine can store both instances. Its map is therefore annotated as Map<string, State<unknown>>:
/** @type {Map<string, State<unknown>>} */states = new Map();Read State<unknown> as “a state whose props shape is not known at this point in time.” unknown is safer than any: code cannot use an unknown value as a specific shape without checking or narrowing it first.
Reading the JSDoc
Section titled “Reading the JSDoc”| Syntax | Meaning |
|---|---|
Map<string, State<unknown>> |
A map with string keys and state-instance values whose individual props types vary. |
State<unknown> | undefined |
Either a state or no selected state yet. | means “or.” |
@param {unknown} [stateContext={}] |
An optional transition-data parameter whose default value is {}. The square brackets make the parameter optional. |
@returns {void} |
The method returns no value. |
The JSDoc types are checked only by tools configured to check JavaScript. In other words, they do not add runtime validation.