众所周知,NavMeshAgent一旦设定了destination,它就会直奔目标。但是在一些场景中,比如NPC是个射手,除了瞄准玩家,也需要走位。如果不加以处理,我们恐怕会遇见瞄准IK和朝向…难以言表的表现,直接上图吧。
下文我就以我的测试项目为例去讲这个事情。
准备工作
在开始之前,确保你已经熟悉Unity引擎的基本操作和NavMesh的设置。另外,需要在角色身上添加NavMeshAgent组件,并保证角色所在的场景已经设置好了NavMesh。
在我这个项目里,使用了官方的第三人称控制器package和Mirror Networking,可以上Asset Store去找。我们NavMeshAgent除了修改position和rotation,还要把velocity转换成输入的x、y,模拟玩家输入,确保原Controller脚本的move函数正常进行,并且能够更新动画,这样改的量能少一些。
其次需要注意的是,动画机也有改动,动画机参数多了两个float,分别代表纵轴和横轴的输入。与此同时,还加入了一套平移用的动画StateMachine。
至于怎么实现ThirdPersonController的Move函数如何实现固定朝向,这里就不进行赘述。
实现Strafe移动
首先,我们需要在StarterAssetsInput的脚本中添加一个方法来处理NavMeshAgent的移动操作。要激活这套逻辑,**需要确保该角色处于瞄准的状态下,而且我们需要保证agent无论怎么移动都要始终面向目标。**下面是一个示例代码:
/// <summary>
/// NavMeshAgent操作移动
/// </summary>
[ServerCallback]
private void OnNavMeshAgentInput()
{
if (!netIdentity.isServer) return;
if (!_teamIdentifier || !_teamIdentifier.IsBot) return;
if (!_agent || !_agent.enabled) return;
if (aim)
{
if(_agent.remainingDistance > _agent.stoppingDistance)
{
Vector3 targetDirection = _agent.destination - transform.position;
Vector3 desiredVelocity = targetDirection.normalized;
_agent.velocity = desiredVelocity;
}
}
MoveInput(new Vector2(_agent.velocity.x, _agent.velocity.z));
// TODO: 跳跃、下蹲、跑步
// 根据角色速度判断是否要切换到跑步状态
// 跑步已改到Behavior Designer实现
// sprint = _agent.velocity.magnitude > 3f; // 假设速度大于5时切换到跑步状态
}
上面的代码中,我们通过计算目标方向和期望速度来实现Strafe移动效果。当角色处于移动状态时,设置NavMeshAgent的速度为期望速度,从而实现横向移动。
然而没有完事,人的确固定朝向移动了,但是腿出了毛病,前进变后退,左右却在前进后退时播放。这个是因为velocity的x和z与正常玩家的输入的xy值是有出入的,我们需要特殊处理。
接着,我们需要更新动画控制器来反映角色的移动状态。下面是更新动画控制器的代码片段:
// update animator if using character
if (_hasAnimator)
{
_animator.SetFloat(_animIDSpeed, _animationBlend);
_animator.SetFloat(_animIDMotionSpeed, inputMagnitude);
if (!_agent || !_agent.enabled)
{
_animator.SetFloat(_animIDHorizontal, _speed * _input.move.x);
_animator.SetFloat(_animIDVertical, _speed * _input.move.y);
}
else
{
_animator.SetFloat(_animIDVertical, Vector3.Dot(transform.forward, _agent.desiredVelocity), 0.1f, Time.deltaTime);
_animator.SetFloat(_animIDHorizontal, Vector3.Dot(transform.right, _agent.desiredVelocity), 0.1f, Time.deltaTime);
}
}
在上面的代码片段中,我们使用了Vector3.Dot方法来计算两个向量之间的点积。点积是一种向量运算,用于衡量两个向量之间的相似程度。具体来说,点积计算的是两个向量之间的夹角的余弦值乘以两个向量的长度之积。
在动画控制器中,我们使用Vector3.Dot方法来计算角色的前方向和NavMeshAgent的desiredVelocity向量之间的点积。这样做的目的是为了获取角色当前移动方向与期望移动方向之间的相似程度,从而在动画中正确地表现出角色的横向移动状态。
可能不是很完美,但希望能有所启发,至少在更新这个动画机参数这块。