|
简单总结下navmeshagent使用中遇到的几个问题.
自己的游戏中是使用agent获取目标速度, 然后根据动画root motion更新GameObject位置.获取速度并旋转物体朝向的代码如下
public Vector2 GetDirToPosbyNav(Vector3 pos)
{
Vector2 dir = Vector2.zero;
if (!nav.isOnNavMesh)
{
nav.Warp(new Vector3(Position.x, 0f, Position.z));
return dir;
}
NavMeshHit hit;
if (!nav.Raycast(pos, out hit))//检测不到路线上的agent
{
if (!Physics.CheckBox(transform.position + nav.radius * Vector3.up, nav.radius * Vector3.one))
return GameUtil.To2DVector(pos - transform.position);
}
bool hasDest = nav.SetDestination(pos);
bool hasPath = NavMesh.CalculatePath(Position, pos, NavMesh.AllAreas, path);
if (!hasDest || !hasPath || path.status != NavMeshPathStatus.PathComplete)
{
return dir;
}
dir = new Vector2(nav.desiredVelocity.x, nav.desiredVelocity.z).normalized;
return dir;
}
public void RotateToPosByNav(Vector3 pos)
{
Vector2 dir = GetDirToPosbyNav(pos);
if (dir != Vector2.zero)
{
Vector3 targetFWD = new Vector3(dir.x, 0f, dir.y);
transform.rotation = Quaternion.RotateTowards(transform.rotation,
Quaternion.LookRotation(targetFWD, Vector3.up), Const.MaxRoteAngDelta);
}
}更新物体位置的代码有两种方法, 顾名思义, 分别命名为UseNav和ForceAnim, nav是navmeshagent, anim是animator, 代码如下
public void UseNav()//基于agent位移
{
nav.velocity = anim.velocity;//此处赋值在下一帧才会生效
transform.position = nav.nextPosition; //nav.nextPosition就是agent圆柱状collider的位置
}
public void ForceAnim()//基于动画位移
{
nav.velocity = Vector3.zero;
nav.nextPosition = tranform.position + anim.deltaPosition;
transform.position = nav.nextPosition;
}
protected virtual void OnAnimatorMove()//调用位移更新
{
//两种方法二选一
//ForceAnim();//适用于Idle等无位移动画, 避免被推走
UseNav()//适用于攻击/移动等带位移的动画,避免穿模
}
private void InitNav()
{
nav.enabled = true;
nav.updatePosition = false;//重要
nav.updateRotation = false;//重要
nav.isStopped = false;
nav.autoBraking = false;
nav.obstacleAvoidanceType = ObstacleAvoidanceType.LowQualityObstacleAvoidance;
}
通常推荐的是第一种UseNav, 后来我又遇到了两个agent可以相互穿越或者被对方推着滑动的问题, 实验之后发现了agent另一个关键参数在起作用: ObstacleAvoidanceType枚举, 完全不规避为NoObstacleAvoidance, 其他是HighQualityObstacleAvoidance等, 控制agent A碰撞处于idle动画的agentB, 观察B的表现, 反复实验之后, 得到了下面的表格:
A\B | (Avoid, UseNav) | (Avoid,ForceAm) | (NoAvd, UseNav) | (NoAvd, ForceAm) | (Avoid, UseNav) | 滑动 | 原地 | 原地 | 原地 | (Avoid,ForceAm) | 滑动 | 穿越 | 穿越 | 穿越 | (NoAvd, UseNav) | 滑动 | 穿越 | 穿越 | 穿越 | (NoAvd, ForceAm) | 滑动 | 穿越 | 穿越 | 穿越 | 结论:
- 只要受击体为Avoid+UseNav即为滑动, 与驱动体无关
- 只要驱动体为Avoid+UseNav就不会穿越, 与受击体无关
- ForceAnim适用于Idle等无位移动画, 避免被推走, UseNav适用于攻击/移动等带位移的动画,避免穿模
|
|