Game development quality gates and mandatory checks. Activate when building, reviewing, debugging, or deploying any game project (H5/Canvas/WebGL/Phaser/Three.js/2D/3D). Covers state cleanup, lifecycle management, input handling, audio, persistence, networking, anti-cheat, and performance. Use as pre-deploy checklist or when diagnosing game-specific bugs (state leaks, phantom timers, buff conflicts, memory growth, touch issues).
Mandatory quality standards for all game projects. Based on 70+ real bugs and industry best practices.
Bugs come from cross-state interactions, not individual features. Each feature works alone; they break in combination.
All exit paths (death/level-complete/quit/pause/scene-switch) call ONE cleanup method with options.
cleanupGameState(opts) {
// Fixed order: sub-objects → buffs+timers → UI → projectiles → (optional) enemies/controls/events
}
// Every exit: resetBall(), levelComplete(), gameOver(), onShutdown() → calls this
New feature = add one line here. Never scatter cleanup across exits.
Any code modifying attributes (speed/attack/size/defense) must check for active temporary effects first.
// ❌ speed = Math.max(speed, BASE_SPEED); // ignores slow buff
// ✅ speed = Math.max(speed, this._currentBaseSpeed); // buff-aware baseline
Extract all needed data before destroy()/dispose()/remove().
const { x, y } = obj; const color = obj.getData('color');
obj.destroy();
spawnParticles(x, y, color);
Track all setTimeout/setInterval/delayedCall/rAF. Cancel in cleanup.
this.activeTimers.push(this.time.delayedCall(10000, cb));
// In cleanup: this.activeTimers.forEach(t => t.remove(false));
Multiply all time-dependent logic by delta. Never assume 60fps.
// ✅ player.x += speed * (delta / 1000);
update(time, delta): delta in ms, divide by 1000clock.getDelta(): returns secondsOn scene/level switch, clean: event listeners, timers, rAF, audio nodes, object pools, WebGL resources (geometry/material/texture dispose), global state, pending fetch/XHR.
Verify: Chrome DevTools → Memory → heap snapshots before/after transition.
resume() inside a user interaction eventvisibilitychange → pause all audio when hidden, resume when visibleWeixinJSBridge.invoke('getNetworkType') before autoplayversion field for migration when game updatesvisibilitychange (hidden)wx.setStorage (not localStorage)All network calls (leaderboard/share/ads/sync): 5s timeout + local cache fallback + no blocking game flow on failure.
Three tiers: critical (startup, <2s) → level assets (loading screen) → deferred (background lazy load). Fatal error only for critical failures; degrade gracefully for non-critical.
Compression: GLB+Draco, WebP images, MP3+OGG dual audio, sprite atlases.
Client is untrusted. Server validates:
references/anti-cheat.md for implementation patternsFor Phaser-specific rules (pointer ID tracking, physics group cleanup, OVERLAP_BIAS, time vs physics pause):
→ Read references/phaser.md
For Three.js-specific rules (dispose trio, GLB compression pipeline, animation state machine, prune pitfalls):
→ Read references/threejs.md
Run this checklist before every deployment:
cleanupGameState()?→ See references/phaser.md or references/threejs.md for engine checklists.