BMAD-METHOD/expansion-packs/bmad-wechat-mini-game-dev/data/development-guidelines.md

447 lines
11 KiB
Markdown

# Game Development Guidelines (WeChat Mini Game, JavaScript)
## Overview
This document establishes coding standards, architectural patterns, and development practices for game development using the WeChat Mini Game framework with JavaScript. These guidelines ensure consistency, performance (60+ FPS target), maintainability, and enforce Test-Driven Development (TDD) across all game development stories.
## Performance Philosophy
- **"Measure, don't guess"** - Profile everything with the WeChat Mini Game profiler
- **"Focus on what matters: framerate and responsiveness"** - 60+ FPS is the minimum, not the target
- **"The best code is no code"** - Simplicity beats cleverness
- **"Think about cache misses, not instruction counts"** - Memory access patterns matter most
## JavaScript Standards
### Naming Conventions
**Classes and Scripts:**
- PascalCase for class names: `PlayerController`, `GameData`, `InventorySystem`
- Snake_case for file names: `player_controller.js`, `game_data.js`
- Descriptive names that indicate purpose: `GameStateManager` not `GSM`
**Functions and Methods:**
- camelCase for functions: `calculateDamage()`, `processInput()`
- Descriptive verb phrases: `activateShield()` not `shield()`
- Private methods prefix with underscore: `_updateHealth()`
**Variables and Properties:**
- camelCase for variables: `playerHealth`, `movementSpeed`
- Constants in UPPER_SNAKE_CASE: `MAX_HEALTH`, `GRAVITY_FORCE`
- Boolean variables with is/has/can prefix: `isAlive`, `hasKey`, `canJump`
- Event names in snake_case: `health_changed`, `level_completed`
## WeChat Mini Game Architecture Patterns
### Object-Based Architecture
**Scene Composition Over Inheritance:**
```javascript
// Player.js - Single responsibility component
class PlayerHealth {
constructor(maxHealth) {
this.maxHealth = maxHealth;
this.currentHealth = maxHealth;
}
takeDamage(amount) {
this.currentHealth = Math.max(0, this.currentHealth - amount);
if (this.currentHealth === 0) {
// emit 'died' event
}
}
}
```
### Event-Based Communication
**Decouple Systems with Events:**
```javascript
// GameManager.js - Global manager
class GameManager {
constructor() {
this.score = 0;
this.currentLevel = 1;
}
startGame() {
this.score = 0;
this.currentLevel = 1;
// emit 'game_started' event
}
}
// Player.js - Connects to events
class Player {
constructor(gameManager) {
gameManager.on('game_over', this._onGameOver.bind(this));
}
_onGameOver() {
// Stop player movement
}
}
```
### JSON-Based Data Management
**Use JSON for Game Data:**
```javascript
// weapon_data.json
{
"weaponName": "Sword",
"damage": 10,
"attackSpeed": 1.0,
"sprite": "images/sword.png"
}
// Weapon.js - Uses the data
class Weapon {
constructor(weaponData) {
this.weaponData = weaponData;
}
attack() {
return this.weaponData ? this.weaponData.damage : 0;
}
}
```
## Performance Optimization
### Object Pooling (MANDATORY for Spawned Objects)
```javascript
// ObjectPool.js - Generic pooling system
class ObjectPool {
constructor(scene, initialSize) {
this.scene = scene;
this._pool = [];
for (let i = 0; i < initialSize; i++) {
let instance = this.scene.create();
instance.visible = false;
this._pool.push(instance);
}
}
getObject() {
for (let obj of this._pool) {
if (!obj.visible) {
obj.visible = true;
return obj;
}
}
// Expand pool if needed
let newObj = this.scene.create();
this._pool.push(newObj);
return newObj;
}
returnObject(obj) {
obj.visible = false;
}
}
```
### Process Optimization
**Use Appropriate Process Methods:**
```javascript
// For physics calculations (fixed timestep)
function _physics_process(delta) {
// Movement, collision detection
}
// For visual updates and input
function _process(delta) {
// Animations, UI updates
}
// Use timers or events instead of checking every frame
function _ready() {
// Use setTimeout or setInterval
}
```
### Memory Management
**Prevent Memory Leaks:**
```javascript
// Clean up event listeners
function _destroy() {
gameManager.off('score_changed', this._onScoreChanged);
}
```
## Test-Driven Development (MANDATORY)
### JavaScript Testing
**Write Tests FIRST:**
```javascript
// test/unit/test_player_health.js
const assert = require('assert');
const PlayerHealth = require('../../js/player/player_health.js');
describe('PlayerHealth', function() {
let playerHealth;
beforeEach(function() {
playerHealth = new PlayerHealth(100);
});
it('should reduce health when taking damage', function() {
playerHealth.takeDamage(30);
assert.strictEqual(playerHealth.currentHealth, 70);
});
it('should not go below zero health', function() {
playerHealth.takeDamage(120);
assert.strictEqual(playerHealth.currentHealth, 0);
});
});
```
## Input Handling
### WeChat Mini Game Input System
```javascript
// Handle touch events
wx.onTouchStart(function(res) {
// Handle touch start
});
wx.onTouchMove(function(res) {
// Handle touch move
});
wx.onTouchEnd(function(res) {
// Handle touch end
});
```
## Scene Management
### Scene Loading and Transitions
```javascript
// SceneManager.js - Global manager
class SceneManager {
constructor() {
this.currentScene = null;
}
changeScene(path) {
// Unload current scene
// Load new scene
}
}
```
## Project Structure
```
WeChatMiniGameProject/
├── images/ # Image assets
├── js/ # JavaScript scripts
│ ├── libs/ # Third-party libraries
│ ├── base/ # Base classes
│ ├── player/ # Player-related scripts
│ ├── enemies/ # Enemy scripts
│ ├── systems/ # Game systems
│ ├── ui/ # UI scripts
│ └── utils/ # Utility scripts
├── audio/ # Audio assets
├── game.js # Game entry point
├── game.json # Game configuration
├── project.config.json # Project configuration
└── project.private.config.json # Private project configuration
```
## Development Workflow
### TDD Story Implementation Process
1. **Read Story Requirements:**
- Understand acceptance criteria
- Identify performance requirements (60+ FPS)
2. **Write Tests FIRST (Red Phase):**
- Write failing unit tests
- Define expected behavior
- Run tests to confirm they fail
3. **Implement Feature (Green Phase):**
- Write minimal code to pass tests
- Follow WeChat Mini Game patterns and conventions
4. **Refactor (Refactor Phase):**
- Optimize for performance
- Clean up code structure
- Ensure 60+ FPS maintained
- Run profiler to validate
5. **Integration Testing:**
- Test scene interactions
- Validate performance targets
- Test on all platforms
6. **Update Documentation:**
- Mark story checkboxes complete
- Document performance metrics
- Update File List
### Performance Checklist
- [ ] Stable 60+ FPS achieved
- [ ] Object pooling for spawned entities
- [ ] No memory leaks detected
- [ ] Draw calls optimized
- [ ] Appropriate process methods used
- [ ] Events properly connected/disconnected
- [ ] Tests written FIRST (TDD)
- [ ] 80%+ test coverage
## Performance Targets
### Frame Rate Requirements
- **Mobile**: 60 FPS on mid-range devices
- **Frame Time**: <16.67ms consistently
### Memory Management
- **Scene Memory**: Keep under platform limits
- **Texture Memory**: Optimize imports, use compression
- **Object Pooling**: Required for bullets, particles, enemies
- **Reference Cleanup**: Prevent memory leaks
### Optimization Priorities
1. **Profile First**: Use the WeChat Mini Game profiler to identify bottlenecks
2. **Optimize Algorithms**: Better algorithms beat micro-optimizations
3. **Reduce Draw Calls**: Batch rendering, use atlases
## General Optimization
### Anti-Patterns
1. **Security Holes**
- Unvalidated user input
- Memory disclosure
2. **Platform Sabotage**
- Fighting the WeChat Mini Game's scene system
- Reimplementing platform features
- Ignoring hardware capabilities
## JavaScript Optimization
### Performance Destroyers
1. **Allocation Disasters**
- Creating Arrays/Objects in loops
- String concatenation with +
- Unnecessary object instantiation
- Resource loading in game loop
- Event connections without caching
2. **Process Method Abuse**
- Frame-by-frame checks for rare events
- Unnecessary process enabling
### JavaScript Death Sentences
```javascript
// CRIME: String concatenation in loop
for (let i = 0; i < 1000; i++) {
text += i; // Dies. Use Array.join()
}
// CRIME: Creating objects in loop
for (const enemy of enemies) {
let bullet = new Bullet(); // Dies. Object pool
}
// CRIME: Checking rare conditions every frame
function _process(delta) {
if (playerDied) { // Dies. Use events
gameOver();
}
}
// CRIME: Object creation without pooling
function spawnParticle() {
let p = new Particle(); // Dies. Pool everything spawned
this.addChild(p);
}
```
### The Only Acceptable JavaScript Patterns
```javascript
// GOOD: Cached object references
let scoreLabel = this.getChildByName('score');
// GOOD: Object pooling
let bulletPool = [];
function _ready() {
for (let i = 0; i < 50; i++) {
let bullet = new Bullet();
bullet.visible = false;
bulletPool.push(bullet);
}
}
// GOOD: Efficient string building
function buildText(count) {
let parts = [];
for (let i = 0; i < count; i++) {
parts.push(i);
}
return parts.join('');
}
// GOOD: Timer-based checks
function _ready() {
setInterval(this._checkRareCondition.bind(this), 1000);
}
// GOOD: Batch operations
let updatesPending = [];
function queueUpdate(value) {
updatesPending.push(value);
if (updatesPending.length === 1) {
setTimeout(this._processUpdates.bind(this), 0);
}
}
function _processUpdates() {
// Process all updates at once
for (const value of updatesPending) {
// Do work
}
updatesPending = [];
}
```
### JavaScript-Specific Optimization Rules
1. **NEVER create objects without pooling** - Pool or die
2. **NEVER concatenate strings in loops** - Array.join()
3. **ALWAYS batch similar operations** - One update, not many
4. **NEVER check conditions every frame** - Use events and timers
```