回合制战斗的技能实现
我们游戏的逻辑部分是放在服务器端进行处理的,实际上前端所处理的仅仅是一个表现效果,可以说只是一个播放器。但实际上完全可以在本地加上一个逻辑模块,将全部的逻辑放在本地处理。
一般来说,逻辑模块处理技能之后,display模块会得到如下数据
1.技能id
2.主动角色:战场中的哪个角色释放了技能 (实际上,这个拥有多个角色,实现出类似于合体技的效果,但是我们的项目没有这类的需求,故只处理了一个)
3.被动角色组:战场中哪些角色被主动角色发动的技能施加了影响 (是伤害?还是治疗?还是增加了一个buff?)
我们先看一下我们第一版的战斗技能实现方式,最原始的版本,这里以近战和远程攻击为例
1.通过技能id读表获得此技能的类型 (是近战还是远程,还是全体)
2.卡牌移动到目标前方
3.卡牌播放攻击动作
4.卡牌播放动作同时,在卡牌前播放特效
5.特效的伤害帧播放受击卡牌的受伤动画
6.卡牌归位,下一轮
远程攻击
1.卡牌播放放波动作
2.特效从我方卡牌飞向目标卡牌(可能有多个)
3.特效关键帧触发后,被击卡牌播放受伤动画
4.卡牌动作归位,下一轮
全体攻击同上,只是取敌方阵正中央播放一个全体特效
......
这个原始版本有如下的几个弊端
1.每类技能都需要单独了一套实现,类似于飞行时间此类的细节可以通过增加技能参数来实现,但是实现类似于打横排,打竖排之类就需要新增加一种技能类型,big class bang!
2.即便是同类攻击,也需要处理很多小的细节。
例如:
某远程技能需要在释放时,先播放一个特效,然后再将飞弹打出去。
某近战技能需要在目标卡牌前播放一个蓄力动画再发动攻击。
某全体技能需要释放者先走到屏幕中央,然后再播技能特效,而另一个技能则是走到敌方阵营的正中央
......
3.修改怎么办?如果某些技能需要改变,策划们会屁颠屁颠地跑过来,很开心得指出你的细节问题,然后各种不相关的人也过来巴拉巴拉巴拉....能tm把人给烦死,这种痛苦大家都懂得
以上几个重大缺陷最终迫使我放弃了第一版的战斗实现,虽然当时的战斗复杂程度已经和目标游戏《放开那三国》差不多了,但是实在是可扩展性极差,不得已,推倒原设计转入了第二版。
第二版的设计目标
经历了第一版的惨痛经历,我为第二版设定了如下的设计目标
1.策划可以通过配表,独立实现技能权利 (没事不准来烦我!)
2.高度抽象,将全部的实现抽象为几个类,避免继续第一版类爆炸 (功能内聚)
3.针对特性编程,不针对实现编程,不搞特殊化技能的代码实现 (给你工具,自己实现)
实际上这几个需求的目标是完全一致的,即,实现一个技能的工具箱,策划通过独立组合卡牌和特效的动态效果,来独立配置技能。先重新分析下第一版的战斗。
近战战斗
1.通过技能id读表获得此技能的类型 (...)
2.卡牌移动到目标前方 (卡牌移动了)
3.卡牌播放攻击动作 (卡牌播放了一个动作)
4.卡牌播放动作同时,在卡牌前播放特效 (卡牌播放了一个动作,产生了一个特效)
5.特效的伤害帧播放受击卡牌的受伤动画 (产生了一个特效)
6.卡牌归位,下一轮 (卡牌移动了,技能结束了)
抽象地语义分析一下,这里面有这么几个简单的过程:
1.卡牌移动了
2.卡牌播放动作了
3.产生了技能特效
4.技能结束了
进一步分析会发现:卡牌移动和卡牌播放动作完全可以合并成同一个,卡牌动作
我们可以视
卡牌原地不动播放做动作是卡牌动作的一个特例
卡牌移动但是不播放动作也是卡牌动作的一个特例
这样过程就抽象为
1.卡牌动作
2.产生了技能效果
3.技能结束
这三个过程
这时候再分析远程
远程攻击
1.卡牌播放放波动作 (卡牌动作)
2.特效从我方卡牌飞向目标卡牌(产生了技能特效,技能特效移动了)
3.特效关键帧触发后,被击卡牌播放受伤动画 (卡牌动作,产生了一个技能特效)
4.卡牌动作归位,下一轮 (卡牌动作,技能结束)
同上面的分析,我们会发现,技能特效的产生和移动同样可以合并成一个,即技能效果,静止技能是移动技能的一个特例
所以最终我们的技能工具箱里只剩下了三个抽象工具
1.卡牌动作 (移动和动作)
2.技能效果 (创建和动作)
3.技能结束 (技能结束)
也就是说,近战攻击和远程攻击都是由这三个过程组合而成的,之前没有提到的全体技能也可以进行此类分析
1.卡牌播放了施法动作 -> 卡牌动作
2.卡牌上方播放了施法特效 -> 技能效果
3.目标卡牌组上播放全体特效 -> 技能效果
4.目标卡牌组上播放受伤特效 -> 技能效果
5.技能结束 -> 技能结束
使用工具箱中的三种描述,是完全有可能实现的!
在这里我们假象一种相对复杂的技能:
主动卡释放了一组飞弹,命中了全体敌人,然后移动到敌方阵营正中央,释放了一个全屏幕特效的全体技能,最后从屏幕的最后方,召唤了一只老鹰(特效),飞到敌人后方且攻击了全部敌人
分析如下:
1.主动卡施法动作 -> 卡牌动作
2.释放一组飞弹 -> 技能效果
3.主动卡移动到敌方阵营正中间 -> 卡牌动作
4.敌方正中央播放全体技能特效 -> 技能效果
5.特效触发帧,敌方全体卡牌播放受伤动画 -> 卡牌动作
6.创建老鹰特效,从我方屏幕后方移动到敌方后方 -> 技能效果
7.老鹰特效触发帧播放受伤特效 -> 卡牌动作
8.主动卡归位 -> 卡牌动作
9.技能结束 -> 技能结束
可以看到基本满足了要求,所以我们可以从语义上对回合制游戏的大部分技能进行完全的抽象描述了
接下来,就是对下面三个工具的实现描述
表结构
依上所述,卡牌动作主要分两部分,一个是卡牌的动画动作,另外一个是卡牌执行的位移。
卡牌动画动作
我们使用的是cocostudio实现的动画,每个卡牌的动画都是封装好的,执行某个动作策划只需要将当前卡牌执行的动作名填表即可
卡牌的位移
位移相对麻烦一些,因为我们不可能让策划详细填写坐标吧?但是实际上,卡牌位移可能出现的起始位置是一个有限集,是完全可以预定义的,如下
1.主动卡原位置
2.目标卡位置(目标卡一般是个卡组,这个返回的是第一个)
3.目标卡组列位置 (取第一张卡,然后根据位置算出当前的列位置)
4.目标卡组行位置 (同上)
5.屏幕正中央
6.我方后方
7.敌方后方
8.敌方正中央
....
以上列举一些可能卡牌位置,具体实现时,可以灵活添加。
举一个实际的例子,某个卡牌动作是,从主动卡原位置移动到屏幕正中央(0.5秒),且播放移动动作(walk),策划填卡牌动作表如下,move代表的是起始位置
cardMove.xlsx
id | act | move | time |其他 ...
---------------------------
1 | walk | [1,5] | 0.5 |其他 ...
*这里的act下填写的是卡牌的动作名称
这样当技能的状态机执行到这儿的时候,就知道该如何移动了
同理,一个老鹰特效从屏幕后方飞刀敌方中央(0.5),策划填表特效表如下
effect.xlsx
id | file | move | time |其他 ...
---------------------------
1 | eagle| [6,8] | 0.5 |其他 ...
*这里的file下填写的使用的动画文件的名字
技能状态机依次读取对应的结构然后播放就可以了....
等等! 还没有讲调用!
是的!
调用也很简单
cardMove.xlsx
id | act | move | time | callback | ...
--------------------------------------------
1 | walk | [1,5] | 0.5 | [effect,1] |
id | act | move | time | callback | ...
--------------------------------------------
2 | walk | [5,1] | 0.5 | [over,1] |
effect.xlsx
id | file | move | time | callback |
--------------------------------------------
1 | eagle| [6,8] | 0.5 | [cardMove,2] |
简单的流程就是:
cardMove[1] -> effect[1] -> cardMove[2] -> over
这个的实现方式有点类似于链表,callback指定了技能状态机下一个执行的函数,一直执行到 over 为止,技能状态机开始执行下一个技能。
实际上的cardMove表和effect表远比这要复杂,在实际的项目中,我们的callback是一个带延迟时间的数组 如下:
[[1.5,[cardMove,2]],[1.5,[effect,2]],[1.0,[effect,3]]]
这条callback延时调用了三个函数,实现了一个卡牌移动和两个技能特效,而前面的数值则是延迟调用的时间,这种多重调用可以制造天女散花类的效果
大家可以考虑下下面这个技能效果如何通过配表实现
主动卡发射了一个飞弹,命中了屏幕正中央,引发一次爆炸特效,之后从爆炸特效发射出六个飞弹命中了敌方的六个目标。
后期有时间会把示例代码发出来,基于quick cocos2dx的 lua实现。
应某友要求,接下来将会尝试使用此套类似的机制,尝试实现LOL中的部分技能效果,使策划能够自由配置想要的技能效果。