├── LOL技能实现.txt └── 回合制战斗的技能实现.txt /LOL技能实现.txt: -------------------------------------------------------------------------------- 1 | ARPG类的游戏相对于之前的回合制游戏最大的区别就是技能激活时间的不确定性,但是依然可以可以套用以前的循环链条 2 | 3 | 角色播放动作,关键帧时执行逻辑 4 | 技能特效创建后,被激活时,执行逻辑 5 | 6 | 这里角色的逻辑和技能的逻辑都包括 7 | 1.在某个位置创建特效 8 | 2.造成伤害 9 | 3.上buff/debuff 10 | 4.造成卡牌动作(主动卡/被动卡组) 11 | 12 | 角色动作和特效触发均可以触发下一步逻辑,从而形成一个链表似的逻辑链条,每一块逻辑触发都会执行下一个逻辑块 13 | 14 | 例如:某飞弹,命中目标后爆炸为10个子飞弹向四周发射,全部过程为 15 | 16 | 1.角色动作,关键帧之后在身前释放飞弹 17 | 2.飞弹按照特效配置速度向前方飞去 18 | 3.命中目标,执行命中逻辑, 19 | 4.在目标位置创建10个子飞弹,飞弹方向分别指向四周 20 | 5.技能结束 21 | 22 | 特效动作模式 23 | 1.创建位置 24 | 2.飞行轨迹 25 | 3.飞行速度 26 | 27 | 28 | 相对于之前的回合制技能,这类特效最大的特点是内含逻辑判断,所以,技能逻辑必要含有以下属性 29 | 1.isActive --是否被激活,不激活的技能无法与其他的角色/特效发生碰撞,只是一个展示效果。可通过碰撞回调或主角激活此逻辑 30 | 2.maxColNum --最大碰撞个数,此技能特效最多会碰撞多少个目标,填 -1 则是无上限碰撞 31 | 3.hitCB --命中回调,命中后的逻辑,制造眩晕或者其他 32 | 4.tarType --目标类型 是敌方?我方?另外的技能特效?(于我方敌方发生碰撞) 33 | 5.进入碰撞区逻辑 --部分持续性时间较长的技能需要此类逻辑 例如,阿卡丽的雾,进入的时候把阿卡丽隐身 onEnter 34 | 6.离开碰撞区逻辑 --例如,阿卡丽的雾,离开时,把阿卡丽解除隐身 onExit 35 | 7.碰撞区更新时间 --例如,假设某角色扔出一个毒瓶,在一定时间内创造了一个范围的毒雾,每隔1秒触发一次,则此处填写一个激活时间 36 | 8.特效本体移除时间 37 | ..... 38 | 每个技能的特效和逻辑使用策略模式进行结合,这样就可以配置出多样的技能 39 | 40 | 41 | 盲僧技能实现:天音波/回音击 42 | 天音波:李青发出刺耳的声波定位敌人,对首个敌人造成物理伤害。如果天音波击中敌人,李青在接下来3秒可施放回音击。 43 | 回音击:李青冲向被天音波击中的敌人,造成物理伤害并附加敌人损失生命值8%的伤害。 44 | 45 | 46 | 盲僧的技能可以算是两段式技能,即第二个技能满足条件后再次释放会出第一个技能,可以用单例技能管理类中的旗标来判断当前要释放的是哪个技能 47 | 因为同场竞技时,只会有一个盲僧,所以此类旗标可以使用 阵营ID + 角色ID + 技能ID 作为key值来取得当前技能的旗标 48 | 49 | 天音波的技能描述很简单 50 | 1.角色播动作 51 | 2.沿指定方向放出飞弹特效 -- 特效碰撞一个目标后处理逻辑 (命中特效,旗标设置为2号技能) 52 | 53 | 回音击的技能描述更简单 54 | 1.冲向目标 --角色动作 55 | 2.播放攻击动画 --角色动作 56 | 3.处理逻辑 57 | 58 | 以上的方式 59 | 60 | --- 61 | 多加一句,逻辑判断及伤害,可使用model类来处理此类技能逻辑,实现逻辑与效果的解耦 62 | 有时间写一个战斗model的实现,这里就先假设逻辑判断的部分已经和展示部分分离了即可 63 | 64 | 65 | 德莱文/旋转飞斧 66 | 1.角色施法动作 67 | 2.创建特效(飞斧,目标为敌人) 68 | 3.飞斧碰撞后,触发逻辑 69 | 4.逻辑创建后,创建反向飞斧,目标为德莱文 70 | 5.命中德莱文后,逻辑 技能清除CD 71 | 72 | 73 | 辛德拉 74 | 辛德拉的球 75 | -------------------------------------------------------------------------------- /回合制战斗的技能实现.txt: -------------------------------------------------------------------------------- 1 | 回合制战斗的技能实现 2 | 3 | 我们游戏的逻辑部分是放在服务器端进行处理的,实际上前端所处理的仅仅是一个表现效果,可以说只是一个播放器。但实际上完全可以在本地加上一个逻辑模块,将全部的逻辑放在本地处理。 4 | 一般来说,逻辑模块处理技能之后,display模块会得到如下数据 5 | 1.技能id 6 | 2.主动角色:战场中的哪个角色释放了技能 (实际上,这个拥有多个角色,实现出类似于合体技的效果,但是我们的项目没有这类的需求,故只处理了一个) 7 | 3.被动角色组:战场中哪些角色被主动角色发动的技能施加了影响 (是伤害?还是治疗?还是增加了一个buff?) 8 | 9 | 10 | 我们先看一下我们第一版的战斗技能实现方式,最原始的版本,这里以近战和远程攻击为例 11 | 1.通过技能id读表获得此技能的类型 (是近战还是远程,还是全体) 12 | 2.卡牌移动到目标前方 13 | 3.卡牌播放攻击动作 14 | 4.卡牌播放动作同时,在卡牌前播放特效 15 | 5.特效的伤害帧播放受击卡牌的受伤动画 16 | 6.卡牌归位,下一轮 17 | 18 | 远程攻击 19 | 1.卡牌播放放波动作 20 | 2.特效从我方卡牌飞向目标卡牌(可能有多个) 21 | 3.特效关键帧触发后,被击卡牌播放受伤动画 22 | 4.卡牌动作归位,下一轮 23 | 24 | 全体攻击同上,只是取敌方阵正中央播放一个全体特效 25 | ...... 26 | 27 | 这个原始版本有如下的几个弊端 28 | 1.每类技能都需要单独了一套实现,类似于飞行时间此类的细节可以通过增加技能参数来实现,但是实现类似于打横排,打竖排之类就需要新增加一种技能类型,big class bang! 29 | 2.即便是同类攻击,也需要处理很多小的细节。 30 | 例如: 31 | 某远程技能需要在释放时,先播放一个特效,然后再将飞弹打出去。 32 | 某近战技能需要在目标卡牌前播放一个蓄力动画再发动攻击。 33 | 某全体技能需要释放者先走到屏幕中央,然后再播技能特效,而另一个技能则是走到敌方阵营的正中央 34 | ...... 35 | 3.修改怎么办?如果某些技能需要改变,策划们会屁颠屁颠地跑过来,很开心得指出你的细节问题,然后各种不相关的人也过来巴拉巴拉巴拉....能tm把人给烦死,这种痛苦大家都懂得 36 | 37 | 以上几个重大缺陷最终迫使我放弃了第一版的战斗实现,虽然当时的战斗复杂程度已经和目标游戏《放开那三国》差不多了,但是实在是可扩展性极差,不得已,推倒原设计转入了第二版。 38 | 39 | 40 | 第二版的设计目标 41 | 经历了第一版的惨痛经历,我为第二版设定了如下的设计目标 42 | 43 | 1.策划可以通过配表,独立实现技能权利 (没事不准来烦我!) 44 | 2.高度抽象,将全部的实现抽象为几个类,避免继续第一版类爆炸 (功能内聚) 45 | 3.针对特性编程,不针对实现编程,不搞特殊化技能的代码实现 (给你工具,自己实现) 46 | 47 | 实际上这几个需求的目标是完全一致的,即,实现一个技能的工具箱,策划通过独立组合卡牌和特效的动态效果,来独立配置技能。先重新分析下第一版的战斗。 48 | 49 | 近战战斗 50 | 51 | 1.通过技能id读表获得此技能的类型 (...) 52 | 2.卡牌移动到目标前方 (卡牌移动了) 53 | 3.卡牌播放攻击动作 (卡牌播放了一个动作) 54 | 4.卡牌播放动作同时,在卡牌前播放特效 (卡牌播放了一个动作,产生了一个特效) 55 | 5.特效的伤害帧播放受击卡牌的受伤动画 (产生了一个特效) 56 | 6.卡牌归位,下一轮 (卡牌移动了,技能结束了) 57 | 58 | 抽象地语义分析一下,这里面有这么几个简单的过程: 59 | 60 | 1.卡牌移动了 61 | 2.卡牌播放动作了 62 | 3.产生了技能特效 63 | 4.技能结束了 64 | 65 | 进一步分析会发现:卡牌移动和卡牌播放动作完全可以合并成同一个,卡牌动作 66 | 我们可以视 67 | 卡牌原地不动播放做动作是卡牌动作的一个特例 68 | 卡牌移动但是不播放动作也是卡牌动作的一个特例 69 | 这样过程就抽象为 70 | 71 | 1.卡牌动作 72 | 2.产生了技能效果 73 | 3.技能结束 74 | 75 | 这三个过程 76 | 这时候再分析远程 77 | 78 | 远程攻击 79 | 1.卡牌播放放波动作 (卡牌动作) 80 | 2.特效从我方卡牌飞向目标卡牌(产生了技能特效,技能特效移动了) 81 | 3.特效关键帧触发后,被击卡牌播放受伤动画 (卡牌动作,产生了一个技能特效) 82 | 4.卡牌动作归位,下一轮 (卡牌动作,技能结束) 83 | 84 | 同上面的分析,我们会发现,技能特效的产生和移动同样可以合并成一个,即技能效果,静止技能是移动技能的一个特例 85 | 86 | 所以最终我们的技能工具箱里只剩下了三个抽象工具 87 | 88 | 1.卡牌动作 (移动和动作) 89 | 2.技能效果 (创建和动作) 90 | 3.技能结束 (技能结束) 91 | 92 | 93 | 也就是说,近战攻击和远程攻击都是由这三个过程组合而成的,之前没有提到的全体技能也可以进行此类分析 94 | 95 | 1.卡牌播放了施法动作 -> 卡牌动作 96 | 2.卡牌上方播放了施法特效 -> 技能效果 97 | 3.目标卡牌组上播放全体特效 -> 技能效果 98 | 4.目标卡牌组上播放受伤特效 -> 技能效果 99 | 5.技能结束 -> 技能结束 100 | 101 | 使用工具箱中的三种描述,是完全有可能实现的! 102 | 103 | 在这里我们假象一种相对复杂的技能: 104 | 主动卡释放了一组飞弹,命中了全体敌人,然后移动到敌方阵营正中央,释放了一个全屏幕特效的全体技能,最后从屏幕的最后方,召唤了一只老鹰(特效),飞到敌人后方且攻击了全部敌人 105 | 106 | 分析如下: 107 | 108 | 1.主动卡施法动作 -> 卡牌动作 109 | 2.释放一组飞弹 -> 技能效果 110 | 3.主动卡移动到敌方阵营正中间 -> 卡牌动作 111 | 4.敌方正中央播放全体技能特效 -> 技能效果 112 | 5.特效触发帧,敌方全体卡牌播放受伤动画 -> 卡牌动作 113 | 6.创建老鹰特效,从我方屏幕后方移动到敌方后方 -> 技能效果 114 | 7.老鹰特效触发帧播放受伤特效 -> 卡牌动作 115 | 8.主动卡归位 -> 卡牌动作 116 | 9.技能结束 -> 技能结束 117 | 118 | 可以看到基本满足了要求,所以我们可以从语义上对回合制游戏的大部分技能进行完全的抽象描述了 119 | 120 | 接下来,就是对下面三个工具的实现描述 121 | 表结构 122 | 依上所述,卡牌动作主要分两部分,一个是卡牌的动画动作,另外一个是卡牌执行的位移。 123 | 卡牌动画动作 124 | 我们使用的是cocostudio实现的动画,每个卡牌的动画都是封装好的,执行某个动作策划只需要将当前卡牌执行的动作名填表即可 125 | 126 | 卡牌的位移 127 | 位移相对麻烦一些,因为我们不可能让策划详细填写坐标吧?但是实际上,卡牌位移可能出现的起始位置是一个有限集,是完全可以预定义的,如下 128 | 1.主动卡原位置 129 | 2.目标卡位置(目标卡一般是个卡组,这个返回的是第一个) 130 | 3.目标卡组列位置 (取第一张卡,然后根据位置算出当前的列位置) 131 | 4.目标卡组行位置 (同上) 132 | 5.屏幕正中央 133 | 6.我方后方 134 | 7.敌方后方 135 | 8.敌方正中央 136 | .... 137 | 以上列举一些可能卡牌位置,具体实现时,可以灵活添加。 138 | 139 | 举一个实际的例子,某个卡牌动作是,从主动卡原位置移动到屏幕正中央(0.5秒),且播放移动动作(walk),策划填卡牌动作表如下,move代表的是起始位置 140 | 141 | cardMove.xlsx 142 | 143 | id | act | move | time |其他 ... 144 | --------------------------- 145 | 1 | walk | [1,5] | 0.5 |其他 ... 146 | *这里的act下填写的是卡牌的动作名称 147 | 148 | 这样当技能的状态机执行到这儿的时候,就知道该如何移动了 149 | 150 | 同理,一个老鹰特效从屏幕后方飞刀敌方中央(0.5),策划填表特效表如下 151 | 152 | effect.xlsx 153 | 154 | id | file | move | time |其他 ... 155 | --------------------------- 156 | 1 | eagle| [6,8] | 0.5 |其他 ... 157 | 158 | *这里的file下填写的使用的动画文件的名字 159 | 160 | 技能状态机依次读取对应的结构然后播放就可以了.... 161 | 等等! 还没有讲调用! 162 | 是的! 163 | 调用也很简单 164 | 165 | cardMove.xlsx 166 | id | act | move | time | callback | ... 167 | -------------------------------------------- 168 | 1 | walk | [1,5] | 0.5 | [effect,1] | 169 | 170 | id | act | move | time | callback | ... 171 | -------------------------------------------- 172 | 2 | walk | [5,1] | 0.5 | [over,1] | 173 | 174 | effect.xlsx 175 | id | file | move | time | callback | 176 | -------------------------------------------- 177 | 1 | eagle| [6,8] | 0.5 | [cardMove,2] | 178 | 179 | 简单的流程就是: 180 | 181 | cardMove[1] -> effect[1] -> cardMove[2] -> over 182 | 183 | 这个的实现方式有点类似于链表,callback指定了技能状态机下一个执行的函数,一直执行到 over 为止,技能状态机开始执行下一个技能。 184 | 185 | 实际上的cardMove表和effect表远比这要复杂,在实际的项目中,我们的callback是一个带延迟时间的数组 如下: 186 | 187 | [[1.5,[cardMove,2]],[1.5,[effect,2]],[1.0,[effect,3]]] 188 | 189 | 这条callback延时调用了三个函数,实现了一个卡牌移动和两个技能特效,而前面的数值则是延迟调用的时间,这种多重调用可以制造天女散花类的效果 190 | 191 | 大家可以考虑下下面这个技能效果如何通过配表实现 192 | 193 | 主动卡发射了一个飞弹,命中了屏幕正中央,引发一次爆炸特效,之后从爆炸特效发射出六个飞弹命中了敌方的六个目标。 194 | 195 | 196 | 197 | 198 | 199 | 200 | 201 | 202 | 203 | --------------------------------------------------------------------------------