狭义来讲,技能就是几个人作战时,可以对他人使用产生一定效果的操作。魔兽世界对技能定义进行了扩展,即在魔兽世界中,产生一定效果的任何操作都是技能。例如 吃面包,使用物品,采矿,训练商业技能 等等。
魔兽世界中技能可以产生一个立即的效果(例如 扣血,扣篮,挖到一个矿),或者是一个持续的状态(buff),或者两者兼而有之。立即效果处理起来比较简单这里就不细说了,这里主要说说程序中buff怎样处理。
魔兽世界中buff简直是千变万化,估计没有人能完全的了解所有的buff的效果。这其中有很多种分类,例如
类型 |
例子 |
影响数值型 |
奥术智慧,邪甲术 |
控制型 |
变羊术 恐惧 |
持续伤害型 |
腐蚀术 |
被动触发型 |
荆棘术 |
加强效果型 |
法师的冲击天赋 |
...... |
...... |
很显然,暴雪的开发人员不可能一个一个技能去编写。那不仅难以控制代码,策划也无法脱离程序员去实现技能。其实我们根据破解魔兽世界的客户端,可以看到,魔兽的技能是一个一个配置出来的!程序员只需要做好基本的效果,之后技能就交给策划去配置了。
咋一看,buff种类太多,看上去让人头大。其实可以分解出一个共有的特点:buff是由三个部分组成的: 1. 时间 2. 条件 3. 动作。
我们分析一下上面5种类型的buff这三个特点:
技能 |
时间 |
条件 |
动作 |
奥术智慧 |
加减buff时 |
本次加的buff |
加减智力数值 |
变羊术 |
加减buff时 |
本次加的buff |
变形,减buff 还原 |
腐蚀术 |
该buff时间间隔到的时候 |
扣血 |
|
荆棘术 |
被打时 |
1.近战攻击 2.命中 |
对攻击敌人释放攻击技能 |
法师的冲击天赋 |
打中时 |
1.火焰魔法 2.命中 3. %2 几率roll成功 |
对目标释放一个晕技能 |
一个动作可以是具体效果,也可以是释放一个技能
上面几个技能触发时机如下图所示:
我们可以发现,在整个技能流程中buff作用效果可以穿插在一些时间点。所以我们可以这样设计配置文件:
技能id |
时间点 |
条件 |
动作 |
奥术智慧id |
1,2 |
5 |
1,2 |
变羊id |
1,2 |
5 |
3,4 |
腐蚀术id |
3 |
5 |
|
荆棘术id |
4 |
1 and 2 |
6 |
冲击id |
5 |
3 and 2 and 4 |
6 |
这里给出具体含义:
时间点:
1 加buff时
2 减buff时
3 buff tick到时
4 被打时
5 打人时
条件:
1 技能是近战
2 技能命中
3 技能是火焰魔法
4 几率roll成功
5 本次技能施放的buff
动作
1 加智力
2 减智力
3 变形
4 变形还原
5 扣血
6 释放一个技能
时间点的使用
服务器端怎么使用这些配置呢?很简单,服务器上提供一个钩子列表即可,配置文件中,时间点就是钩子的挂载点
vector<boost::signals2::signal>
比如荆棘术buff加上时,根据配置注册到事件的vector相应位置,下标就是时间点
一旦有人被攻击,就会执行下标为4 的所有事件
一旦攻击 就会执行,下标为5的所有事件
条件的扩展
魔兽世界中,buff触发的条件是极其复杂的。例如上面冲击天赋触发条件涉及到三个小条件。这还是比较简单,条件与条件之间只有and关系。如果有or关系怎么办?我们可以用一棵行为树解决这个问题,行为树可以实现and和or的关系。所以完全可以让程序员开发策划需要的condition节点,策划使用编辑器编辑行为树即可。行为树在此就不细说了,大家可以找google看看
再说天赋成就和LOG系统
看到这里,大家也明白了成就系统无非也是三点1. 时间 2. 条件 3. 动作
比如坐骑成就:
成就id |
时间点 |
条件 |
动作 |
收集50个坐骑 |
收到一个坐骑时 |
有50个坐骑 |
增加一个坐骑大师的称号 |
LOG也是一样:
Log |
时间点 |
条件 |
动作 |
记录一次转移金币大于1000G的LOG |
扣钱 |
数量> 1000 |
记录log |
魔兽世界天赋就是一些隐藏的永久buff
顺便给出一个简单实现:
https://github.com/egametang/Egametang/tree/master/Cpp/Game/BehaviorTree