共计 2036 个字符,预计需要花费 6 分钟才能阅读完成。
传统实现方式的痛点
在开发 MOBA 英雄的技能系统时,新手程序员往往会写出这样的代码:

void Update() {if (Input.GetKeyDown(KeyCode.Q) && !isCoolingDown) {if (comboStep == 0) {StartCoroutine(PlayQ1());
comboStep = 1;
} else if (comboStep == 1 && Time.time - lastQTime < 1.0f) {StartCoroutine(PlayQ2());
comboStep = 0;
}
}
// 更多 if-else...
}
这种写法会带来三个典型问题:
- 状态判断与效果实现强耦合,新增技能要修改核心逻辑
- 连招计时、冷却判断等通用逻辑重复编写
- 技能打断等异常处理代码分散在各处
设计模式选型对比
状态模式(State Pattern)
最适合处理技能生命周期:
- 准备阶段(前摇)
- 生效阶段(伤害判定)
- 收招阶段(后摇)
- 冷却阶段
命令模式(Command Pattern)
解决技能效果执行问题:
- 支持效果撤销(如回放系统)
- 实现复合技能(组合按键触发)
策略模式(Strategy Pattern)
适用于可替换的技能效果算法,但不如命令模式灵活
核心架构实现
状态机基类设计
classDiagram
class ISkillState {
<<interface>>
+Enter()
+Update()
+Exit()}
class SkillStateMachine {
-currentState : ISkillState
+ChangeState()}
class ReadyState {+Enter()
+Update()}
ISkillState <|-- ReadyState
ISkillState <|-- CastingState
ISkillState <|-- CooldownState
SkillStateMachine o-- ISkillState
效果组合示例(C#)
// 定义技能效果接口
public interface ISkillEffect {void Execute(GameObject target);
}
// 具体效果实现
public class DamageEffect : ISkillEffect {
public int DamageValue;
public void Execute(GameObject target) {target.GetComponent<Health>().TakeDamage(DamageValue);
}
}
// 效果组合器
public class CompositeEffect : ISkillEffect {private List<ISkillEffect> _effects = new();
public void AddEffect(ISkillEffect effect) {_effects.Add(effect);
}
public void Execute(GameObject target) {foreach (var effect in _effects) {effect.Execute(target);
}
}
}
事件总线解耦
// 定义技能事件
public class SkillEvent {
public const string CAST_START = "CAST_START";
public const string HIT_TARGET = "HIT_TARGET";
}
// 触发示例
EventBus.Publish(SkillEvent.HIT_TARGET, new {
skillId = 1001,
target = enemy
});
// 监听示例
EventBus.Subscribe(SkillEvent.HIT_TARGET, (data) => {// 处理命中逻辑});
性能优化关键点
预加载策略
- 在角色加载时预实例化常用技能模板
- 使用对象池管理粒子特效
- 异步加载大型技能资源
GC 优化技巧
- 避免在 Update 中 new 对象
- 重用 List 等集合容器
- 使用结构体代替类存储瞬发数据
生产环境注意事项
网络同步方案
// 客户端预测 + 服务器校验
void ClientCastSkill(int skillId) {
// 本地立即表现
PlayLocalEffect();
// 发送给服务器
Network.Send("CAST_SKILL", skillId);
}
// 服务器补偿
void ServerVerify(int frame, SkillData data) {if (IsValid(frame, data)) {BroadcastToAllClients(data);
} else {
// 回滚客户端状态
SendRollback(clientId);
}
}
优先级仲裁规则
- 硬直中的技能不可打断
- 高优先级技能可以打断低优先级后摇
- 相同优先级先进先出
延伸思考
如何实现以下热更新需求:
- 运行时修改技能伤害公式
- 动态调整连招触发间隔
- 不重启游戏更新技能特效
可以考虑的方案:
- 使用 ScriptableObject 存储配置
- 实现配置文件的版本比对机制
- 设计安全的资源热更管道
正文完
