# 游戏架构设计模式
前人总结的架构智慧——站在巨人肩膀上设计
前置知识:第01章 系统设计方法论 + 第07章 游戏系统设计实战
阅读指南(初学者必看)
为什么你需要学习游戏架构设计模式?
第01章学了设计模式的基础,本章是设计模式在游戏架构中的深度应用。ECS、对象池、行为树、状态机、空间分区——这些不是理论,是游戏工业界每天都在用的架构模式。
学完本章,你能回答:
- ECS 和传统 OOP 有什么本质区别?什么时候该用 ECS?
- 状态机和行为树怎么选?2~5个状态用状态机,复杂AI用行为树?
- 空间分区算法怎么选?四叉树、八叉树、空间哈希、BVH各适合什么场景?
本文结构
第一部分:ECS(Entity Component System)——组合优于继承
第二部分:对象池与命令模式——性能与回放
第三部分:状态机与行为树——AI 行为设计
第四部分:空间分区——空间查询优化
一、ECS(Entity Component System)
生活类比:ECS 就像"自助餐"。传统面向对象是"套餐"(固定搭配),ECS 是"自选"(自由组合)。
传统 OOP:
class Monster extends Character {
int hp;
int attack;
void update() { ai(); move(); render(); }
}
ECS:
Entity = 只是一个 ID
Component = 纯数据(HealthComponent, AttackComponent, PositionComponent)
System = 纯逻辑(AISystem, MoveSystem, RenderSystem)
优势:
1. 组合优于继承 ── 冰冻怪物?加个 FrozenComponent 就行
2. 数据导向 ── Component 连续存储,缓存友好
3. 并行友好 ── System 之间独立,可以并行执行
4. 热重载 ── 修改 System 不影响 Entity
劣势:
1. 学习曲线陡
2. 简单场景反而更复杂
3. 调试困难(Entity 只是个 ID)
ECS完整实现
// World:管理所有Entity和Component
class World {
private nextEntityId: number = 1;
private entities: Set<Entity> = new Set();
private components: Map<string, Map<Entity, any>> = new Map();
private systems: System[] = [];
createEntity(): Entity {
const entity = this.nextEntityId++;
this.entities.add(entity);
return entity;
}
destroyEntity(entity: Entity) {
this.entities.delete(entity);
for (const componentMap of this.components.values()) {
componentMap.delete(entity);
}
}
addComponent<T>(entity: Entity, componentName: string, component: T) {
if (!this.components.has(componentName)) {
this.components.set(componentName, new Map());
}
this.components.get(componentName)!.set(entity, component);
}
getComponent<T>(entity: Entity, componentName: string): T | undefined {
const componentMap = this.components.get(componentName);
return componentMap?.get(entity);
}
hasComponent(entity: Entity, componentName: string): boolean {
const componentMap = this.components.get(componentName);
return componentMap?.has(entity) || false;
}
addSystem(system: System) {
this.systems.push(system);
}
update(dt: number) {
for (const system of this.systems) {
system.update(this, dt);
}
}
query(...componentNames: string[]): Entity[] {
const result: Entity[] = [];
for (const entity of this.entities) {
let hasAll = true;
for (const name of componentNames) {
if (!this.hasComponent(entity, name)) {
hasAll = false;
break;
}
}
if (hasAll) result.push(entity);
}
return result;
}
}
abstract class System {
abstract update(world: World, dt: number): void;
}
ECS应用:子弹系统
interface Bullet { damage: number; ownerId: Entity; }
interface Lifetime { remaining: number; }
class BulletSystem extends System {
update(world: World, dt: number) {
// 移动子弹
const bullets = world.query('Bullet', 'Position', 'Velocity');
for (const bullet of bullets) {
const pos = world.getComponent<Position>(bullet, 'Position')!;
const vel = world.getComponent<Velocity>(bullet, 'Velocity')!;
pos.x += vel.vx * dt;
pos.y += vel.vy * dt;
}
// 检测碰撞
for (const bullet of bullets) {
const bulletComp = world.getComponent<Bullet>(bullet, 'Bullet')!;
const pos = world.getComponent<Position>(bullet, 'Position')!;
const targets = world.query('Health', 'Position');
for (const target of targets) {
if (target === bulletComp.ownerId) continue;
const targetPos = world.getComponent<Position>(target, 'Position')!;
const dist = Math.sqrt((pos.x - targetPos.x) ** 2 + (pos.y - targetPos.y) ** 2);
if (dist < 10) {
const health = world.getComponent<Health>(target, 'Health')!;
health.current -= bulletComp.damage;
world.destroyEntity(bullet);
break;
}
}
}
}
}
二、对象池与命令模式
对象池
应用:子弹、粒子、怪物复用
类比:自行车租赁站,还了再借
优势:
- 减少GC压力
- 减少内存分配开销
- 创建/销毁高频对象时性能提升明显
命令模式
应用:技能系统、回放系统
类比:遥控器按钮,按了执行命令
优势:
- 操作可记录(用于回放)
- 操作可撤销(用于悔棋)
- 操作可队列(用于技能连招)
三、状态机与行为树
状态机(FSM)── 适合简单AI
角色状态:待机 → 移动 → 攻击 → 死亡
触发条件:发现敌人/到达目标/HP归零
层次状态机(HFSM):
"战斗"状态内部嵌套"追击/攻击/逃跑"子状态
比纯状态机灵活,比行为树简单
行为树(BT)── 适合复杂AI
已在其他项目中详解
选择指南:
- 2~5个状态的简单AI → 状态机
- 需要灵活组合的复杂AI → 行为树
- 需要长期规划的AI → GOAP/HTN
四、空间分区
空间分区算法选择:
| 算法 | 2D/3D | 动态/静态 | 适用场景 |
|------|-------|----------|---------|
| 四叉树 | 2D | 静态 | 地图碰撞 |
| 八叉树 | 3D | 静态 | 3D场景管理 |
| 空间哈希 | 2D/3D | 动态 | 角色碰撞 |
| BVH | 2D/3D | 动态 | 射线检测 |
| 网格 | 2D | 静态 | 瓦片地图 |
详见 4_2_game-math-physics 项目
自问自答
Q:ECS 适合所有游戏吗? A:不是。ECS 适合实体多、组合多、需要高性能的场景(成千上万个子弹、粒子)。如果游戏实体少(棋牌、AVG),传统 OOP 更简单直观。不要为了用 ECS 而用 ECS。
Q:对象池和 ECS 的 Component 怎么配合? A:Component 本身就是纯数据,非常适合用对象池管理。比如子弹的 Component(Position + Velocity + Damage),用完还回池子,新子弹从池子里取。ECS + 对象池是游戏开发中经典的性能优化组合。
Q:四叉树和空间哈希哪个好? A:看场景。四叉树适合静态/半静态场景(地图、障碍物),查询效率高但更新成本高;空间哈希适合动态场景(大量角色移动),更新快但查询效率取决于哈希函数。游戏中的角色碰撞用空间哈希更多。
Q:状态机可以嵌套吗? A:可以,这叫层次状态机(HFSM)。比如"战斗"状态内部嵌套"追击/攻击/逃跑"子状态。HFSM 是状态机和行为树之间的折中方案——比纯状态机灵活,比行为树简单。
实践任务
- 任务1:用 ECS 架构实现一个简单的游戏(至少3种 Entity、5种 Component、3种 System)
- 任务2:用传统 OOP 实现同样的游戏,对比两种架构的代码组织和扩展性
- 任务3:实现一个层次状态机(HFSM),管理角色的待机/移动/战斗/逃跑状态
- 任务4:实现空间哈希,对比暴力遍历 O(n²) 的碰撞检测性能
- 任务5:在 ECS 中集成对象池,实现子弹的创建/销毁/复用,对比 GC 表现
与其他章节的关联
| 本章内容 | 关联章节 | 关联点 |
|---|---|---|
| ECS | 第01章 系统设计方法论 | 第01章的设计模式是 ECS 的理论基础 |
| 状态机 | 第07章 游戏系统设计实战 | 战斗系统的 TurnManager 可以用状态机实现 |
| 行为树 | 第07章 游戏系统设计实战 | 战斗系统的 AI 行为用行为树设计 |
| 空间分区 | 4_2_game-math-physics | 碰撞检测和寻路是空间分区的两大应用 |
| 对象池 | 第03章 帧同步Lockstep | 帧同步的输入缓冲可以用对象池管理 |
上一章:09-游戏运营与商业化 下一章:11-LuaCSharp脚本与热更新