Scenes
A Scene is a named, reusable unit of game logic driven by aSceneManager from @g2w/core. The manager owns a fixed set of named scenes and updates only the active one — it runs no frame loop of its own; you drive it from onUpdate.
The Scene interface
All five hooks are optional and synchronous:
| Hook | When it runs |
|---|---|
init | At most once per instance, lazily on first activation. A failed init may be retried; enter never runs unless init completed. |
enter | Every time the scene becomes active. |
update | Once per frame with elapsed seconds. Game already clamps deltaTime. |
exit | When the scene stops being active — paired with every successful enter. |
dispose | Once when the manager is disposed, only for scenes whose init completed. |
Acquire resources in init or enter and release them in exit or dispose. Hook errors are never wrapped or swallowed — they propagate to whichever call triggered the hook.
Define and register scenes
import { SceneManager, type Scene } from "@g2w/core";
const menu: Scene = {
enter() { renderer.stage.addChild(menuView); },
update(dt) {
if (input.wasKeyPressed("Enter")) scenes.goTo("gameplay");
},
exit() { renderer.stage.removeChild(menuView); },
};
const scenes = new SceneManager({
scenes: { menu, gameplay },
initial: "menu",
});Registration claims the scene: an instance belongs to exactly one manager for its lifetime, and registering the same instance anywhere else throws SceneError. The claim is permanent — disposal of the owning manager does not release it. Create a new instance instead.
Wire it into the game
const game = new G2WGame({
// ...renderer, canvas, size
onUpdate: (deltaTime) => {
scenes.update(deltaTime); // no loop of its own
},
});
await game.init();
scenes.start();
game.start();
// scene disposal joins the game's LIFO finalizer chain:
// shutdown releases scenes before the renderer
game.addFinalizer(() => scenes.dispose());Transitions
goTo(name) exits the previous scene before entering the next one. Transitions apply immediately — except when requested from inside a running scene hook, where they are deferred to the start of the nextupdate, so the current update finishes on the scene that asked for the change. The most recent request always wins: it replaces any pending deferred transition, and requesting the already-active scene cancels a pending one. Unknown names throw SceneError without changing any state.
Disposal
dispose() is immediate, idempotent, and terminal: the active scene is exited, then every initialized scene is disposed in registration order. Cleanup is best-effort — one hook's failure does not prevent the remaining hooks, and the first error is rethrown (as anAggregateError when several fail) after all cleanup attempts complete.
Next steps
See Input for reading player actions insideupdate, orarchitecture notes for the ownership rules behind the claim system.