Dependency Injection
EngineContext
Section titled “EngineContext”EngineContext is the typed dependency injection container at the heart of
YAGE. Plugins register services during install(), and the rest of the engine
resolves them by key.
import { EngineContext, ServiceKey } from "@yagejs/core";ServiceKey
Section titled “ServiceKey”Every service is identified by a ServiceKey<T>. The type parameter ensures
that resolve() returns the correct type without casts:
import { ServiceKey } from "@yagejs/core";
// Define a key with a descriptive labelconst AudioMixerKey = ServiceKey<AudioMixer>("AudioMixer");Never use plain strings for service resolution — always define a ServiceKey.
Registering and Resolving
Section titled “Registering and Resolving”// In a plugin's install():function install(context: EngineContext) { const mixer = new AudioMixer(); context.register(AudioMixerKey, mixer);}
// Resolve (throws if not registered)const mixer = context.resolve(AudioMixerKey);
// Try resolve (returns undefined if not registered)const mixer = context.tryResolve(AudioMixerKey);
// Check existenceif (context.has(AudioMixerKey)) { // ...}context.resolve() throws a clear error listing the missing key name.
Prefer it when the service is required. Use context.tryResolve() for optional
services.
Well-Known Keys
Section titled “Well-Known Keys”YAGE’s core and official plugins register these services:
| Key | Type | Package |
|---|---|---|
EngineKey | Engine | @yagejs/core |
EventBusKey | EventBus | @yagejs/core |
SceneManagerKey | SceneManager | @yagejs/core |
LoggerKey | Logger | @yagejs/core |
QueryCacheKey | QueryCache | @yagejs/core |
ErrorBoundaryKey | ErrorBoundary | @yagejs/core |
GameLoopKey | GameLoop | @yagejs/core |
InspectorKey | Inspector | @yagejs/core |
SystemSchedulerKey | SystemScheduler | @yagejs/core |
ProcessSystemKey | ProcessSystem | @yagejs/core |
AssetManagerKey | AssetManager | @yagejs/core |
Accessing Services from Components
Section titled “Accessing Services from Components”Inside a component, use this.use(key). The result is cached after the first
call so repeated access is free:
import { Component } from "@yagejs/core";import { InputManagerKey } from "@yagejs/input";import { PhysicsWorldKey } from "@yagejs/physics";
class PlayerController extends Component { update(dt: number) { const input = this.use(InputManagerKey); const physics = this.use(PhysicsWorldKey);
if (input.isPressed("jump")) { const body = this.sibling(RigidBody); physics.applyImpulse(body, { x: 0, y: -600 }); } }}Accessing Services from Systems
Section titled “Accessing Services from Systems”Systems receive the EngineContext in onRegister. Store references there or
call this.use(key) during update:
import { System, Phase, EngineContext } from "@yagejs/core";
class DebugOverlaySystem extends System { readonly phase = Phase.Render; readonly priority = 9999;
private logger!: Logger;
onRegister(context: EngineContext) { this.logger = context.resolve(LoggerKey); }
update(dt: number) { const stats = this.use(GameLoopKey).stats; this.logger.debug(`FPS: ${stats.fps}`); }}Custom Services
Section titled “Custom Services”Register your own services for shared game state:
import { ServiceKey, Plugin, EngineContext } from "@yagejs/core";
interface Inventory { items: string[]; add(item: string): void; has(item: string): boolean;}
export const InventoryKey = ServiceKey<Inventory>("Inventory");
export function inventoryPlugin(): Plugin { return { name: "inventory", install(context: EngineContext) { context.register(InventoryKey, { items: [], add(item) { this.items.push(item); }, has(item) { return this.items.includes(item); }, }); }, };}Any component or system can then this.use(InventoryKey) to access the shared
inventory.