Project Structure
Every Quantum Forge game follows the four-layer pattern. Here's what each file does and where things go.
Directory Layout
After npx quantum-forge init my-game:
my-game/
├── package.json # Dependencies, scripts
├── tsconfig.json # TypeScript config with path aliases
├── vite.config.ts # Vite + Quantum Forge plugin
├── index.html # Canvas element + entry point
├── CLAUDE.md # AI assistant context
├── src/
│ ├── main.ts # Controller — wires everything
│ ├── engine/
│ │ └── GameEngine.ts # Engine — state coordinator
│ ├── logic/
│ │ └── GameLogic.ts # Pure functions — game rules
│ ├── rendering/
│ │ └── GameRenderer.ts # Renderer — draws from state
│ └── game.test.ts # Tests
└── dist/ # Built WASM artifacts (after setup)The Four Layers
1. logic/GameLogic.ts — Pure Functions
All game rules as pure functions. No imports from engine, renderer, or framework. Easy to test.
export function canMove(player: Player, dx: number, bounds: Bounds): boolean {
return player.x + dx >= 0 && player.x + dx < bounds.width;
}2. engine/GameEngine.ts — State Coordinator
Extends Engine<YourState>. Holds state, exposes helpers. Never contains logic or animation.
import { Engine } from "@quantum-realm-games/quantum-forge-framework/engine";
export class GameEngine extends Engine<GameState> {
getHelpers() {
return {
movePlayer: (dx: number, dy: number) => { /* update state */ },
};
}
}3. rendering/GameRenderer.ts — Visual Output
Extends PixiRenderer or CanvasRenderer. Reads state, draws pixels. Never computes.
import { PixiRenderer } from "@quantum-realm-games/quantum-forge-framework/rendering";
export class GameRenderer extends PixiRenderer {
protected draw(state: GameState) { /* render from state */ }
}4. main.ts — Controller
Creates and wires engine, renderer, input, game loop. Handles timing and user interaction.
const engine = new GameEngine(logger);
const renderer = new GameRenderer({ canvas, logger });
const input = new InputManager({ logger });
const loop = new GameLoop({ update, render, targetFps: 60, logger });
await renderer.init();
loop.start();Adding Quantum
When your game needs quantum mechanics, add a registry file:
src/
├── logic/
│ ├── GameLogic.ts
│ └── QuantumRegistry.ts ← newThe registry extends QuantumPropertyManager:
import { QuantumPropertyManager, ensureLoaded } from "@quantum-realm-games/quantum-forge-framework/quantum";
export class QuantumRegistry extends QuantumPropertyManager {
constructor(logger?: any) { super({ dimension: 2, logger }); }
// Game-specific quantum operations
}Initialize in main.ts:
await ensureLoaded();
const registry = new QuantumRegistry(logger);
const engine = new GameEngine(registry, logger);Adding Packages
Use the CLI to add optional packages:
npm run add-systemOr import directly:
import { InputManager } from "@quantum-realm-games/quantum-forge-framework/input";
import { SpatialGrid } from "@quantum-realm-games/quantum-forge-framework/collision";
import { AudioManager } from "@quantum-realm-games/quantum-forge-framework/audio";Import Paths
Within a scaffolded project, imports use the package's subpath exports:
// ✅ Consumer imports
import { Engine } from "@quantum-realm-games/quantum-forge-framework/engine";
import { PixiRenderer } from "@quantum-realm-games/quantum-forge-framework/rendering";
import { InputManager } from "@quantum-realm-games/quantum-forge-framework/input";Within the framework monorepo itself, imports use path aliases:
// Framework-internal imports (not for consumers)
import { Engine } from "@core/engine/Engine";
import { InputManager } from "@packages/input";