Unity中如何让Nav Mesh Agents将彼此视为障碍物?
问题核心
- NavMeshAgent默认不将其他Agent视为障碍物,导致单位间推挤甚至穿模,无法实现《星际争霸》《魔兽争霸》《Dota》类的单位互避效果
- 同一对象同时挂载NavMeshAgent与NavMeshObstacle时,会因Agent原点与不可行走区域重叠出现随机抖动;调整偏移量仅能临时解决,其他单位靠近时重叠问题复现
- 增大Agent半径无法解决重叠问题,核心需求是同一对象共存Agent与Obstacle,且Agent忽略自身的Obstacle
可行解决方案
方法1:动态切换NavMeshObstacle雕刻状态
这是最直接的方案,利用carveOnlyStationary属性让单位移动时不生成自身障碍物,静止时成为阻挡:
- 给目标单位同时挂载
NavMeshAgent和NavMeshObstacle组件 - 在Inspector面板设置
NavMeshObstacle.carveOnlyStationary = true,默认关闭carving选项 - 通过脚本监控Agent移动状态,动态开启/关闭Obstacle的雕刻功能:
using UnityEngine; using UnityEngine.AI; public class UnitNavManager : MonoBehaviour { private NavMeshAgent _agent; private NavMeshObstacle _obstacle; void Start() { _agent = GetComponent<NavMeshAgent>(); _obstacle = GetComponent<NavMeshObstacle>(); _obstacle.carveOnlyStationary = true; _obstacle.carving = false; } void Update() { bool isMoving = _agent.velocity.magnitude > 0.1f; // 移动时关闭自身障碍物雕刻,避免与Agent冲突;静止时开启,阻挡其他单位 _obstacle.carving = !isMoving; } }
方法2:分层隔离自身Agent与Obstacle
通过Layer和子对象分离组件,实现Agent忽略自身Obstacle:
- 创建两个自定义Layer:
UnitAgent和UnitObstacle - 将挂载
NavMeshAgent的主对象Layer设为UnitAgent - 创建子对象,将
NavMeshObstacle挂载到子对象上,子对象Layer设为UnitObstacle - 在NavMesh设置中,为
UnitObstacle指定专属的NavMeshArea(比如自定义UnitBlock区域) - 将主对象的
NavMeshAgent.areaMask设置为不包含UnitBlock区域,让Agent忽略自身Obstacle生成的不可行走区域 - 其他单位的Obstacle同样使用
UnitObstacleLayer,确保能被当前Agent检测到,实现互避
方法3:自定义动态互避逻辑(补充增强)
如果默认NavMesh避障效果不够细腻,可以结合碰撞检测实现单位间的排斥力:
- 给每个单位添加Sphere Collider(勾选Is Trigger),设置合适的检测半径
- 编写脚本检测周围同层单位,计算排斥力并施加到Agent上:
using UnityEngine; using UnityEngine.AI; public class UnitRepel : MonoBehaviour { private NavMeshAgent _agent; public float repelForce = 6f; public float repelRange = 1.8f; void Start() { _agent = GetComponent<NavMeshAgent>(); } void FixedUpdate() { Collider[] nearbyUnits = Physics.OverlapSphere(transform.position, repelRange, LayerMask.GetMask("Unit")); foreach (Collider unitCollider in nearbyUnits) { if (unitCollider.gameObject != gameObject) { Vector3 repelDir = (transform.position - unitCollider.transform.position).normalized; _agent.AddForce(repelDir * repelForce, ForceMode.VelocityChange); } } } }
内容的提问来源于stack exchange,提问作者KoHcocb
相关产品推荐
相关产品推荐

