🏗️ 游戏设计模式:理论与 Unity 实战
设计模式 (Design Patterns) 是解决常见架构问题的通用方案。但在游戏开发中,性能敏感性和迭代速度要求我们对经典模式进行“魔改”。 本文档总结了在 Project Vampirefall 及业界 3A/独立游戏中最高频使用的模式。1. 核心模式 (The Essentials)
1.1 单例模式 (Singleton) & 服务定位器 (Service Locator)
- 理论: 保证一个类只有一个实例,并提供全局访问点。
- Unity 痛点: 传统的
Instance静态变量会导致紧耦合,且难以测试。 - 最佳实践: 服务定位器 (Service Locator) 或 依赖注入 (DI)。
- 不要直接调用
AudioManager.Instance.Play()。 - 而是让系统在启动时注册:
ServiceLocator.Register<IAudioService>(new AudioManager())。 - 业务逻辑只依赖接口:
ServiceLocator.Get<IAudioService>().Play()。
- 不要直接调用
- Vampirefall 应用:
GameManager,SaveSystem,LootManager。
1.2 对象池模式 (Object Pool)
- 理论: 预先实例化一组对象,用完回收而非销毁,避免 GC (垃圾回收) 造成的卡顿。
- Unity 实战:
- 使用 Unity 2021+ 内置的
UnityEngine.PoolAPI。 - 脏数据清理 (Dirty State): 核心难点在于
OnGet和OnRelease。取出对象时,必须重置其状态(血量、Buff、位置),否则会出现“刚出生的怪带着半血”的 Bug。
- 使用 Unity 2021+ 内置的
- Vampirefall 应用: 子弹、伤害数字、怪物模型。
1.3 状态机模式 (State Machine / FSM)
- 理论: 对象处于有限的几种状态之一 (Idle, Walk, Attack),且根据条件在状态间切换。
- 实战:
- 分层状态机 (HFSM): 状态套状态。
Alive状态下包含Move,Skill;Dead状态下包含Ragdoll,Dissolve。 - 代码即数据: 不要用巨大的
switch-case。每个状态应该是一个独立的类 (IState)。
- 分层状态机 (HFSM): 状态套状态。
- Vampirefall 应用: 怪物 AI、UI 窗口流转、防御塔行为。
2. 进阶架构模式 (Advanced Architecture)
2.1 观察者模式 (Observer / Event Bus)
- 理论: 定义一对多的依赖关系,当一个对象改变状态时,所有依赖者都会收到通知。
- 实战: 事件总线 (Event Bus)。
GlobalEvents.OnEnemyKilled?.Invoke(enemyInfo)。- 成就系统订阅它:“杀敌数+1”。
- 任务系统订阅它:“任务进度更新”。
- UI 系统订阅它:“飘字显示”。
- 优点: 彻底解耦。杀怪逻辑不需要知道成就系统的存在。
2.2 命令模式 (Command)
- 理论: 将“请求”封装成对象。
- 实战: RTS 和 回合制游戏的核心。
- 不要直接
unit.MoveTo(pos)。 - 而是创建
new MoveCommand(unit, pos)并压入队列。
- 不要直接
- Vampirefall 应用:
- 输入缓冲 (Input Buffer): 在攻击动作未结束时按下闪避,指令被存入 Command Queue,动作结束后立即执行。
- 录像回放: 记录一局内所有的 Command,即可完美重播整局游戏。
2.3 装饰器模式 (Decorator) - Deep Dive
- 核心价值: 动态地、透明地、无限叠加地改变对象的行为,而无需修改原始类。
A. 为什么“继承”是死胡同?
假设你需要实现:A(火焰)、B(冰冻)、C(吸血)。 如果用继承:FireAttack, IceAttack, FireAndIceAttack…
组合爆炸 (Combinatorial Explosion): 10 种效果需要 个类。
B. 像俄罗斯套娃一样 (The “Russian Doll” Model)
装饰器模式创建的是“包装纸”。-
Component (接口):
IAttack -
Concrete (本体):
SwordAttack(最里面的娃娃) -
Decorator (包装):
FireDecorator(包在外面)
Game -> LifeSteal -> Fire -> Sword
- 外层只管调
Execute(),不知道里面包了多少层。
C. Vampirefall 实战代码 (C#)
D. 装饰器 vs 事件 (Event)
- 事件 (Event): 通常发生在动作之后 (
OnHit)。适合做 UI 显示、成就统计。 - 装饰器: 可以介入动作之前,修改参数甚至阻止执行。适合做暴击计算、格挡判定。
E. 工业级痛点与进化:管道模式 (Pipeline)
标准的 GoF 装饰器在面对复杂 RPG 系统时有三大缺陷:- 移除困难: 想要移除中间的一层包装(如 Buff 过期),需要解构整个链条。
-
顺序混乱: 先加后乘
(10+5)*1.5vs 先乘后加(10*1.5)+5,结果截然不同。 - 互斥管理: 如何实现“武器附魔只能有一个”?
3. 性能特化模式 (Performance Oriented)
3.1 数据局部性 (Data Locality / ECS)
- 理论: CPU 缓存行 (Cache Line) 极其敏感。处理连续内存的数据比处理随机指针快几十倍。
- 实战: Unity DOTS (Data-Oriented Technology Stack)。
- Struct vs Class: 核心战斗数据全部用
struct存储。 - Array vs List: 使用定长数组或 NativeArray。
- Struct vs Class: 核心战斗数据全部用
- Vampirefall 应用: 500+ 同屏怪物的移动与碰撞计算。
3.2 享元模式 (Flyweight)
- 理论: 运用共享技术有效地支持大量细粒度的对象。
- 实战: ScriptableObject。
- 1000 个哥布林,不需要存 1000 份“攻击力=5,名字=Goblin”。
- 它们共享同一个
MonsterData.asset(ScriptableObject)。 - 每个实例只存变化的数据(当前血量、位置)。
4. 设计模式陷阱 (Anti-Patterns)
- 过度单例 (Singleton Abuse): 导致代码变成一团乱麻,哪里都能改数据,无法追踪 Bug 来源。
-
上帝类 (God Class): 一个
PlayerController写了 3000 行代码,包含输入、移动、动画、音效。解法: 使用组件模式 (Component),拆分为PlayerInput,PlayerMover,PlayerAnimator。 - 过度设计 (Over-Engineering): 为还没出现的需求写复杂的接口。YAGNI (You Aren’t Gonna Need It) 原则是王道。
📚 扩展阅读与代码圣经 (References)
🏛️ 设计模式基础
- Game Programming Patterns (Bob Nystrom)
- 必读圣经。免费在线阅读。重点推荐 Type Object (对应 ScriptableObject) 和 Data Locality (对应 DOTS) 章节。
- Refactoring.Guru
- 图文并茂地讲解了 GoF 23 种经典模式。如果你忘了“装饰器模式”怎么写,来这查。
🏎️ 性能与架构
- Unity ECS (Entity Component System) Documentation
- 官方 DOTS 文档。理解数据导向编程 (Data-Oriented Design) 的权威来源。
- Dependency Injection in Unity (Infallible Code)
- 讲解为什么你应该使用 DI (依赖注入) 来替代 Singleton,从而写出更干净、可测试的代码。
🔧 实战案例
- Command Pattern in Strategy Games
- 详解如何利用命令模式实现 RTS 游戏中的“输入缓冲”和“回放系统”。