game-architecture
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseGame Architecture Patterns
游戏架构模式
Reference knowledge for building well-structured browser games. These patterns apply to both Three.js (3D) and Phaser (2D) games.
构建结构良好的浏览器游戏的参考知识。这些模式同时适用于 Three.js(3D)和 Phaser(2D)游戏。
Reference Files
参考文件
For detailed reference, see companion files in this directory:
- — Object pooling, delta-time normalization, resource disposal, wave/spawn systems, buff/powerup system, haptic feedback, asset management
system-patterns.md
如需详细参考,请参阅此目录中的配套文件:
- — 对象池、delta-time 标准化、资源释放、波次/生成系统、增益/强化系统、触觉反馈、资产管理
system-patterns.md
Core Principles
核心原则
-
Core Loop First: Implement the minimum gameplay loop before any polish. The order is: input -> movement -> fail condition -> scoring -> restart. Only after the core loop works should you add visuals, audio, or juice. Keep initial scope small: 1 scene/level, 1 mechanic, 1 fail condition.
-
Event-Driven Communication: Modules never import each other for communication. All cross-module messaging goes through a singleton EventBus with predefined event constants.
-
Centralized State: A single GameState singleton holds all game state. Systems read state directly and modify it through events. No scattered state across modules.
-
Configuration Centralization: Every magic number, balance value, asset path, spawn point, and timing value goes in. Game logic files contain zero hardcoded values.
Constants.js -
Orchestrator Pattern: Oneclass initializes all systems, manages game flow (boot -> gameplay -> death/win -> restart), and runs the main loop. Systems don't self-initialize. No title screen by default — boot directly into gameplay. Only add a title/menu scene if the user explicitly asks for one.
Game.js -
Restart-Safe and Deterministic: Gameplay must survive full restart cycles cleanly.restores a complete clean slate. All event listeners are removed in cleanup/shutdown. No stale references, lingering timers, leaked tweens, or orphaned physics bodies survive across restarts. Test by restarting 3x in a row — the third run must behave identically to the first.
GameState.reset() -
Clear Separation of Concerns: Code is organized into functional layers:
- - Foundation (Game, EventBus, GameState, Constants)
core/ - - Engine-level systems (input, physics, audio, particles)
systems/ - - Game mechanics (player, enemies, weapons, scoring)
gameplay/ - - World building (level construction, asset loading)
level/ - - Interface (menus, HUD, overlays)
ui/
-
核心循环优先:在进行任何润色之前,先实现最小可用的游戏循环。顺序为:输入 -> 移动 -> 失败条件 -> 计分 -> 重启。只有核心循环正常运行后,才能添加视觉、音频或游戏手感增强效果。初始范围应保持精简:1 个场景/关卡、1 种机制、1 个失败条件。
-
事件驱动通信:模块之间绝不通过 import 相互通信。所有跨模块消息都通过单例 EventBus 传递,并使用预定义的事件常量。
-
集中式状态:使用单个 GameState 单例保存所有游戏状态。系统直接读取状态,并通过事件修改状态。状态不得分散在各模块中。
-
集中式配置:所有 magic number、平衡数值、资源路径、生成点和时间参数都应放入。游戏逻辑文件中不得包含任何硬编码值。
Constants.js -
Orchestrator 模式:由一个类初始化所有系统、管理游戏流程(boot -> gameplay -> death/win -> restart),并运行主循环。各个系统不得自行初始化。默认不使用标题画面——启动后直接进入游戏。只有当用户明确要求时,才添加标题/菜单场景。
Game.js -
可安全重启且行为确定:游戏流程必须能够干净地经历完整的重启周期。应恢复完全干净的状态。所有事件监听器都必须在 cleanup/shutdown 阶段移除。重启后不得残留过时引用、残留计时器、泄漏的补间动画或孤立物理刚体。连续重启 3 次进行测试——第三次运行的表现必须与第一次完全一致。
GameState.reset() -
关注点清晰分离:代码按功能层组织:
- - 基础层(Game、EventBus、GameState、Constants)
core/ - - 引擎级系统(输入、物理、音频、粒子)
systems/ - - 游戏机制(玩家、敌人、武器、计分)
gameplay/ - - 世界构建(关卡搭建、资源加载)
level/ - - 界面(菜单、HUD、叠加层)
ui/
Event System Design
事件系统设计
Event Naming Convention
事件命名约定
Use format grouped by feature area:
domain:actionjs
export const Events = {
// Player
PLAYER_DAMAGED: 'player:damaged',
PLAYER_HEALED: 'player:healed',
PLAYER_DIED: 'player:died',
// Enemy
ENEMY_SPAWNED: 'enemy:spawned',
ENEMY_KILLED: 'enemy:killed',
// Game flow
GAME_STARTED: 'game:started',
GAME_PAUSED: 'game:paused',
GAME_OVER: 'game:over',
// System
ASSETS_LOADED: 'assets:loaded',
LOADING_PROGRESS: 'loading:progress'
};使用 格式,并按功能领域分组:
domain:actionjs
export const Events = {
// Player
PLAYER_DAMAGED: 'player:damaged',
PLAYER_HEALED: 'player:healed',
PLAYER_DIED: 'player:died',
// Enemy
ENEMY_SPAWNED: 'enemy:spawned',
ENEMY_KILLED: 'enemy:killed',
// Game flow
GAME_STARTED: 'game:started',
GAME_PAUSED: 'game:paused',
GAME_OVER: 'game:over',
// System
ASSETS_LOADED: 'assets:loaded',
LOADING_PROGRESS: 'loading:progress'
};Event Data Contracts
事件数据契约
Always pass structured data objects, never primitives:
js
// Good
eventBus.emit(Events.PLAYER_DAMAGED, { amount: 10, source: 'enemy', damageType: 'melee' });
// Bad
eventBus.emit(Events.PLAYER_DAMAGED, 10);始终传递结构化数据对象,绝不传递原始值:
js
// Good
eventBus.emit(Events.PLAYER_DAMAGED, { amount: 10, source: 'enemy', damageType: 'melee' });
// Bad
eventBus.emit(Events.PLAYER_DAMAGED, 10);State Management
状态管理
GameState Structure
GameState 结构
Organize state into clear domains:
js
class GameState {
constructor() {
this.player = { health, maxHealth, speed, inventory, buffs };
this.combat = { killCount, waveNumber, score };
this.game = { started, paused, isPlaying };
}
}将状态划分为清晰的领域:
js
class GameState {
constructor() {
this.player = { health, maxHealth, speed, inventory, buffs };
this.combat = { killCount, waveNumber, score };
this.game = { started, paused, isPlaying };
}
}Game Flow
游戏流程
Standard flow for both 2D and 3D games:
Boot/Load -> Gameplay <-> Pause Menu (if requested)
-> Game Over -> Gameplay (restart)No title screen by default. Games boot directly into gameplay. The Play.fun widget handles score display, leaderboards, and wallet connect in a deadzone at the top of the game, so no in-game score HUD is needed. Only add a title/menu scene if the user explicitly requests one.
2D 和 3D 游戏的标准流程:
Boot/Load -> Gameplay <-> Pause Menu (if requested)
-> Game Over -> Gameplay (restart)默认不使用标题画面。 游戏启动后直接进入 gameplay。Play.fun widget 会在游戏顶部的死区处理分数显示、排行榜和钱包连接,因此不需要游戏内计分 HUD。只有当用户明确要求时,才添加标题/菜单场景。
Common Architecture Pitfalls
常见架构陷阱
- Unwired physics bodies — Creating a static physics body (e.g., ground, wall) without wiring it to other bodies via or
physics.add.collider()has no gameplay effect. Every boundary or obstacle needs explicit collision wiring to the entities it should interact with. After creating any static body, immediately add the collider call.physics.add.overlap() - Interactive elements blocked by overlapping display objects — When building UI (buttons, menus), the topmost display object in the scene list receives pointer events. Never hide the interactive element behind a decorative layer. Either make the visual element itself interactive, or ensure nothing is rendered on top of the hit area.
- Polish before gameplay — Adding particles, screen shake, and transitions before the core loop works is a common time sink. Get input -> action -> fail condition -> scoring -> restart working first. Everything else is polish.
- No cleanup on restart — Forgetting to remove event listeners, destroy timers, and dispose resources in causes ghost behavior, double-firing events, and memory leaks after restart.
shutdown()
- 物理刚体未正确连接 — 创建静态物理刚体(例如地面、墙壁)后,如果未通过 或
physics.add.collider()将其连接至其他刚体,就不会产生任何游戏效果。每个边界或障碍物都需要与对应实体显式建立碰撞连接。创建任何静态刚体后,应立即添加 collider 调用。physics.add.overlap() - 重叠显示对象阻挡交互元素 — 构建 UI(按钮、菜单)时,场景列表中最上层的显示对象会接收指针事件。绝不要将交互元素隐藏在装饰层下方。可以将视觉元素本身设为可交互,或者确保点击区域上方没有任何渲染内容。
- 在 gameplay 之前进行润色 — 在核心循环尚未运行前添加粒子、屏幕震动和转场,是常见的时间浪费。应先确保输入 -> 操作 -> 失败条件 -> 计分 -> 重启能够正常运行,其他内容均属于润色。
- 重启时未清理资源 — 如果忘记在 中移除事件监听器、销毁计时器并释放资源,重启后会出现异常行为、事件重复触发和内存泄漏。
shutdown()
Pre-Ship Validation Checklist
发布前验证清单
Before considering a game complete, verify all items:
- Core loop — Player can start, play, lose/win, and see the result
- Restart — Works cleanly 3x in a row with identical behavior
- Mobile input — Touch/tap/swipe/gyro works; 44px minimum tap targets
- Desktop input — Keyboard + mouse works
- Responsive — Canvas resizes correctly on window resize
- Constants — Zero hardcoded magic numbers in game logic
- EventBus — No direct cross-module imports for communication
- Cleanup — All listeners removed in shutdown, resources disposed
- Mute toggle — See rule
mute-button - Delta-based — All movement uses delta time, not frame count
- Build — succeeds with no errors
npm run build - No errors — No uncaught exceptions or console errors at runtime
在认为游戏完成之前,验证以下所有项目:
- 核心循环 — 玩家可以开始游戏、进行游戏、失败/获胜并查看结果
- 重启 — 连续干净重启 3 次,且每次行为完全一致
- 移动端输入 — 触摸/点按/滑动/陀螺仪正常工作;点击目标至少为 44px
- 桌面端输入 — 键盘和鼠标正常工作
- 响应式 — 窗口调整大小时,Canvas 能正确缩放
- Constants — 游戏逻辑中不存在任何硬编码 magic number
- EventBus — 跨模块通信不依赖直接 import
- 清理 — shutdown 时移除所有监听器并释放资源
- 静音切换 — 参见 规则
mute-button - 基于 Delta — 所有移动都使用 delta time,而不是帧数
- 构建 — 成功执行且没有错误
npm run build - 无错误 — 运行时不存在未捕获异常或控制台错误