Unity3D RPG Core | 22 设置兽人士兵
这节我们设置新敌人兽人士兵的相关内容。
1. 设置兽人士兵动画控制器和数据
上一节敌人乌龟的动画控制器是使用复用动画控制器来制作的。兽人士兵与它逻辑不同,因为我们想加一个推开角色人物的功能。因此这边我们复制一份史莱姆的动画控制器,在其基础上进行修改。
我们需要使用兽人的动画替换之前的每一个动画(没有对应的动画可以使用 Idle 动画代替)。如图 2 所示,我们将推开人物功能当成兽人技能触发,对应触发变量 Skill,其对应动画 Attack02 动画。同时注意将之前的暴击转移逻辑去掉。
1.1 创建 ScriptableObject 文件
同样兽人士兵也需要创建属性和攻击的 ScriptableObject 文件。
攻击数据如图 2 所示,技能距离设置短一点,当人物靠近穿过兽人的时候,会触发技能将人物推开。
2. 新的控制脚本
我们新建兽人对应的控制脚本 GruntController,使它继承于 EnemyController 类:
- public class GruntController : EnemyController
注意删掉自动生成的生命周期空函数,否则会覆盖父类原有的函数。
接着我们把 GruntController 拖拽到兽人对象上,设置好 Nav Mesh Agent 参数、指定属性 ScriptableObject 文件、指定攻击 ScriptableObject 文件。此时运行游戏,可以看到兽人士兵逻辑已经基本完成了。
2.1 实现推开功能
推开的功能和受伤的实现一样,放在动画帧事件里触发实现。首先我们先将基础攻击的帧绑定到之前的 Hit() 函数上。
推开的事件绑定到另一帧上,绑定事件为 KickOff() 函数。
KickOff() 函数如代码清单 1 所示,首先使兽人朝向角色,接着计算推出方向。使用 NavMeshAgent.velocity 将人物移动,距离大小使用 m_kickForce 变量控制。
注意角色人物的 Stopping Distance 不要设置为 0,否则推开会无效。原因未知。
补上技能触发的逻辑,如代码清单 2 所示,我们以技能触发为优先。技能这块逻辑还不是很清楚,日后再做完善。此时运行程序,可以验证推开功能已经实现。
2.2 实现推开眩晕
我们利用上角色素材包里的眩晕动画。如图 3 所示,我们将眩晕动画拖拽到角色人物的动画控制器中,并使用变量 Dizzy 触发。
同样在 KickOff() 函数中触发角色人物眩晕。
- public class GruntController : EnemyController
- {
- public float m_kickForce = 20;
- public void KickOff()
- {
- if (m_playerObj != null)
- {
- transform.LookAt(m_playerObj.transform);
- Vector3 direction = m_playerObj.transform.position - transform.position;
- direction.Normalize();
- NavMeshAgent agent = m_playerObj.GetComponent<NavMeshAgent>();
- agent.isStopped = true;
- agent.velocity = m_kickForce * direction;
- m_playerObj.GetComponent<Animator>().SetTrigger("Dizzy");
- }
- }
- }
3. 一些问题
运行过程中遇到一些问题。
3.1 推开后无法无法攻击
推开后直接点人物会无法攻击。KickOff() 函数中会将 agent 停止,在攻击事件中需要将 agent 恢复。
3.2 眩晕、受伤等过程还能移动
人物在眩晕或者受伤动画过程中,还能点地板取消移动,这点不太合理。我们通过指定动画行为脚本来修复。如图 4 所示,我们可以在相应动画节点界面上点击 Add Behaviour 添加一个脚本,命名为 StopAgent。
打开创建的脚本文件。如代码清单 3 所示,里面对应了动画执行的各个阶段。animator 参数就是当前执行挂载的 Animator 组件,我们可以通过它找到 NavMeshAgent 组件。
动画开始时(OnStateEnter),我们使 NavMeshAgent 停止;结束时(OnStateExit),恢复 NavMeshAgent。因为点地板时会恢复 NavMeshAgent,所以我们需要在动画执行过程中(OnStateUpdate)反复停止 NavMeshAgent。
我们在角色人物的眩晕和受伤动画上加上 StopAgent 脚本。目前兽人攻击时,如果人物移动,兽人也会漂移着攻击,所以也将兽人的两个攻击动画加上 StopAgent 脚本。
3.3 StopAgent 脚本带来的问题
StopAgent 脚本会带来一个问题:因为动画期间一直使用 NavMeshAgent 组件,而在敌人死亡前的一段时间,我们会关闭 NavMeshAgent,这就会带来以下报错。
- "Stop" can only be called on an active agent that has been placed on a NavMesh.
- UnityEngine.StackTraceUtility:ExtractStackTrace ()
- StopAgent:OnStateUpdate (UnityEngine.Animator,UnityEngine.AnimatorStateInfo,int) (at Assets/Scripts/AnimatorBehaviour/StopAgent.cs:17)
当时关闭 NavMeshAgent 的原因是怕死亡时模型还挡着道。如果不关闭,设置 NavMeshAgent.radius 为零,同样能达到这个效果,同时还能修复上面的报错。
- public class EnemyController : MonoBehaviour, IEndGameObserver
- {
- void SwitchState()
- {
- case EnemyStates.DEATH:
- m_collider.enabled = false;
- //m_navMeshAgent.enabled = false;
- m_navMeshAgent.radius = 0;
- Destroy(gameObject, 2.0f);
- break;
- }
- }
- }
修复掉现有发现的问题后,最终的效果如图 5 所示。