boardgame-phaser/docs/GamePatterns.md

4.4 KiB

Game Architecture Patterns & Practices

Reference guide for implementing board games using boardgame-phaser framework. Explore packages/onitama-game/ and packages/sample-game/ for concrete implementations.

Architecture Overview

Games follow a layered architecture separating logic, presentation, and UI state:

packages/my-game/
├── src/
│   ├── game/          # Pure game logic (re-exported from boardgame-core)
│   ├── scenes/        # Phaser scenes (MenuScene, GameScene)
│   ├── spawners/      # Data-driven object lifecycle
│   ├── renderers/     # Renderers for game objects
│   ├── state/         # UI-only reactive state
│   ├── config.ts      # Centralized layout & style constants
│   └── ui/App.tsx     # Preact root component
Layer Responsibility
game/ State, commands, prompts, validation (pure logic)
scenes/ Phaser lifecycle, input handling, visual composition
spawners/ Reactive game object spawn/update/despawn
renderers/ Phaser visual representation of game objects
state/ UI-only reactive state (selection, hover, etc.)
ui/ Preact components bridging Phaser and DOM

Core Patterns

1. ReactiveScene / GameHostScene

Extend ReactiveScene(packages\framework\src\scenes\ReactiveScene.ts) to use reactive integration features.

  • Access game context for scene navigation
  • Use this.disposables for auto-cleanup on shutdown.

2. Spawner Pattern

Implement Spawner<TData, TObj> for data-driven objects.

  • *getData(): Yield objects that should exist.
  • getKey(): Unique identifier for diffing.
  • onSpawn(): Create Phaser objects.
  • onUpdate(): Handle data changes (animate if needed).
  • onDespawn(): Clean up with optional animation.
  • See: packages/onitama-game/src/spawners/

3. Reactive UI State

Use MutableSignal for UI-only state, separate from game state.

  • Mutate via .produce().
  • React via effect() from @preact/signals-core.
  • Clean up effects on object 'destroy' to prevent leaks.
  • See: packages/onitama-game/src/state/ui.ts

4. Custom Containers

Extend Phaser.GameObjects.Container to encapsulate visuals and state.

  • Store logical state as private signals.
  • Use effect() for reactive highlights/selections.

5. Tween Interruption

Always register state-related tweens: this.scene.addTweenInterruption(tween). Prevents visual glitches when game state changes mid-animation.

6. Scene Navigation

Use await this.sceneController.launch('SceneKey'). Register scenes in App.tsx via <PhaserScene>. Pass data via data prop.

Best Practices

  • Centralize Config: Keep CELL_SIZE, BOARD_OFFSET, colors, etc., in src/config.ts with as const. Avoid magic numbers.
  • Type Imports: Use import type { Foo } from 'bar' for type-only imports.
  • Input Handling: Use this.add.zone() for grid/cell-based input zones.
  • Cleanup: Always dispose effect() on 'destroy'. Use this.disposables.add() for scene-level resources.
  • Keep files short and focused*: Each file should have a single responsibility, and not exceed 200 lines.

Common Pitfalls

Pitfall Solution
Duplicate constants across files Export from single config.ts or shared spawner
Missing tween interruptions Always call addTweenInterruption()
Effect memory leaks this.on('destroy', () => dispose())
Hardcoded magic numbers Extract to src/config.ts
UI state staleness Clear related selections on state change

Testing

Add Vitest for UI state transitions, coordinate conversions, and spawner logic. See packages/framework/ for test setup examples.

Quick Reference

Pattern Purpose
GameHostScene Connect Phaser to game host
Spawner<T, TObj> Reactive object lifecycle
MutableSignal UI-only reactive state
effect() React to signal changes in Phaser objects
addTweenInterruption() Prevent animation race conditions
sceneController.launch() Navigate between scenes
spawnEffect() Register spawners
this.disposables.add() Auto-cleanup resources
  • AGENTS.md — Project overview, commands, and code style
  • docs/GameModule.md — GameModule implementation guide
  • packages/framework/src/boardgame-phaser source code
  • packages/onitama-game/src/ — Complete game implementation reference