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
如需详细参考,请查看本目录中的配套文件:
- — 对象池、增量时间归一化、资源回收、波次/生成系统、增益/强化系统、触觉反馈、资源管理
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个失败条件。
-
事件驱动通信:模块之间绝不通过互相导入来通信。所有跨模块消息传递都通过单例EventBus完成,并使用预定义的事件常量。
-
集中式状态管理:单个GameState单例存储所有游戏状态。系统直接读取状态,并通过事件修改状态。状态不会分散在各个模块中。
-
配置集中化:所有魔术数字、平衡数值、资源路径、生成点和时间参数都放在中。游戏逻辑文件中不能存在硬编码值。
Constants.js -
编排器模式:一个类负责初始化所有系统、管理游戏流程(启动 -> 游戏运行 -> 死亡/胜利 -> 重启),并运行主循环。系统不会自行初始化。默认不设置标题界面 — 直接启动进入游戏。仅当用户明确要求时,才添加标题/菜单场景。
Game.js -
支持安全重启与确定性:游戏玩法必须能在完整重启循环后正常运行。可恢复到完全干净的初始状态。所有事件监听器都会在清理/关闭阶段移除。重启后不能存在陈旧引用、残留计时器、泄漏的补间动画或孤立的物理体。测试时需连续重启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'
};采用的格式,按功能区域分组:
领域:操作js
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
// 正确写法
eventBus.emit(Events.PLAYER_DAMAGED, { amount: 10, source: 'enemy', damageType: 'melee' });
// 错误写法
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游戏的标准流程:
启动/加载 -> 游戏运行 <-> 暂停菜单(若有需求)
-> 游戏结束 -> 游戏运行(重启)默认不设置标题界面。游戏直接启动进入玩法。Play.fun组件会在游戏顶部的闲置区域处理分数展示、排行榜和钱包连接,因此无需在游戏内添加分数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()与其他物体关联,不会产生任何游戏玩法效果。每个边界或障碍物都需要与它应该交互的实体显式设置碰撞关联。创建任何静态物体后,应立即添加碰撞器调用。physics.add.overlap() - 交互元素被重叠显示对象遮挡 — 构建UI(按钮、菜单)时,场景列表中最顶层的显示对象会接收指针事件。绝不要让交互元素被装饰层遮挡。要么让视觉元素本身可交互,要么确保点击区域上方没有任何渲染内容。
- 先打磨后做玩法 — 在核心循环正常运行前就添加粒子效果、屏幕震动和过渡动画,是常见的时间浪费。先实现输入 -> 动作 -> 失败条件 -> 计分 -> 重启的流程。其他所有内容都属于优化打磨部分。
- 重启时未清理资源 — 在中忘记移除事件监听器、销毁计时器和释放资源,会导致重启后出现幽灵行为、事件重复触发和内存泄漏。
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
- 桌面端输入 — 键盘+鼠标功能正常
- 响应式适配 — 画布在窗口大小改变时能正确调整
- 常量配置 — 游戏逻辑中无硬编码的魔术数字
- EventBus通信 — 无直接跨模块导入用于通信的情况
- 资源清理 — 所有监听器在关闭时已移除,资源已释放
- 静音开关 — 符合规则
mute-button - 基于增量时间 — 所有移动逻辑使用增量时间,而非帧数
- 构建流程 — 执行成功,无错误
npm run build - 运行无错误 — 运行时无未捕获异常或控制台错误