找回密码
 立即注册
查看: 245|回复: 0

Unity NavMeshAgent 耦合动画移动

[复制链接]
发表于 2023-4-3 08:10 | 显示全部楼层 |阅读模式
简单总结下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适用于攻击/移动等带位移的动画,避免穿模
懒得打字嘛,点击右侧快捷回复 【右侧内容,后台自定义】
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|手机版|Unity开发者联盟 ( 粤ICP备20003399号 )

GMT+8, 2024-6-27 06:20 , Processed in 0.090312 second(s), 25 queries .

Powered by Discuz! X3.5 Licensed

© 2001-2024 Discuz! Team.

快速回复 返回顶部 返回列表