Game Context
GameContext is the “glue” object that connects your game code to engine services. Instead of reaching for globals/singletons, scenes and game objects receive a context instance and can query what they need (canvas, scenes, audio, input, messaging).
Getting the context
From your Game instance:
const ctx = game.getContext();
From a Scene or GameObject that has been added to a scene (they get the context injected by the engine):
const ctx = this.getContext();
if (!ctx) return; // not attached yet
Why it can be null: a GameObject/Scene only gets a context after it’s registered with the running game/scene manager.
What GameContext contains
Core references
getGame(): access to theGameinstance (tick rate, saves, etc.)getCanvas(): the engineCanvasController(rendering helpers, raw 2D context access)getSceneManager(): theSceneManager(scene stack + transitions)getSoundManager(): theSoundManager(SFX/music playback)getKeyAccumulator(): the rawKeyAccumulator(usually you’ll use the helper methods below)
Scene control helpers
GameContext exposes a few convenience wrappers around the SceneManager:
ctx.setCurrentScene("MainMenu");
ctx.pushScene("PauseMenu");
ctx.popScene();
await ctx.transitionToScene("Level1", transition, "replace"); // or "push"
These are useful from game objects (UI buttons, triggers, etc.) without having to keep a reference to the scene manager.
Input helpers
The context keeps an internal KeyAccumulator, and provides simple queries:
if (ctx.isKeyPressed("ArrowLeft")) {
// move player
}
const pressed = ctx.getPressedKeys(); // string[]
Prefer these helpers over accessing the KeyAccumulator directly unless you need lower-level behavior.
Messaging (decoupled communication)
GameContext includes a small publish/subscribe MessageBus. It’s meant for “broadcast” style events where objects shouldn’t know about each other directly.
Sending a message
ctx.sendMessage("player:hit", { damage: 10 }, this);
channelis a string (use a naming convention likesystem:eventorfeature:event)payloadis any value (use TypeScript generics for strong typing)senderis optional; when you send from aGameObject, passthisso listeners can ignore their own events if needed
Subscribing to a message
const unsubscribe = ctx.subscribeToMessage<{ damage: number }>(
"player:hit",
(payload, sender) => {
console.log(payload.damage, sender);
}
);
// later:
unsubscribe();
Why subscribeToMessage returns a function: unsubscribing is how you prevent duplicate handlers when objects are recreated, and it avoids memory leaks in long-running games.
Convenience from GameObject
GameObject exposes small wrappers around the context:
this.sendMessage("ui:clicked", { id: "start" });
this.onMessage("ui:clicked", (payload) => console.log(payload));
Under the hood this uses the same GameContext message bus, just with less boilerplate.
Tick and frame counters
GameContext tracks:
getTickCount()andincrementTickCount(): the engine increments tick count fromSceneManager.tick().getFrameCount()andincrementFrameCount(): available for render-loop tracking (not currently incremented automatically in the engine).
You can use these for lightweight instrumentation (e.g., “do X every 30 ticks”).