doc status updates
This commit is contained in:
@@ -1,528 +0,0 @@
|
||||
# Web Game Engine Architecture Guide
|
||||
|
||||
This guide provides web game engine-specific guidance (Phaser, PixiJS, Three.js, Babylon.js) for solution architecture generation.
|
||||
|
||||
---
|
||||
|
||||
## Web Game-Specific Questions
|
||||
|
||||
### 1. Engine and Technology Selection
|
||||
|
||||
**Ask:**
|
||||
|
||||
- Which engine? (Phaser 3, PixiJS, Three.js, Babylon.js, custom Canvas/WebGL)
|
||||
- TypeScript or JavaScript?
|
||||
- Build tool? (Vite, Webpack, Rollup, Parcel)
|
||||
- Target platform(s)? (Desktop web, mobile web, PWA, Cordova/Capacitor wrapper)
|
||||
|
||||
**Guidance:**
|
||||
|
||||
- **Phaser 3**: Full-featured 2D game framework, great for beginners
|
||||
- **PixiJS**: 2D rendering library, more low-level than Phaser
|
||||
- **Three.js**: 3D graphics library, mature ecosystem
|
||||
- **Babylon.js**: Complete 3D game engine, WebXR support
|
||||
- **TypeScript**: Recommended for all web games (type safety, better tooling)
|
||||
- **Vite**: Modern, fast, HMR - best for development
|
||||
|
||||
**Record ADR:** Engine selection and build tooling
|
||||
|
||||
---
|
||||
|
||||
### 2. Architecture Pattern
|
||||
|
||||
**Ask:**
|
||||
|
||||
- Scene-based architecture? (Phaser scenes, custom scene manager)
|
||||
- ECS (Entity Component System)? (Libraries: bitECS, ecsy)
|
||||
- State management? (Redux, Zustand, custom FSM)
|
||||
|
||||
**Guidance:**
|
||||
|
||||
- **Scene-based**: Natural for Phaser, good for level-based games
|
||||
- **ECS**: Better performance for large entity counts (100s+)
|
||||
- **FSM**: Good for simple state transitions (menu → game → gameover)
|
||||
|
||||
**Phaser Pattern:**
|
||||
|
||||
```typescript
|
||||
// MainMenuScene.ts
|
||||
export class MainMenuScene extends Phaser.Scene {
|
||||
constructor() {
|
||||
super({ key: 'MainMenu' });
|
||||
}
|
||||
|
||||
create() {
|
||||
this.add.text(400, 300, 'Main Menu', { fontSize: '32px' });
|
||||
|
||||
const startButton = this.add
|
||||
.text(400, 400, 'Start Game', { fontSize: '24px' })
|
||||
.setInteractive()
|
||||
.on('pointerdown', () => {
|
||||
this.scene.start('GameScene');
|
||||
});
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Record ADR:** Architecture pattern and scene management
|
||||
|
||||
---
|
||||
|
||||
### 3. Asset Management
|
||||
|
||||
**Ask:**
|
||||
|
||||
- Asset loading strategy? (Preload all, lazy load, progressive)
|
||||
- Texture atlas usage? (TexturePacker, built-in tools)
|
||||
- Audio format strategy? (MP3, OGG, WebM)
|
||||
|
||||
**Guidance:**
|
||||
|
||||
- **Preload**: Load all assets at start (simple, small games)
|
||||
- **Lazy load**: Load per-level (better for larger games)
|
||||
- **Texture atlases**: Essential for performance (reduce draw calls)
|
||||
- **Audio**: MP3 for compatibility, OGG for smaller size, use both
|
||||
|
||||
**Phaser loading:**
|
||||
|
||||
```typescript
|
||||
class PreloadScene extends Phaser.Scene {
|
||||
preload() {
|
||||
// Show progress bar
|
||||
this.load.on('progress', (value: number) => {
|
||||
console.log('Loading: ' + Math.round(value * 100) + '%');
|
||||
});
|
||||
|
||||
// Load assets
|
||||
this.load.atlas('sprites', 'assets/sprites.png', 'assets/sprites.json');
|
||||
this.load.audio('music', ['assets/music.mp3', 'assets/music.ogg']);
|
||||
this.load.audio('jump', ['assets/sfx/jump.mp3', 'assets/sfx/jump.ogg']);
|
||||
}
|
||||
|
||||
create() {
|
||||
this.scene.start('MainMenu');
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Record ADR:** Asset loading and management strategy
|
||||
|
||||
---
|
||||
|
||||
## Web Game-Specific Architecture Sections
|
||||
|
||||
### Performance Optimization
|
||||
|
||||
**Web-specific considerations:**
|
||||
|
||||
- **Object Pooling**: Mandatory for bullets, particles, enemies (avoid GC pauses)
|
||||
- **Sprite Batching**: Use texture atlases, minimize state changes
|
||||
- **Canvas vs WebGL**: WebGL for better performance (most games)
|
||||
- **Draw Call Reduction**: Batch similar sprites, use sprite sheets
|
||||
- **Memory Management**: Watch heap size, profile with Chrome DevTools
|
||||
|
||||
**Object Pooling Pattern:**
|
||||
|
||||
```typescript
|
||||
class BulletPool {
|
||||
private pool: Bullet[] = [];
|
||||
private scene: Phaser.Scene;
|
||||
|
||||
constructor(scene: Phaser.Scene, size: number) {
|
||||
this.scene = scene;
|
||||
for (let i = 0; i < size; i++) {
|
||||
const bullet = new Bullet(scene);
|
||||
bullet.setActive(false).setVisible(false);
|
||||
this.pool.push(bullet);
|
||||
}
|
||||
}
|
||||
|
||||
spawn(x: number, y: number, velocityX: number, velocityY: number): Bullet | null {
|
||||
const bullet = this.pool.find((b) => !b.active);
|
||||
if (bullet) {
|
||||
bullet.spawn(x, y, velocityX, velocityY);
|
||||
}
|
||||
return bullet || null;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Target Performance:**
|
||||
|
||||
- **Desktop**: 60 FPS minimum
|
||||
- **Mobile**: 60 FPS (high-end), 30 FPS (low-end)
|
||||
- **Profile with**: Chrome DevTools Performance tab, Phaser Debug plugin
|
||||
|
||||
---
|
||||
|
||||
### Input Handling
|
||||
|
||||
**Multi-input support:**
|
||||
|
||||
```typescript
|
||||
class GameScene extends Phaser.Scene {
|
||||
private cursors?: Phaser.Types.Input.Keyboard.CursorKeys;
|
||||
private wasd?: { [key: string]: Phaser.Input.Keyboard.Key };
|
||||
|
||||
create() {
|
||||
// Keyboard
|
||||
this.cursors = this.input.keyboard?.createCursorKeys();
|
||||
this.wasd = this.input.keyboard?.addKeys('W,S,A,D') as any;
|
||||
|
||||
// Mouse/Touch
|
||||
this.input.on('pointerdown', (pointer: Phaser.Input.Pointer) => {
|
||||
this.handleClick(pointer.x, pointer.y);
|
||||
});
|
||||
|
||||
// Gamepad (optional)
|
||||
this.input.gamepad?.on('down', (pad, button, index) => {
|
||||
this.handleGamepadButton(button);
|
||||
});
|
||||
}
|
||||
|
||||
update() {
|
||||
// Handle keyboard input
|
||||
if (this.cursors?.left.isDown || this.wasd?.A.isDown) {
|
||||
this.player.moveLeft();
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### State Persistence
|
||||
|
||||
**LocalStorage pattern:**
|
||||
|
||||
```typescript
|
||||
interface GameSaveData {
|
||||
level: number;
|
||||
score: number;
|
||||
playerStats: {
|
||||
health: number;
|
||||
lives: number;
|
||||
};
|
||||
}
|
||||
|
||||
class SaveManager {
|
||||
private static SAVE_KEY = 'game_save_data';
|
||||
|
||||
static save(data: GameSaveData): void {
|
||||
localStorage.setItem(this.SAVE_KEY, JSON.stringify(data));
|
||||
}
|
||||
|
||||
static load(): GameSaveData | null {
|
||||
const data = localStorage.getItem(this.SAVE_KEY);
|
||||
return data ? JSON.parse(data) : null;
|
||||
}
|
||||
|
||||
static clear(): void {
|
||||
localStorage.removeItem(this.SAVE_KEY);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Source Tree Structure
|
||||
|
||||
**Phaser + TypeScript + Vite:**
|
||||
|
||||
```
|
||||
project/
|
||||
├── public/ # Static assets
|
||||
│ ├── assets/
|
||||
│ │ ├── sprites/
|
||||
│ │ ├── audio/
|
||||
│ │ │ ├── music/
|
||||
│ │ │ └── sfx/
|
||||
│ │ └── fonts/
|
||||
│ └── index.html
|
||||
├── src/
|
||||
│ ├── main.ts # Game initialization
|
||||
│ ├── config.ts # Phaser config
|
||||
│ ├── scenes/ # Game scenes
|
||||
│ │ ├── PreloadScene.ts
|
||||
│ │ ├── MainMenuScene.ts
|
||||
│ │ ├── GameScene.ts
|
||||
│ │ └── GameOverScene.ts
|
||||
│ ├── entities/ # Game objects
|
||||
│ │ ├── Player.ts
|
||||
│ │ ├── Enemy.ts
|
||||
│ │ └── Bullet.ts
|
||||
│ ├── systems/ # Game systems
|
||||
│ │ ├── InputManager.ts
|
||||
│ │ ├── AudioManager.ts
|
||||
│ │ └── SaveManager.ts
|
||||
│ ├── utils/ # Utilities
|
||||
│ │ ├── ObjectPool.ts
|
||||
│ │ └── Constants.ts
|
||||
│ └── types/ # TypeScript types
|
||||
│ └── index.d.ts
|
||||
├── tests/ # Unit tests
|
||||
├── package.json
|
||||
├── tsconfig.json
|
||||
├── vite.config.ts
|
||||
└── README.md
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Testing Strategy
|
||||
|
||||
**Jest + TypeScript:**
|
||||
|
||||
```typescript
|
||||
// Player.test.ts
|
||||
import { Player } from '../entities/Player';
|
||||
|
||||
describe('Player', () => {
|
||||
let player: Player;
|
||||
|
||||
beforeEach(() => {
|
||||
// Mock Phaser scene
|
||||
const mockScene = {
|
||||
add: { sprite: jest.fn() },
|
||||
physics: { add: { sprite: jest.fn() } },
|
||||
} as any;
|
||||
|
||||
player = new Player(mockScene, 0, 0);
|
||||
});
|
||||
|
||||
test('takes damage correctly', () => {
|
||||
player.health = 100;
|
||||
player.takeDamage(20);
|
||||
expect(player.health).toBe(80);
|
||||
});
|
||||
|
||||
test('dies when health reaches zero', () => {
|
||||
player.health = 10;
|
||||
player.takeDamage(20);
|
||||
expect(player.alive).toBe(false);
|
||||
});
|
||||
});
|
||||
```
|
||||
|
||||
**E2E Testing:**
|
||||
|
||||
- Playwright for browser automation
|
||||
- Cypress for interactive testing
|
||||
- Test game states, not individual frames
|
||||
|
||||
---
|
||||
|
||||
### Deployment and Build
|
||||
|
||||
**Build for production:**
|
||||
|
||||
```json
|
||||
// package.json scripts
|
||||
{
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
"build": "tsc andand vite build",
|
||||
"preview": "vite preview",
|
||||
"test": "jest"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Deployment targets:**
|
||||
|
||||
- **Static hosting**: Netlify, Vercel, GitHub Pages, AWS S3
|
||||
- **CDN**: Cloudflare, Fastly for global distribution
|
||||
- **PWA**: Service worker for offline play
|
||||
- **Mobile wrapper**: Cordova or Capacitor for app stores
|
||||
|
||||
**Optimization:**
|
||||
|
||||
```typescript
|
||||
// vite.config.ts
|
||||
export default defineConfig({
|
||||
build: {
|
||||
rollupOptions: {
|
||||
output: {
|
||||
manualChunks: {
|
||||
phaser: ['phaser'], // Separate Phaser bundle
|
||||
},
|
||||
},
|
||||
},
|
||||
minify: 'terser',
|
||||
terserOptions: {
|
||||
compress: {
|
||||
drop_console: true, // Remove console.log in prod
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Specialist Recommendations
|
||||
|
||||
### Audio Designer
|
||||
|
||||
**When needed:** Games with music, sound effects, ambience
|
||||
**Responsibilities:**
|
||||
|
||||
- Web Audio API architecture
|
||||
- Audio sprite creation (combine sounds into one file)
|
||||
- Music loop management
|
||||
- Sound effect implementation
|
||||
- Audio performance on web (decode strategy)
|
||||
|
||||
### Performance Optimizer
|
||||
|
||||
**When needed:** Mobile web games, complex games
|
||||
**Responsibilities:**
|
||||
|
||||
- Chrome DevTools profiling
|
||||
- Object pooling implementation
|
||||
- Draw call optimization
|
||||
- Memory management
|
||||
- Bundle size optimization
|
||||
- Network performance (asset loading)
|
||||
|
||||
### Monetization Specialist
|
||||
|
||||
**When needed:** F2P web games
|
||||
**Responsibilities:**
|
||||
|
||||
- Ad network integration (Google AdSense, AdMob for web)
|
||||
- In-game purchases (Stripe, PayPal)
|
||||
- Analytics (Google Analytics, custom events)
|
||||
- A/B testing frameworks
|
||||
- Economy design
|
||||
|
||||
### Platform Specialist
|
||||
|
||||
**When needed:** Mobile wrapper apps (Cordova/Capacitor)
|
||||
**Responsibilities:**
|
||||
|
||||
- Native plugin integration
|
||||
- Platform-specific performance tuning
|
||||
- App store submission
|
||||
- Device compatibility testing
|
||||
- Push notification setup
|
||||
|
||||
---
|
||||
|
||||
## Common Pitfalls
|
||||
|
||||
1. **Not using object pooling** - Frequent instantiation causes GC pauses
|
||||
2. **Too many draw calls** - Use texture atlases and sprite batching
|
||||
3. **Loading all assets at once** - Causes long initial load times
|
||||
4. **Not testing on mobile** - Performance vastly different on phones
|
||||
5. **Ignoring bundle size** - Large bundles = slow load times
|
||||
6. **Not handling window resize** - Web games run in resizable windows
|
||||
7. **Forgetting audio autoplay restrictions** - Browsers block auto-play without user interaction
|
||||
|
||||
---
|
||||
|
||||
## Engine-Specific Patterns
|
||||
|
||||
### Phaser 3
|
||||
|
||||
```typescript
|
||||
const config: Phaser.Types.Core.GameConfig = {
|
||||
type: Phaser.AUTO, // WebGL with Canvas fallback
|
||||
width: 800,
|
||||
height: 600,
|
||||
physics: {
|
||||
default: 'arcade',
|
||||
arcade: { gravity: { y: 300 }, debug: false },
|
||||
},
|
||||
scene: [PreloadScene, MainMenuScene, GameScene, GameOverScene],
|
||||
};
|
||||
|
||||
const game = new Phaser.Game(config);
|
||||
```
|
||||
|
||||
### PixiJS
|
||||
|
||||
```typescript
|
||||
const app = new PIXI.Application({
|
||||
width: 800,
|
||||
height: 600,
|
||||
backgroundColor: 0x1099bb,
|
||||
});
|
||||
|
||||
document.body.appendChild(app.view);
|
||||
|
||||
const sprite = PIXI.Sprite.from('assets/player.png');
|
||||
app.stage.addChild(sprite);
|
||||
|
||||
app.ticker.add((delta) => {
|
||||
sprite.rotation += 0.01 * delta;
|
||||
});
|
||||
```
|
||||
|
||||
### Three.js
|
||||
|
||||
```typescript
|
||||
const scene = new THREE.Scene();
|
||||
const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
|
||||
const renderer = new THREE.WebGLRenderer();
|
||||
|
||||
renderer.setSize(window.innerWidth, window.innerHeight);
|
||||
document.body.appendChild(renderer.domElement);
|
||||
|
||||
const geometry = new THREE.BoxGeometry();
|
||||
const material = new THREE.MeshBasicMaterial({ color: 0x00ff00 });
|
||||
const cube = new THREE.Mesh(geometry, material);
|
||||
scene.add(cube);
|
||||
|
||||
function animate() {
|
||||
requestAnimationFrame(animate);
|
||||
cube.rotation.x += 0.01;
|
||||
renderer.render(scene, camera);
|
||||
}
|
||||
animate();
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Key Architecture Decision Records
|
||||
|
||||
### ADR Template for Web Games
|
||||
|
||||
**ADR-XXX: [Title]**
|
||||
|
||||
**Context:**
|
||||
What web game-specific issue are we solving?
|
||||
|
||||
**Options:**
|
||||
|
||||
1. Phaser 3 (full framework)
|
||||
2. PixiJS (rendering library)
|
||||
3. Three.js/Babylon.js (3D)
|
||||
4. Custom Canvas/WebGL
|
||||
|
||||
**Decision:**
|
||||
We chose [Option X]
|
||||
|
||||
**Web-specific Rationale:**
|
||||
|
||||
- Engine features vs bundle size
|
||||
- Community and plugin ecosystem
|
||||
- TypeScript support
|
||||
- Performance on target devices (mobile web)
|
||||
- Browser compatibility
|
||||
- Development velocity
|
||||
|
||||
**Consequences:**
|
||||
|
||||
- Impact on bundle size (Phaser ~1.2MB gzipped)
|
||||
- Learning curve
|
||||
- Platform limitations
|
||||
- Plugin availability
|
||||
|
||||
---
|
||||
|
||||
_This guide is specific to web game engines. For native engines, see:_
|
||||
|
||||
- game-engine-unity-guide.md
|
||||
- game-engine-godot-guide.md
|
||||
- game-engine-unreal-guide.md
|
||||
Reference in New Issue
Block a user