用Unity实现弹反效果

作者:Truly
大渣好。
老贼的黑魂系列,想必大家都乐(tong)在其中。

那么要说到魂系列、乃至很多其他ACT要素浓郁的作品,最核心的机制之一,那就是弹反(视游戏不同叫法也不同,一闪、完美防御、弹开等等)。
实现了这个效果,(理论上)你也可以做一个类似的游戏出来(强调:只是理论上,只是理论上)。
今天我们就用简单粗暴的方法来实现一个弹反。
先看效果图。

图中可知:
持续格挡,受击时有晃动、硬直动作。
完美格挡成功时(会根据攻击方向进行格挡),敌人攻击会被弹开。
当敌人被弹开攻击3次时,会进入后仰慢动作。
这次用的资源是在:https://www.mixamo.com 里免费下的Great Sword Pack和Pro Sword And Shield Pack,需要的可以在工程里边拉或者自行下载。导入资源后,用Plane和Cube随便搭建一个场地。
由于这是一个简单版的弹反功能实现,因此我们只需要制作两个功能残缺的工具人:一个只会左砍右砍的双刀火鸡哥,一个只能跑和防反的大剑姐。
一、双剑角色
1.功能及动画状态机
双剑角色作为被弹反的工具人,免费包里没有演出效果比较好的动画,最终只好用同一个挥砍动画(Slash)通过调节应用在三个状态中(完美符合乞丐版气息):左砍、右砍(镜像)、被弹反(调节播放速度为负),自己有动画的小伙伴请毫不犹豫地自行替换。

2.实现思路:
(1)在每个动画快结束的时候执行动画帧事件,随机下一个Slash(挥刀)状态,激活对应攻击源,用于判定攻击方向。
(2)Slash时用动画帧事件激活武器上的触发器。
(3)被弹反的时候,调节播放速度AnimaSpeed为负,使其回放。
(4)当被弹反3次时触发BehitBack后仰动画。
3.相关设置
(1)角色设置
以下添加的Collider都勾选IsTrigger。
①给角色添加CapsuleCollider,然后调节至跟角色差不多大小。

②添加RigidBody,并勾选IsKinematic。
③展开角色的骨骼,分别给双剑所在节点加上BoxCollier组件,并且调节至跟剑差不多大小,设置Tag为”EWeapon”,用于格挡时碰撞检测。
④在剑的节点下边新建一个空的物体SwordOri并添加Collider组件,设置Tag”为EWeaponOri”,作为攻击源用于给玩家判定攻击方向。

(2)设置动画
因为需要一个后仰的慢动作,把BehitBack动画的Speed调为0.5。

4.部分功能实现
(1)激活武器的Collider
双击Animator窗口里的LSlash,展开Events,拖动动画进度条。
在手刚挥到角色前方的位置,点击Events下“+”,添加动画帧事件(当动画播放到这个位置时会执行对应名称的方法),设置完成后Apply。

由于LSlash和RSlash使用的是同一个动画,这里通过获取当前动画状态的名字的方式来判断应该激活哪边的武器Collider,代码(含注释)如下:
//激活武器Collider
public void OnAnimation_OpenSwordCollider()
{
if (beBlock)
{
return;
}
//获取当前动画信息
aniInfo = animator.GetCurrentAnimatorStateInfo(0);
if (aniInfo.IsName("LSlash")) //激活左剑Collider
{
lSwordCollider.enabled = true;
}
else if (aniInfo.IsName("RSlash")) //激活右剑Collider
{
rSwordCollider.enabled = true;
}
}
同理,在砍动作结束时添加动画帧事件关闭武器的Collider。
(2)被弹反
这次乞丐版的弹反实际上是Slash动画的回放,所以只要播放速度为负即可,为了提升视觉效果,我们通过动画曲线模拟先快后慢的弹开速度。
①设置动画曲线
在脚本中声明一个变量public AnimationCurve animaSpeed,保存后我们可以在Unity中找到这个变量,点击右侧图框便能编辑曲线。

横轴将作为时间,竖轴将作为播放速度(具体参数按自己喜好调节):

②根据动画曲线调节播放速度
打开Animator窗口,添加float类型参数AnimaSpeed。
找到LSlash和RSlash两个动画状态,分别勾选Speed下Multiplier的Parameter并选择AnimSpeed,把动画播放的速度与参数AnimaSpeed建立联系。
通过AnimationCurve.Evaluate(float time)设置AnimaSpeed来调节播放速度。

