17.1 NavMesh 概述
NavMesh(导航网格)是 Unity 内置的寻路系统,将场景烘焙为网格数据,AI 角色可以自动计算路径并移动到目标。
核心组件
| 组件 |
作用 |
| NavMesh |
场景的导航网格数据 |
| NavMesh Agent |
角色的寻路代理 |
| NavMesh Obstacle |
动态障碍物 |
| Off-Mesh Link |
跳跃/跨越的链接(沟壑、围栏) |
17.2 烘焙 NavMesh
设置步骤
- 选中场景中可作为地面的物体,Inspector 顶部勾选 Navigation Static
- Window → AI → Navigation 打开导航面板
- Bake 标签页配置参数:
| 参数 |
说明 |
| Agent Radius |
代理半径(影响可通行的窄道) |
| Agent Height |
代理高度 |
| Max Slope |
最大可爬坡度数 |
| Step Height |
最大跨步高度 |
| Min Region Area |
最小区域面积(小于此值的区域视为不可达) |
- 点击 Bake 按钮生成 NavMesh
烘焙后可在 Scene 视图中看到蓝色覆盖层,表示可行走区域。
17.3 NavMeshAgent 组件
添加和配置
给角色添加 NavMeshAgent 组件,配置移动参数:
| 属性 |
说明 |
| Speed |
移动速度 |
| Angular Speed |
旋转速度 |
| Acceleration |
加速度 |
| Stopping Distance |
停止距离(到达目标点的容忍距离) |
| Auto Braking |
到达目标时自动减速 |
| Obstacle Avoidance |
障碍物躲避等级 |
基础寻路
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39
| public class EnemyAI : MonoBehaviour { private NavMeshAgent agent;
[SerializeField] private Transform target; [SerializeField] private float updateInterval = 0.5f; private float timer;
void Start() { agent = GetComponent<NavMeshAgent>(); }
void Update() { timer += Time.deltaTime; if (timer >= updateInterval) { timer = 0f; agent.SetDestination(target.position); }
if (!agent.pathPending && agent.remainingDistance <= agent.stoppingDistance) { if (!agent.hasPath || agent.velocity.magnitude < 0.1f) { Attack(); } } }
private void Attack() { Debug.Log("攻击玩家!"); } }
|
17.4 寻路状态与路径信息
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
| bool isOnNavMesh = agent.isOnNavMesh; bool hasPath = agent.hasPath; bool isPathStale = agent.isPathStale; bool pathPending = agent.pathPending; NavMeshPathStatus pathStatus = agent.pathStatus;
float remainingDistance = agent.remainingDistance; float stoppingDistance = agent.stoppingDistance; Vector3 desiredVelocity = agent.desiredVelocity; Vector3 nextPosition = agent.nextPosition;
agent.isStopped = true; agent.ResetPath(); agent.Warp(newPos);
NavMeshPath path = new NavMeshPath(); if (NavMesh.CalculatePath(startPos, endPos, NavMesh.AllAreas, path)) { for (int i = 0; i < path.corners.Length - 1; i++) { Debug.DrawLine(path.corners[i], path.corners[i + 1], Color.green, 1f); } }
|
17.5 动态障碍物
NavMeshObstacle 组件让移动物体影响寻路:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| public class MovingObstacle : MonoBehaviour { private NavMeshObstacle obstacle;
void Start() { obstacle = GetComponent<NavMeshObstacle>(); obstacle.carving = true; }
void Update() { transform.position = new Vector3( Mathf.Sin(Time.time) * 5f, 0, transform.position.z ); } }
|
carving = true 会持续在运行时的 NavMesh 中更新空洞,性能消耗随障碍物数量增加。
17.6 Off-Mesh Link
Off-Mesh Link 用于连接 NavMesh 上不连续的区域(跳跃、爬梯子)。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35
|
void Start() { agent.autoTraverseOffMeshLink = true; }
void Update() { if (agent.isOnOffMeshLink) { StartCoroutine(TraverseLink()); } }
IEnumerator TraverseLink() { OffMeshLinkData data = agent.currentOffMeshLinkData; Vector3 startPos = agent.transform.position; Vector3 endPos = data.endPos + Vector3.up * 1f; float elapsed = 0f; float duration = 1f;
while (elapsed < duration) { elapsed += Time.deltaTime; float t = elapsed / duration; agent.transform.position = Vector3.Lerp(startPos, endPos, t); yield return null; }
agent.CompleteOffMeshLink(); }
|
17.7 本章小结
- NavMesh 通过烘焙生成可行走区域,搭配 NavMeshAgent 实现 AI 寻路
SetDestination 是核心 API,设置目标位置
- NavMeshObstacle 让动态物体影响寻路
- Off-Mesh Link 连接 NavMesh 不连续区域
- 合理的区域遮罩(Area Mask)控制角色的可通行区域
练习题
- 烘焙一个简单的场景,让一个角色 AI 自动走到鼠标点击位置
- 实现”巡逻”逻辑:AI 在多个路点之间循环移动
- 添加一个可推动的箱子(NavMeshObstacle),观察 AI 避让效果
- 在场景中设置两个不同区域层(如地面和水面),让 AI 避开水面