3.战斗:技能和buff实现方案

技能&buff

  • 1、主动技&被动技
    • 技能:实现主动技
    • Buff :实现被动技
  • 2、事件通用性
  • 3、事件的搜索
    • 搜索的方式
    • 搜索的规则
    • 搜索的优先级
    • 其它搜索附加条件
      • 朝向相关
      • 搜索的目标筛选配置
  • 4、事件的处理对象
  • 5、状态
  • 6、伤害
  • 7. 循环触发
  • 8. 待优化项

1、主动技&被动技

先说一下技能的整体设计框架:
技能是基础配置+多个阶段。
buff是基础配置+多个触发时机。
阶段和触发方式下,都支持配置多个事件。

技能:实现主动技

- 技能基础配置
    - 阶段1
    - 阶段基础配置
        - 事件1(例:给别人加buff事件)
        - 事件2(例:给自己加buff事件)
    - 阶段2
    - 阶段基础配置
        - 事件1(例:治疗事件)
        - 事件2(例:伤害事件)

Buff :实现被动技

- buff基础配置
1、当攻击时
	- 治疗事件
	- 伤害事件
2、当被击时
	- 治疗事件
	- 被动位移事件
3、当暴击时
	- 治疗事件
	- 主动位移事件
4、当被暴击时
5、当命中时
6、当被命中时
7、当出生时
8、当死亡时
9、当眩晕时
10、当被眩晕时
... ...
  • 以上都是buff中事件的触发时机,buff有一个比较重要的触发时机:周期性执行。即每隔n毫秒去执行某个配置事件,因为比较常用,所以抽取到buff基础配置中。
  • 一个被动技可能会设计多个效果。比如有个技能设计如下:在击杀了一名单位后,会投骰随机获得1到6的额外赏金。在死亡时,会扣除100到300金币。那么该buff需要为每个触发时机添加一个cd时间。buff中有一个map记录各个时机的触发时间戳。

buff配置格式如下:

- buff基础配置
    - 当被攻击时
        - 伤害事件
    - 周期性触发
        - 恢复事件

2、事件通用性

技能&buff,是上层两个不同的调用入口。里面真正实现效果的,被封装为事件。
技能&buff仅仅是框架,提供安装、触发、拆卸事件的功能。
事件对于技能&buff是通用的。

    服务器端处理事件:
    1、伤害事件
    2、给自身加buff
    3、给别人加buff
    4、治疗事件
    5、位移事件
    6、嘲讽事件
    7、召唤NPC事件
    8、执行lua表达式事件
    9、复活事件
     
    客户端处理事件:
    1.角色动画
    2.警示语言
    3.警示特效
    4.屏幕特效
    5.播放特效
    6.子弹
    7.播放声音
    8.相机抖动
    9.相机播放animation
    10.武器动画
    11.读条
    12.动画控制
    13.连接特效
    14.拖尾特效
    15.客户端表达式
调用
调用
需要通用对象
技能
事件
Buff
releaser,owner,tar

releaser:技能或buff的释放者
owner:技能实体或buff的拥有者
tar:技能或buff的目标

3、事件的搜索

搜索的方式

1、扇形
2、圆形
3、长方形
4、环形
5、单体

搜索的规则

取出玩家在地图上所处的格子id,计算出玩家位置,搜索视野范围内指定距离的玩家。
    注意1:不同的视野方案,会有不同的搜索处理。(这里举例九宫格视野:以玩家为中心,在附近九个视野格子内,搜索出所有单位)
    注意2:这是第一次搜索,直接用圆形搜索,只判断视野内的单位是否在圆形范围内。(因为扇形、矩形、环形都可以从圆形结果中做剔除)

搜索的优先级

举个例子,技能配置的目标数量:5。
1、搜索到所有满足条件的单位,然后按距离排序,返回前5个
    假设附近九宫格内有20个单位,遍历后有8个单位满足距离条件,12个单位在距离范围之外。那么排序这8个单位,返回前5个单位作为此次搜索的结果。
   (一般有距离排序需求的技能是:嘲讽、治疗。如果你放一个范围嘲讽技,但是离你最近的敌方单位没有被嘲讽,反而距离远的单位被嘲讽到,表现上会有些奇怪。)
2、搜索到满足条件的指定数量对象后,结束搜索并返回搜索结果列表
    假设附近有20个单位,搜索出5个满足条件的单位,就提前退出结束搜索。(比如12345单位正好满足条件,那么就不再处理第6个单位)
3、搜索到所有满足条件的单位后,从可选列表中,随机返回指定数量。

其它搜索附加条件

1.排除受击目标
2.残血优先
3.某某类型优先(比如优先英雄,没英雄时,小兵也是目标)

朝向相关

1、朝向
2、角度
3、弧度

搜索的目标筛选配置

在SLG项目中,由于1个玩家会创建多个部队,所以需要区分[本部队]和[己方其它部队]。

enum ETarRela
{
    ETR_NONE 	= 0   ,	// 无
    ETR_SELF 	= 1<<0,	//本部队
    ETR_OWN  	= 1<<1,	//己方部队
    ETR_FRIEND 	= 1<<2,	//友方部队
    ETR_NEUTRAL = 1<<3,	//中立部队
    ETR_ENEMY 	= 1<<4,	//敌方部队
}

4、事件的处理对象

3.战斗:技能和buff实现方案_第1张图片
如上图所示:玩家A释放了一个傀儡B,玩家C攻击了傀儡B,傀儡B正在攻击玩家D

