Root motion动画可以将角色的根节点(通常是角色的骨盆或脚部)的运动直接应用到游戏对象上,从而实现角色的自然移动和旋转,避免出现脚底打滑的现象。采用Root motion动画的游戏对象,通常是重载了onAnimatorMove函数,在脚本中来设置动画的速度,从而实现角色的移动。Unity的Navigation系统是一个用于实现游戏世界中的寻路和导航功能的组件。它允许游戏角色在复杂的游戏环境中自动找到从一点到另一点的最短路径。如果我们对采用Root motion动画的游戏对象应用Navigation,就会产生冲突,因为这两个组件都会尝试控制游戏对象的移动。有两个解决方式:
一是让动画跟随Navigation agent,通过获取agent.velocity来设置root motion的速度,从而大致匹配Agent的移动到动画的移动。这个方式最简单,但是可能会出现脚底打滑的现象。
二是让Agent跟随动画,关闭agent的updatePosition和updateRotation,通过计算agent的nextPosition和动画根节点的rootPosition的插值来进行控制。这种方式比方式一要复杂,但是效果更好。以下将以一个游戏场景为例子,详细介绍一下如何实现方式二。
游戏场景
在游戏中,对于NPC角色,当前设置了几个状态,分别是漫游Wander,瞄准Aim以及追踪Chase。NPC刚开始是漫游状态,在场景中自由地进行移动,这时是通过Root motion来驱动的。当NPC检测到玩家时,会进入瞄准状态。如果玩家进行躲避NPC,则NPC会进入追踪状态,自动跑到上一次发现玩家的位置,这时NPC是由Navigation来驱动,实现自动寻路。可见对于NPC是需要按照不同的场景来用Root motion或Navigation来驱动的。
Animator设置
建立一个名为Enemy的Animator,包含了两个状态,分别是Aim和Move,设置如下:
添加两个Trigger,分别为Aim和Walk,用于切换状态。定义一个名为Speed的Float变量,用于控制Root motion的移动速度。
Move状态是一个BlendTree,通过Speed来进行Idle,Walk,Run这三种动作的混合,改变Speed的值,可以看到人物动作的改变。
Unity Blendtree动画
改变Speed的值,可以看到人物的动作的改变。
实现漫游状态
现在给游戏对象增加一个名为EnemyAI的脚本文件,实现游戏对象在场景中漫游。代码如下:
public class EnemyAI : MonoBehaviour
{
[Header("Enemy eyeview")]
public float eyeviewDistance = 500.0f;
public float viewAngle = 120f;
public float obstacleRange = 3.0f;
[Header("Enemy Property")]
public float enemyHeight = 1.8f;
public float enemyWidth = 1.2f;
public float rotateSpeed = 2.0f;
public float maxDetectDistance = 10f;
private float _walkSpeed = 1.5f;
private float _runSpeed = 3.5f;
private Animator _animator;
private Transform _transform;
private float _currentSpeed;
private float _targetSpeed;
private float _statusDuration = 1.0f;
private bool _isStatusTimerEnds = true;
private bool _isDetectTimerEnds = true;
[Flags]
private enum EnemyStatus {
Aim,
Shoot,
Wander,
Chase
}
private EnemyStatus _enemyStatus;
void Start()
{
_animator = GetC