State
State is a base class for a screen or mode with a predictable lifecycle. A State Machine selects the active state; subclasses react when they become active, update each frame, render, and clean up when another state replaces them.
Quick Start
Section titled “Quick Start”A state receives shared game capabilities once in its constructor from Game. Transition-specific data arrives later through enter().
/** @typedef {{ message: string }} DialogProps */
/** @extends {State<DialogProps>} */class DialogState extends State { enter(props) { super.enter(props); this.message = props.message; }
exit() { super.exit(); this.message = undefined; }
update(dt) { this.elapsed += dt; }
render(context) { context.fillText(this.message, 24, 24); }}The super.enter(props) and super.exit() calls are important: they store and clear the transition data used by activeProps.
flowchart LR A[Construct State with GameServices] --> B[enter props] B --> C[props are available through activeProps] C --> D[update and render each frame] D --> E[exit] E --> F[props are cleared] F --> B
Constructor and Properties
Section titled “Constructor and Properties”Constructor
Section titled “Constructor”new State(game);Creates a state with the shared GameServices object supplied by Game. game provides capabilities that live for the whole application, including input, timer, stateMachine, assets, the canvas, and its drawing context. Pass only data specific to one transition through enter(props).
this.game.timer.after(0.5, callback);The injected GameServices object. It is available for the entire lifetime of the state.
The transition data most recently received by enter(), or undefined before entry and after exit(). Prefer activeProps instead of reading props directly since it gives the expected Props type and checks that the state is active.
activeProps
Section titled “activeProps”const props = this.activeProps;Returns the current transition data as Props. It throws if the state has not entered yet or has already exited.
update() { const { message } = this.activeProps; console.log(message);}Lifecycle Methods
Section titled “Lifecycle Methods”enter(props)
Section titled “enter(props)”enter(props);Receives transition-specific data and stores it as the active props. Override it to initialize the state, and call super.enter(props) if the subclass uses activeProps.
exit()
Section titled “exit()”exit();Clears the active props. Override it to release state-specific work, and call super.exit() to preserve that cleanup.
update(dt)
Section titled “update(dt)”update(dt);Runs once per frame while the state is selected. dt is the elapsed time in seconds since the previous frame; see delta time for why updates use elapsed time instead of a fixed amount. The base implementation does nothing, so subclasses only override it when they need update behavior.
render(context)
Section titled “render(context)”render(context);Runs once per frame while the state is selected. context is the CanvasRenderingContext2D used for drawing. The base implementation does nothing.
Props Generics and JSDoc
Section titled “Props Generics and JSDoc”State is generic: each subclass can declare the shape of data it expects when entered. That lets checked JavaScript identify mistakes such as reading a missing property or treating a string as a number.
/** * @template [Props=Record<string, never>] */class State {}Read this declaration from left to right:
| Syntax | Meaning |
|---|---|
@template |
Declares a reusable type parameter, similar to TypeScript’s State<Props>. |
Props |
The name of this state’s transition-data type. |
[ ... ] |
Makes the type parameter optional in JSDoc. |
=Record<string, never> |
The default is an object with no permitted named properties, for states that do not need transition data. |
A subclass supplies its own type with @extends:
/** @typedef {{ message: string, dismissible?: boolean }} DialogProps */
/** @extends {State<DialogProps>} */class DialogState extends State { update() { const { message, dismissible } = this.activeProps; // message is a string; dismissible is boolean | undefined. }}