此时设计不同的技能,举例设计以下4种技能,该技能都被赋予A持有:
1、释放一个傀儡,当傀儡被攻击,回复自身100血量。
   对傀儡B来说,其实拥有了一个被动技能。当它被攻击时,会给释放者A回血。一般做法是:给傀儡B加一个被动buff。
  (这个被动buff需要记录释放者A。并且将玩家A传递给恢复事件,在恢复事件中拿到玩家A的对象,然后执行加血接口)

2、释放一个傀儡,当傀儡被攻击,回复自身100血量,傀儡会对攻击者造成100的反击伤害
    技能2的回血功能和技能1一样实现。另外技能2的被动buff还有一个功能,会对攻击者造成反击伤害。
    所以这个被动buff需要配置:
        a.当受击时,触发治疗事件。(治疗事件传入releaser)
        b.当受击时,触发伤害事件。(伤害事件传入cre)
  (这个被动buff需要记录释放者A、攻击者玩家C。并且将玩家A传递给恢复事件,玩家C传递给伤害事件)

3、释放一个傀儡,当傀儡被攻击,回复自身100血量,傀儡会对攻击者造成100的反击伤害,并且傀儡启动自毁装置,化作一道流光对傀儡当前攻击的目标造成减速效果
(需要记录释放者A、当前产生攻击行为的两方:玩家C和傀儡B、傀儡B的攻击目标:玩家D)

4、释放一个傀儡,当傀儡被攻击,回复自身100血量,傀儡会对攻击者造成100的反击伤害,并且傀儡自爆,在傀儡周围选取2个目标,对其造成眩晕
(需要记录释放者A、当前产生攻击行为的两方:玩家C和傀儡B、傀儡执行一次搜索,搜索出E、F)

在程序数据结构设计上:
    玩家A:被记为释放者,命名releaser
    傀儡B:被记为拥有者,命名owner
    玩家C:被记为互动方,命名cre
    玩家D:被记为当前目标,命名curTar

所以伤害事件中,设计上需要有这4种单位,才能满足上述的技能设计:releaser、owner、cre、curTar

5、状态

状态是一个可叠加的数据,比如buff-1给玩家A添加了2s时长的眩晕,buff-2给玩家A添加了10s的眩晕。当buff-1的时长到了以后,此时不能直接将眩晕的状态清除掉,而是将眩晕状态的层数减1。当10s的时长到了以后,再减去眩晕的层数,此时层数为0,会清掉眩晕状态。

这里也一直有个有个遗留问题:不支持相同buffId叠加

int state[32];

6、伤害

伤害分担
伤害递减

伤害事件是一个比较特殊的事件,会附带位移、self加buff、other加buff等行为。
并且伤害事件是唯一一个,需要附加当伤害事件后,执行xx事件的事件。就是事件中还会嵌套事件,其它事件没有这么复杂,理论上也可以嵌套。

p16的技能都是在服务端由ai释放的。

a. 在释放时是直接push到技能模块的队列中,接下来就是在tick中执行队列中的技能。

p11的技能是由玩家释放的,客户端上传UseSkill的消息到服务器。

a.服务器会判断技能是否瞬发类型,如果是瞬发类,那么流程类似t16会直接进技能模块的队列。
b.但如果不是瞬发类,而是持续类的话,则会先进生物体object的action队列

7. 循环触发

被动技一般使用buff来实现。
比如被动技石化,效果是:当受击时,增加10点防御值。一般是给玩家挂个buff,在处理玩家受击的逻辑处,遍历身上所有buff,如果某个buff有配置触发节点当受击时,那么执行这个节点下面挂载的事件列表。比如石化这里是AttrAdd(self, “defense.value”, 10)。

void BuffOnBeAttack(MapUnit* attacker)
{
    for(auto& buff : mBuffs)
    {
        if(!buff->eventOnBeAttack){continue;}  // 如果没有配置当受击时,无需处理;
        buff->OnBeAttack(attacker);
    }
}

由于节点和事件,都是交给策划配置的。那么可能会出现一些循环触发的问题,需要程序在代码上,设计机制来避免。

1. 当攻击时,下面挂一个伤害事件。(自我循环)
	[当攻击时,触发伤害事件] >> [伤害事件执行后,触发当攻击时] >> [当攻击时,触发伤害事件]。。。
2. 当受击时,下面挂一个伤害事件。(双方循环)
	[A攻击B以后,触发B的当受击时,执行伤害事件] >> [B的伤害事件打到A,触发A的当受击时,执行伤害事件] 
	>> [A又产生伤害事件,打到B]。。。
3. 不同技能间的组合循环(组合循环)
    技能1:当攻击时,治疗自身,回复100血量
	技能2:当治疗时,攻击1次敌方
4. 当我暴击率是100%的时候(条件循环),设计了下面这种技能
	技能3:当暴击时,攻击1次敌方
	那么我1次攻击,触发暴击后 >> 当暴击时,攻击敌方,此次攻击又触发暴击 >> 当暴击时,攻击1次敌方。。。

解决方案是所有被动技的触发入口只封装1个函数。
在函数开始处,声明一个static set gNodes。每次处理节点前,检查当前节点是否已被加入set。如果find到了,说明会造成循环。如果没有find到,那么加入节点到set中,处理节点下挂载的事件。处理完以后,再从set中移除节点。

8. 待优化项

  1. buff计数其实是一个常见的功能,但之前的框架里并没有。

你可能感兴趣的:(01,-,项目,游戏开发)