前言

攻速机制是Moba游戏中非常重要的机制之一,他会和状态机制,Buff机制产生联系,但是网上却少有详细介绍攻速机制并实现的文章,故有此文。

名词定义

因为为了描述清楚攻速机制需要有一些专有名词,为了避免歧义,本文中的名词及其相关释义如下所示(下面提到的所有概念均为我们逻辑层概念,和动画没有任何关系)

  • 基本攻速:也即基础攻击间隔(BAT)的倒数,即两次攻击之间的间隔时长,比如一个英雄BAT为0.5,即0.5s攻击一次,那么它的基本攻速就是2,大部分英雄基本攻速不一样

  • 攻速收益:对于攻速加成的实际转换比例,比如一双攻速鞋+35%攻速,如果一个英雄攻速收益是50%,那么他真正获得的额外攻速就是17.5%,大部分攻速收益不一样

  • 额外攻速:是指通过Buff,装备,技能,等级成长获得的攻速,计算公式为:额外攻速 = 额外攻速加成 * 攻速收益

  • 总攻速:计算公式为:最终攻速 = 基本攻速 + 额外攻速

  • 基础攻击前摇:攻击打出伤害之前的时间段,在此时间段中对攻击指令进行主动/被动取消都将终止此次攻击,并且没有伤害,大部分英雄基础攻击前摇不一样

  • 攻击前摇:计算公式:1+\frac{\mathrm{基础攻击前摇}}{1+\mathrm{额外攻速}}

  • 攻击奏效帧:攻击奏效的那一帧

  • 基础攻击后摇:攻击奏效之后的时间段,因为我们伤害已经打出,目的已达到,可进行取消去执行其他指令。例如寻路,释放技能等,大部分英雄攻击后摇不一样

  • 攻击后摇:计算公式:1+\frac{\mathrm{基础攻击后摇}}{1+\mathrm{额外攻速}}

机制理解

单论攻速和攻击前后摇都好理解,但再把他们两个和动画放在一起谈的话,可能就有些混乱了,我们来理一理

首先我们明确一点,攻击前摇时长不依赖于动画播放,而是动画播放依赖于攻击前摇,下文攻击后摇同理

我们先做出如下假设

  1. 假设我们英雄此时基础攻速为0.5(2s攻击一次),额外攻速为0,攻速收益为1(如果购买一双攻速鞋会直接+35%攻速)
  2. 假设基础前摇为0.3s,基础后摇0.3s
  3. 假设攻击动画长度为1s,攻击奏效帧处于第0.3s处(在整个动画片段30%刻度的地方),剩下的0.7s都可以作为后摇的动画片段

众所周知,在前期攻速很慢的情况下,我们的英雄会出现在攻击后罚站的情况,下面来复现这一情况

那么此时我们对一名敌人发起攻击,它的时间线会是这样的

0.0s:发起攻击,有0.3s的前摇以及0.3s的后摇,然后是1.4s的空闲时间(可以是继续播放后摇动画,如果后摇动画播放完成,就会罚站)

0.3s:我们需要保证动画的攻击奏效帧和我们逻辑上的攻击奏效帧一致,幸运的是动画攻击奏效帧恰巧是0.3s处,所以不需要改变动画速度,剩下的0.7s作为后摇动画片段

0.6s:逻辑上的后摇结束了

1.0s:动画上的后摇结束了

1.0s~2.0s:罚站阶段

2.0s~2.3s:下一次攻击前摇

然后我们看看需要改变动画速度的例子

买几件攻速装备来增加我们英雄的额外攻速,这里假设买了50%的攻速加成装备,会加0.5的额外攻速,此时我们攻击前摇为0.31+0.5=0.2\frac{0.3}{1+0.5}=0.2,攻击后摇为0.31+0.5=0.2\frac{0.3}{1+0.5}=0.2,总攻速为1.0,即1s攻击一次

此时我们对一名敌人发起攻击,会有0.2s的前摇以及0.2s的后摇,然后是0.6s的空闲时间

那么此时我们对一名敌人发起攻击,它的时间线会是这样的

0.0s:发起攻击,有0.2s的前摇以及0.2s的后摇,然后是0.6s的空闲时间

0.2s:我们需要保证动画的攻击奏效帧和我们逻辑上的攻击奏效帧一致,但是我们动画需要0.3s才能到达它的攻击奏效帧,所以我们要加速动画使其经过0.2s就播放到动画的攻击奏效帧,所以有此通用公式   =  \mathrm{缩放比例}\;=\;\frac{\mathrm{逻辑攻击奏效帧时间点}}{\mathrm{动画攻击奏效帧时间点}}

  =  1\mathrm{动画速度}\;=\;\frac{1}{\mathrm{缩放比例}}

所以我们这里的缩放比例就是0.20.3  =  0.67\frac{0.2}{0.3}\;=\;0.67,整个动画的长度就变成0.67s,前0.2s为前摇动画,后0.47s为后摇动画,动画播放速度调整为1.5倍

0.4s:逻辑上的后摇结束了

0.67s:动画上的后摇结束了

0.67s~1.0s:罚站阶段

1.0s~1.2s:下一次攻击前摇

减少额外攻速同理

代码设计思路

先从宏观上看,我们应当把普通攻击抽象成一个FSM中的状态,这样方便与各种其他状态(例如寻路,释放技能,眩晕,禁锢,缴械等)进行交互和通信

有一个MouseTargetSelectorComponent来提供给我们当前鼠标选中的对象,有一个CommonAttackComponent来处理具体的攻击逻辑,还有一个CommonAttackState来控制攻击状态切换

具体来看就是,整个攻击流程将基于await/async语法来实现,这样做的好处是避免了Unity经典协程的传统艺能

每一次的普通攻击指令都要先去获取攻击速度,再决定此次攻击各个阶段的时长,非常经典的例子就是狼人白嫖被动的攻速去平A(这里懂得自然懂,不懂的说了也不懂)

image-20210907003938686

更加具体的代码就不贴了,因为涉及到客户端与服务端的交互,代码量较多,有兴趣的可以去

https://gitee.com/NKG_admin/NKGMobaBasedOnET查看相关代码

参考

https://dota2-zh.gamepedia.com/%E6%94%BB%E5%87%BB%E9%80%9F%E5%BA%A6

https://dota2-zh.gamepedia.com/%E6%94%BB%E5%87%BB%E5%8A%A8%E4%BD%9C