代码(含注释)如下:
//被格挡时触发动作回弹
public IEnumerator BeBlocked()
{
OnAnimation_CloseWeaponCollier(); //关闭武器碰撞盒
beBlock = true;
beBlockCounter += 1; //被弹反次数+1
timer = 0;
while (timer < 0.8f) //弹反动画播放0.8s
{
SetAnimaSpeed();
yield return new WaitForFixedUpdate();
}
beBlock = false;
OnAnimation_RandomAState(); //随机下一个状态
animator.SetFloat("AnimaSpeed", 1); //播放速度恢复正常
}
//设置格挡回弹时的速度
public void SetAnimaSpeed()
{
timer += Time.fixedDeltaTime; //计时
animaSpeed = animaSpeedCur.Evaluate(timer); //读取曲线数据
animator.SetFloat("AnimaSpeed", animaSpeed); //设置播放速度
}
二、大剑角色
1.功能及动画状态机
大剑角色具有只有格挡(完美格挡、持续格挡、受击)和移动功能。

2.实现思路:
(1)点击右键,进入PerfectBlock状态,根据敌人攻击来方触发对应的格挡动画。
(2)当在PerfectBlock状态下,敌人的武器碰到格挡Collider时会被弹反。
(3)按住右键进入KeepBlocking状态,持续防御。
(4)当在KeepBlocking状态下,敌人的武器碰到格挡Collider时,玩家被击晃动。
3.相关设置
(1)角色设置
以下添加的Collider都勾选IsTrigger。
①给角色添加Character Controller组件,用于实现移动。
②添加BoxCollider,调节大小与位置,使其位于角色前方,用于探测敌人和判定攻击方向。
③展开角色骨骼,在武器节点下新建空的子物体BlockCollider并添加BoxCollider,用于格挡检测,可以适量调大来增加格挡容错率。


(2)动画设置
当只有左方向的格挡动画时,可以通过设置动画状态的Mirror(镜像)属性获得右方向的格挡动画。
拿PerfectBlock作为例子,在Animator添加一个Bool类型参数isRightB,在Inspector窗口如下图设置:勾选Mirror右侧Parameter,并选择参数isRightB,表示当isRightB为true时播放镜像。接下来只需在脚本中控制isRightB的切换就可以控制左右格挡。

剩下的KeepBlock、BlockImpact、BlockEnd状态也这样操作,就可以获得一套右格挡动画。
4.部分功能实现
Block
(1)角色上的代码(含注释)如下:
①敌人攻击时,大剑角色前方的大Collider会检测到敌人的SwordOri。
private void OnTriggerEnter(Collider other)
{
if (other.CompareTag("Enemy")) //获取敌人Transform
{
targetTrans = other.transform;
}
if (other.CompareTag("EWeaponOri")) //检测敌人攻击时激活的攻击源Collider
{
eWeaponOriTrans = other.transform;
}
}
②敌人攻击时会激活SwordOri,通过计算SwordOri与角色右方的夹角,决定格挡方向。

//检测攻击方向
void CheckAttackDir(Vector3 eWeaponOriPos)
{
Vector3 aDir = eWeaponOriPos - transform.position; //从玩家指向敌人攻击源
float angle = Vector3.Angle(transform.right, aDir); //计算aDir与角色右方的夹角
if (angle < 90) //夹角<90度往右格挡
{
isRightB = true;
}
else
{
isRightB = false; //否则往左格挡
}
}
③激活blockCollider进行碰撞监测,并且根据攻击来向播放相应的格挡动画。
//格挡
public void Block()
{
if (isFighting) //如果战斗相关动画在播放,则返回
{
return;
}
if (eWeaponOriTrans) //如果检测到敌人攻击源
{
CheckAttackDir(eWeaponOriTrans.position); //检测敌人攻击方向
}
LookAtTarget(); //转向目标
isBlocking = true;
isPerfectBlock = true;
blockCollider.SetActive(true); //激活格挡Collider
animator.SetTrigger("Block");
}
(2)BlockCollider上的代码(含注释)如下:
在BlockCollider上添加脚本,当碰到敌人的武器时,如果玩家是PerfectBlock状态,使敌人变为BeBlock状态,触发弹反,否则玩家进入格挡受击状态(isBImpact)。
private void OnTriggerEnter(Collider other)
{
if (other.CompareTag("EWeapon"))
{
Enemy enemy = other.GetComponentInParent<Enemy>();
if (!player.isPerfectBlock)
{
player.isBImpact = true; //格挡受击
}
else
{
enemy.eState = Estate.BeBlock; //弹反
}
}
}
结语:实现简单版弹反的主要功能基本都介绍完了,实质上是Collider与Animator的应用,有兴趣的小伙伴可以下载工程看看设置参数或者其他小功能的实现,希望对大家有所启发。
下载链接:https://pan.baidu.com/share/init?surl=5kbKX5XUfI5_ZDtZ0xLDVw
提取码:ok3f
咱们的游戏开发交流群也欢迎强势插入:869551769
希望参与线下游戏开发学习的,欢~~~~~~迎访问:http://www.levelpp.com/