Game
game.ts:89 Extends AbstractComponentHost<Game>
Root container for an arcade2d application. A Game owns the renderer
(a PIXI Application under the hood), the per-frame ticker, and
everything else that is conceptually outside a World — input
samplers, audio mixers, asset registries, and similar page-scoped
services that don't belong to any individual simulation.
A Game is the entry point. Real applications start with:
const game = await Game.bootstrap({
backgroundColour: 0x101820,
canvas: { fill: 'window' },
});
const world = game.createWorld({
components: () => ({ ... }),
});Lifecycle and the tick
Game is itself a AbstractComponentHost, so any
Component attached to it goes through the standard
pre-update / update / post-update phases each frame. The order is:
game.onPreUpdate— every game component's pre-phase, in insertion order. Canonical use: input samplers snapshotting pending DOM event state so world-scoped code reads a consistent value all tick.game.onUpdate— every game component's main phase.game.onPostUpdate— every game component's post-phase.world.update()— if an active World exists, the engine drives its full three-phase tick. World-scoped reads of game state (e.g. World.getMouseState) therefore see values that were snapshotted during step 1.
The PIXI ticker drives Game.update; user code never calls it directly.
Worlds
Only one world is active at a time. Game.createWorld constructs
it (auto-attaching a Scene and import('./world').Camera),
and Game.destroyWorld tears it down. Switching between
"menu" and "gameplay" worlds is the canonical use of this pair —
destroy, then create the next one. Attempting to create a second world
while one is already active throws
ErrorCode.GAME_WORLD_ALREADY_EXISTS; attempting to destroy when
no world is active throws ErrorCode.GAME_WORLD_NOT_FOUND.
Cross-references
- GameOptions — bootstrap-time configuration.
- World — the simulation tier driven by
Game.update. - Mouse — the auto-attached input sampler exposed via Game.getMouseState.
Constructors
constructor(_application: Application, options: GameOptions): Game Parameters
-
_applicationApplication -
optionsGameOptions
Properties
enabled: boolean Master gate on every component update phase this host runs. When
false, the host's onPreUpdate, onUpdate, and onPostUpdate
iterations short-circuit at a single check — useful for freezing a
single object during a cutscene, pausing a UI widget while a menu is
up, or temporarily disabling a debug overlay without tearing the
components down.
The flag is not propagated to onAdded or onDestroy. Those
always fire so a host can never end up with half-attached components,
and a disabled object is still cleanly torn down when destroyed.
Defaults to true (active). Flip back to true and the host resumes
ticking from its preserved state on the next world update().
Accessors
activeWorld: null | World The currently active World, or null if no world has been
created (or the last one was destroyed). Most game code that needs the
world will close over the value returned by Game.createWorld
directly; this accessor is intended for higher-level orchestration
(scene-switching managers, dev tools).
assets: AssetLibrary The game's AssetLibrary — the page-scoped registry for loading, caching, and retrieving textures (and, later, other resources). Assets live at the game tier because they outlive any individual World; preload them here and reference them by key from world-scoped code.
Requires the auto-attached AssetLibrary registered under ASSET_LIBRARY_COMPONENT_KEY. If you removed it deliberately, reading this accessor throws ErrorCode.COMPONENT_NOT_FOUND.
audio: AudioEngine The game's AudioEngine — the page-scoped owner of the Web Audio context, the master and per-category gain buses, and the seam used by AssetLibrary to decode AudioAssets and by audio components (AudioSource, Music) to construct voices.
Lives at the game tier because audio outlives any individual World: a music track survives a world transition, and the pause-menu volume sliders mutate a single set of buses.
Requires the auto-attached AudioEngine registered under AUDIO_ENGINE_COMPONENT_KEY. If you removed it deliberately, reading this accessor throws ErrorCode.COMPONENT_NOT_FOUND.
canvas: HTMLCanvasElement The HTML canvas element that arcade2d renders into, owned by the
underlying PIXI application. Exposed for application-level concerns
that legitimately need the canvas — e.g. attaching a contextmenu
handler to suppress the browser's right-click menu over the game.
Methods
_createDependencyResolver(): unknown Subclass hook that produces the concrete dependency resolver the host
hands to a component's resolveDependencies. The World hosts a
resolver scoped to siblings only; a WorldObject hosts one that
also exposes cross-tier lookups against the parent world.
Engine-internal — never called by user code.
Returns
unknown _handleComponentDestroyError(error: unknown, key: string): void Hook for subclasses to intercept errors thrown by a component's
onDestroy during AbstractComponentHost.removeAllComponents.
Default behaviour is to log and swallow — a single bad component must
not prevent the rest of the host's components from being torn down.
Subclasses may override to route errors through their own reporting
channel.
Parameters
-
errorunknown -
keystring
Returns
void addComponent(key: string, component: Component<Game>, options: AddComponentOptions): Component<Game> Adds a new component to the host object. Throws if a component with the
specified key already exists. Calls onAdded() on the component once
registered with its host.
Parameters
-
keystring -
componentComponent<Game> -
optionsAddComponentOptions
addComponentFromFactory(key: string, factory: ComponentFactory<Game>, options: AddComponentOptions): Component<Game> Adds a new component to the host object using a factory function.
Internally produces the new component using the factory function, then
calls addComponent() with the result.
The advantage of using this method over addComponent() is that the
factory function is provided with the host.
Parameters
-
keystring -
factoryComponentFactory<Game> -
optionsAddComponentOptions
addComponents(components: ComponentMap<Game>, options: AddComponentOptions): ComponentMap<Game> Adds a new set of components to the host object. Throws if a component with
the specified key already exists. Calls onAdded() on each component
after they are all registered with the host, rather than one by one.
This is important for components that may want to reference each other
during the addition phase via host.getComponent() or similar methods.
It is recommended to use this method rather than addComponent() in
situations like initialization of a new host object.
Parameters
-
componentsComponentMap<Game> -
optionsAddComponentOptions
Returns
ComponentMap<Game> addComponentsFromFactories(map: ComponentFactoryMap<Game>, options?: AddComponentOptions): ComponentMap<Game> Adds a new set of components to the host object based on an input map of
component keys to factory functions. Behavious is equivalent to
addComponents() using the key and output of each factory function.
Parameters
-
mapComponentFactoryMap<Game> -
options?AddComponentOptions
Returns
ComponentMap<Game> createWorld(options: Omit<WorldOptions, 'components'> & { components?: (world: World) => Record<string, () => Component<World>> }): World Creates a new World and marks it as the active world for this
Game. The engine auto-attaches a Scene (parented to the PIXI
application's stage) and a Camera to every world it creates, so the
user's component factory sees both as already-resolvable siblings.
Only one world may be active at a time. Calling this method while a world already exists throws ErrorCode.GAME_WORLD_ALREADY_EXISTS; destroy the existing one first via Game.destroyWorld.
Parameters
-
optionsOmit<WorldOptions, 'components'> & { components?: (world: World) => Record<string, () => Component<World>> }
Returns
World Throws
EngineError with code ErrorCode.GAME_WORLD_ALREADY_EXISTS when an active world already exists on this game.
Example
const world = game.createWorld({
components: (world) => ({
physics: () => new PhysicsSystem(world),
}),
prefabs: prefabRegistry,
});destroy(): void Tears the game down completely: destroys the active world (if any),
runs onDestroy on every game component, unhooks the PIXI ticker, and
destroys the underlying PIXI application. The Game instance is
not reusable after this call.
Idempotent in the same sense as World.destroy — calling it twice is safe and the second call is a no-op.
Returns
void destroyWorld(): void Destroys the active world, releasing this Game's reference to it.
Equivalent to calling world.destroy() and then clearing
Game.activeWorld, in that order — the world's components and
objects all see their onDestroy hooks fire as part of the call.
Subsequent Game.createWorld calls succeed because the active world slot is now empty.
Returns
void Throws
EngineError with code ErrorCode.GAME_WORLD_NOT_FOUND when no world is currently active.
getComponent(key: string): T Gets a component from the host object using the key it was registered with.
Throws if the component does not exist. Performs an efficient lookup on a
local Map instance.
Parameters
-
keystring
Returns
T getComponentByType(type: ComponentHostConstructor<T>): T Gets a component from the host object using its type. Throws if no
component of the type exists, or if more than one component of the type
exists — in the multi-match case, getComponentByType deliberately
does not pick one for you. Use ComponentHost.getComponentsByType
when you genuinely expect multiple matches, or look the component up by
its string key.
Performs an O(n) lookup once per type initially, then caches the resolved key for O(1) lookups on subsequent calls. The cache is invalidated whenever a component is removed.
Parameters
-
typeComponentHostConstructor<T>
Returns
T getComponentsByType(type: ComponentHostConstructor<T>): readonly T[] Gets every component on the host of the given type, in the order they were originally registered. Returns an empty array if no components match.
Unlike ComponentHost.getComponentByType, this method never throws on multiplicity — it is the explicit "I expect more than one" accessor.
Parameters
-
typeComponentHostConstructor<T>
Returns
readonly T[] getHostReference(): Game Gets a reference to the host object that this component is attached to. Required for the compiler to be able to resolve the type of the host object correctly in some internal function calls (e.g. when resolving the type of the host object from a factory function).
Returns
Game getKeyboardState(): KeyboardState Returns the current keyboard state — the set of physical keys held at
tick-snapshot time, plus a isDown(code) predicate for membership
tests. Page-global; the same snapshot is visible to menu UI and to
gameplay code alike.
Keys are identified by KeyboardEvent.code (the physical key, not
the logical character) so movement bindings like KeyW/KeyA/
KeyS/KeyD keep working across keyboard layouts. See
KeyboardState for the convention and a pointer to MDN's
full code-value list.
Allocates a fresh KeyboardState (with a freshly-cloned
downKeys set) per call so callers can safely stash the result.
Requires the auto-attached Keyboard component registered under KEYBOARD_COMPONENT_KEY. If you removed it deliberately, calling this method throws ErrorCode.COMPONENT_NOT_FOUND.
Returns
KeyboardState getMouseState(): MouseSnapshot Returns the current screen-space mouse snapshot — canvas-local cursor position and the held/released state of the three standard buttons. Independent of any world or camera; the same snapshot is visible to menu UI and to gameplay code alike.
For the world-space projection of the cursor (camera-transformed),
reach for World.getMouseState instead. This method exists so
code working entirely in screen space (overlays, HUDs, intro menus
with no world yet) doesn't have to construct a world to read the
pointer.
Allocates a fresh MouseSnapshot (and a fresh Point for
screenPosition) per call so callers can safely stash the result.
Requires the auto-attached Mouse component registered under MOUSE_COMPONENT_KEY. If you removed it deliberately, calling this method throws ErrorCode.COMPONENT_NOT_FOUND.
Returns
MouseSnapshot getNullableComponent(key: string): null | T Gets a component from the host object using the key it was registered with.
Returns null if the component does not exist, rather than throwing an
error. Useful for referencing transient or optional components without
manually handling errors.
Parameters
-
keystring
Returns
null | T getNullableComponentByType(type: ComponentHostConstructor<T>): null | T Gets a component from the host object using its type. Returns null if
the component does not exist or if more than one component of the type
is registered (i.e. the lookup is ambiguous) — the nullable variant
collapses both "not found" and "ambiguous" into a single null. Use
ComponentHost.getComponentsByType when you need to distinguish
them.
Parameters
-
typeComponentHostConstructor<T>
Returns
null | T hasComponent(key: string): boolean Checks if the host object has a component with the specified key.
Parameters
-
keystring
Returns
boolean hasComponentByType(type: ComponentHostConstructor<T>): boolean Checks if the host object has a component with the specified type.
Parameters
-
typeComponentHostConstructor<T>
Returns
boolean removeAllComponents(): void Removes all components from the host object. Typically called internally
when the lifecycle of the host object is terminated. Differs from
individually removing components in that it first calls onDestroy() on
each component, then removes references from the host object in a separate
step. This allows cleaner teardown of components that may reference each
other.
Returns
void removeComponent(key: string): void Removes a component from the host object. Care should be taken when
manually removing components, as methods like getComponent() will throw
if components do not exist. Removal is idempotent and will do nothing if
the component does not exist i.e. was already removed, or never existed.
Parameters
-
keystring
Returns
void update(): void Runs the game's per-frame tick: every game component's pre/update/post
phase in order, then delegates to the active world's update() if one
exists. Wired to the PIXI ticker during construction, so user code
almost never calls this method directly; it's public only so tests
(and the rare callers that disable the ticker for deterministic
stepping) can drive a single tick on demand.
Returns
void bootstrap(options: GameOptions): Promise<Game> Creates and initialises a new Game. The async step is the PIXI
application's own init() — which constructs the WebGL/WebGPU context
and prepares the renderer. After it resolves, the canvas is appended to
document.body (when running in a browser), the engine's auto-attached
components are registered, the ticker is wired, and the returned
Game is ready to receive a Game.createWorld call.
Parameters
-
optionsGameOptions
Returns
Promise<Game> createHeadless(options: GameOptions): Game Synchronous test-only factory that constructs a Game backed by
a stub PIXI Application. No WebGL context, no canvas mounted
to the DOM, no ticker registered with a real renderer.
When to use this
Unit tests and headless tooling only. The engine's production
surface — every accessor that needs page-scoped services like the
mouse or keyboard — assumes a Game is present, so unit tests
that exercise a World in isolation need something to satisfy
the new mandatory game argument on the World constructor.
This factory exists to give those tests a cheap, synchronous game
without the cost of bootstrapping a real PIXI renderer.
When not to use this
- Never in production game code. The stub application does not
render anything, does not run a ticker, does not own a canvas
attached to the page. Anything that depends on actual draw output —
Scene's transform sync to the screen,AbstractGraphics's parenting to the stage during real rendering, any code expectingGame.updateto be driven by the PIXI ticker — will not behave correctly when the game is headless. Use Game.bootstrap instead, which performs the asynchronous WebGL/WebGPU init and mounts the canvas underdocument.body. - Not for renderer-level unit tests that need to observe the real
PIXI pipeline. Those should construct their own
Applicationand pass it to the publicGameconstructor.
The auto-attached Mouse and Keyboard components are
still registered on the returned game. In non-browser environments
(Node, jest's default node environment) their typeof window
guards short-circuit listener attachment, so they're effectively
inert — callable, but reporting empty input state.
Parameters
-
optionsGameOptions
Returns
Game