共计 2883 个字符,预计需要花费 8 分钟才能阅读完成。
为什么我们需要关注 skill 机制的设计?
想象你正在开发一款 MOBA 游戏,每个英雄有 4 个技能:

- 技能 1:立即造成伤害
- 技能 2:持续治疗效果
- 技能 3:位移并留下残影
- 技能 4:全屏控制效果
或者你正在编写自动化运维工具,需要实现:
- 定时扩容云服务器
- 批量回滚部署
- 故障自动转移
这些场景都需要一个能灵活扩展的 skill 机制。新手常犯的错误是写成这样:
// 反面教材:硬编码技能逻辑
class Hero {useSkill1() {// 200 行具体实现}
useSkill2() {// 又一个 200 行}
}
策略模式:实现多态技能的核心
通过策略模式,我们把每个技能抽象为独立类:
- 定义技能接口
interface ISkill {execute(caster: Character, target?: Character): void;
cooldown: number;
}
- 实现具体技能(以治疗术为例)
class HealSkill implements ISkill {
cooldown = 5000; // 5 秒冷却
/**
* 对目标施加治疗效果
* @param potency 治疗强度系数
*/
constructor(private potency: number) {}
execute(caster: Character, target: Character) {
const healValue = caster.magicAttack * this.potency;
target.receiveHealing(healValue);
// 触发治疗特效 UI
EventBus.emit('SKILL_CAST', {
type: 'HEAL',
amount: healValue
});
}
}
事件驱动架构:解耦技能触发
使用事件总线处理技能交互:
class EventBus {private static listeners: Record<string, Function[]> = {};
static on(event: string, callback: Function) {if (!this.listeners[event]) {this.listeners[event] = [];}
this.listeners[event].push(callback);
}
static emit(event: string, payload?: any) {(this.listeners[event] || []).forEach(fn => fn(payload));
}
}
// 技能释放监听示例
EventBus.on('SKILL_CAST', (data) => {BattleLogger.log(`${data.type} 技能生效,数值:${data.amount}`);
});
装饰器实现冷却系统
通过高阶函数包装技能执行逻辑:
function CooldownDecorator(skill: ISkill): ISkill {
let lastUsed = 0;
return {
...skill,
execute(...args) {const now = Date.now();
if (now - lastUsed < skill.cooldown) {throw new Error('技能冷却中');
}
skill.execute(...args);
lastUsed = now;
}
};
}
// 使用装饰器
const fireball = CooldownDecorator(new DamageSkill(150));
完整 TypeScript 实现示例
abstract class SkillBase implements ISkill {
abstract cooldown: number;
abstract effects: SkillEffect[];
execute(caster: Character, targets: Character[]) {this.validateTargets(targets);
// 应用所有效果
this.effects.forEach(effect => {
targets.forEach(target => {effect.apply(caster, target);
});
});
}
private validateTargets(targets: Character[]) {if (targets.some(t => !t.isAlive)) {throw new Error('无法对死亡目标施放技能');
}
}
}
class PoisonSkill extends SkillBase {
cooldown = 8000;
effects = [new DamageOverTimeEffect(5, 200), // 每秒 5 点伤害,持续 200 秒
new SpeedReductionEffect(0.3) // 减速 30%
];
}
生产环境必须考虑的三大问题
1. 竞态条件处理
当多个技能同时修改同一状态时:
// 使用 Redux-style reducer 处理状态变更
function characterReducer(state: CharacterState, action: SkillAction) {switch (action.type) {
case 'DAMAGE':
return {...state, hp: state.hp - action.payload};
case 'HEAL':
return {
...state,
hp: Math.min(state.maxHp, state.hp + action.payload)
};
// 其他技能效果...
}
}
2. 效果撤销实现
为每个效果设计逆向操作:
interface ReversibleEffect {apply(): void;
revert(): void; // 新增撤销方法
expiration: number;
}
class BuffEffect implements ReversibleEffect {expiration = Date.now() + 10000;
apply(target: Character) {target.addBuff(this);
}
revert(target: Character) {target.removeBuff(this);
}
}
3. 持久化边界条件
处理存档时的特殊场景:
// 序列化时排除临时状态
class SkillManager {toJSON() {
return {
skills: this.skills,
// 不保存冷却中的技能实例
cooldowns: this.cooldowns.map(c => ({
skillId: c.skillId,
remaining: c.remaining
}))
};
}
}
留给读者的思考题
- 当需要热更新技能效果时,如何设计版本兼容机制?
- 如何实现技能组合技(Combo)的判定系统?
- 在 MMO 场景下,怎样优化数百玩家同时释放技能的网络同步?
写在最后
实现一个健壮的 skill 系统就像搭积木,关键在于找到合适的抽象层次。当你的技能类只需要关注效果本身,而不必担心冷却、目标选择等外围逻辑时,就离优雅的设计不远了。建议从简单的伤害类技能开始实践,逐步加入更复杂的控制效果,你会明显感受到这种架构的扩展优势。
正文完
发表至: 编程开发
近两天内
