设计模式 (Design Patterns) 是解决常见架构问题的通用方案。但在游戏开发中,性能敏感性和迭代速度要求我们对经典模式进行“魔改”。
本文档总结了在 Project Vampirefall 及业界 3A/独立游戏中最高频使用的模式。
Instance 静态变量会导致紧耦合,且难以测试。AudioManager.Instance.Play()。ServiceLocator.Register<IAudioService>(new AudioManager())。ServiceLocator.Get<IAudioService>().Play()。GameManager, SaveSystem, LootManager。UnityEngine.Pool API。OnGet 和 OnRelease。取出对象时,必须重置其状态(血量、Buff、位置),否则会出现“刚出生的怪带着半血”的 Bug。Alive 状态下包含 Move, Skill;Dead 状态下包含 Ragdoll, Dissolve。switch-case。每个状态应该是一个独立的类 (IState)。GlobalEvents.OnEnemyKilled?.Invoke(enemyInfo)。unit.MoveTo(pos)。new MoveCommand(unit, pos) 并压入队列。假设你需要实现:A(火焰)、B(冰冻)、C(吸血)。
如果用继承:FireAttack, IceAttack, FireAndIceAttack…
组合爆炸 (Combinatorial Explosion): 10 种效果需要 $2^{10} = 1024$ 个类。
装饰器模式创建的是“包装纸”。
IAttackSwordAttack (最里面的娃娃)FireDecorator (包在外面)调用链: Game -> LifeSteal -> Fire -> Sword
Execute(),不知道里面包了多少层。// 1. 接口
public interface IAttack {
void Execute(Enemy target, DamageInfo info);
}
// 2. 本体
public class SwordAttack : IAttack {
public void Execute(Enemy target, DamageInfo info) {
target.TakeDamage(info.BaseDamage); // 核心逻辑
}
}
// 3. 装饰器基类
public abstract class AttackDecorator : IAttack {
protected IAttack _inner;
public AttackDecorator(IAttack inner) { _inner = inner; }
public virtual void Execute(Enemy target, DamageInfo info) {
_inner.Execute(target, info); // 转发
}
}
// 4. 具体装饰器:拦截与修改
public class CritDecorator : AttackDecorator {
public CritDecorator(IAttack inner) : base(inner) {}
public override void Execute(Enemy target, DamageInfo info) {
// 拦截输入:在攻击发生前修改参数
if (Random.value < 0.2f) info.BaseDamage *= 2.0f;
base.Execute(target, info);
}
}
OnHit)。适合做 UI 显示、成就统计。标准的 GoF 装饰器在面对复杂 RPG 系统时有三大缺陷:
(10+5)*1.5 vs 先乘后加 (10*1.5)+5,结果截然不同。解决方案:Modifier Pipeline (中间件模式) 不再使用层层包裹,而是维护一个有序列表。
public enum ModifierPriority {
BaseStats = 100, // 基础数值 (+10)
Multiplier = 200, // 乘法修正 (+50%)
Conversion = 300, // 属性转化 (物理转火)
OnHit = 400 // 击中特效
}
public class AttackPipeline {
private List<IAttackModifier> _modifiers = new List<IAttackModifier>();
public void AddModifier(IAttackModifier mod) {
// 1. 处理互斥 (Conflict Policy)
var existing = _modifiers.Find(m => m.GroupID == mod.GroupID);
if (existing != null && mod.Policy == ConflictPolicy.Override) {
_modifiers.Remove(existing);
}
// 2. 添加并排序
_modifiers.Add(mod);
_modifiers.Sort((a, b) => a.Priority.CompareTo(b.Priority));
}
public void Execute(AttackContext ctx) {
foreach (var mod in _modifiers) {
mod.OnAttack(ctx);
if (ctx.IsConsumed) break; // 支持中断 (如被格挡)
}
}
}
struct 存储。MonsterData.asset (ScriptableObject)。PlayerController 写了 3000 行代码,包含输入、移动、动画、音效。解法: 使用组件模式 (Component),拆分为 PlayerInput, PlayerMover, PlayerAnimator。