NavMesh
NavMesh(Navigation Mesh)是从场景几何烘培出的可走网格,让 Unity AI Agent 能寻路、避障并在关卡中自动移动。配合 NavMeshAgent 与 NavMeshObstacle,适合敌人巡逻、NPC 跟随与塔防路径,帮助关卡设计与 AI 程序快速落地稳定可靠的导航系统。
想象一下...
NavMesh 是 3D 世界的路网地图。开玩前,Unity「画出」可走区域(蓝色)与阻挡(墙、障碍)。AI 要从 A 到 B 时,查阅该图并用 A-star 求最优路径——绕墙、上楼梯、远离悬崖。NavMeshAgent 就是在这张图上开的 GPS 司机。
概念详解
NavMesh Baking:Unity 分析静态场景几何 (标记为 “Navigation Static”)生成可走网格。关键参数:Agent Radius (与墙的最小间隙——缝隙小于 2×radius 时 agent 挤不过)、 Agent Height(头顶高度——低天花板下不能钻)、Max Slope (最陡爬坡)、Step Height(最大台阶)。烘培:Window → AI → Navigation → Bake。
NavMeshAgent 挂在 NPC/AI 上并查询 NavMesh。主要 API:
agent.SetDestination(target)
开始寻路;
agent.remainingDistance
看剩余距离;
agent.pathStatus
(Complete/Partial/Invalid)。speed、acceleration、stoppingDistance
控制运动。
NavMeshObstacle 是动态阻挡——无需重新烘培。 开启 Carve 可在运行时切出 NavMesh 空洞(更耗 CPU);关闭 Carve 则仅做避让 (agent 绕行,网格不变)。适合开门与移动载具。
动态 NavMesh(运行时重烘)请用 NavMesh Components package
(GitHub 上 Unity-Technologies)配合
NavMeshSurface
——烘培区域并增量更新。Unity 6 把多表面 NavMesh 收进内置包。
NavMesh 系统组成
NavMesh
从场景几何 bake 出的“可行走”网格。静态,bake 后不再改变。
Navigation Static flagNavMeshAgent
挂在 AI 上的组件 — 查询路径、自动移动、避开其他 agent(avoidance)。
SetDestination(target)NavMeshObstacle
动态障碍 — 无需重新 bake。Carve 模式会在运行时在 NavMesh 上挖洞。
Carve = true/false动手步骤
标记静态几何
选中静态网格(墙、地板、平台)→ Inspector → Static 下拉 → Navigation Static(需要光照贴图再勾 Contribute GI)。
烘培 NavMesh
Window → AI → Navigation → Bake 选项卡 → 调整 Agent Radius/Height/Slope → Bake。Scene 视图出现蓝色 NavMesh。
给 NPC 添加 NavMeshAgent
Add Component → NavMeshAgent。Agent Size 与烘培设置一致。若有 Rigidbody,关闭重力(由 agent 负责移动)。
用脚本设置 Destination
agent.SetDestination(target.position)。用 agent.remainingDistance <= agent.stoppingDistance 判断是否到达。
为移动阻挡添加 NavMeshObstacle
Add Component → NavMeshObstacle → Shape:Capsule/Box → 需要实时更新 NavMesh 时开启 Carve(门、停泊车辆)。
交互模拟器
点击地图设置目的地——Agent 用 A* 在 NavMesh 上寻路。
代码示例
基础简单 NavMeshAgent AI——追逐玩家并在停止距离处停下。
using UnityEngine;
using UnityEngine.AI;
public class EnemyAI : MonoBehaviour
{
public Transform player;
public float chaseRange = 15f;
public float attackRange = 2f;
public float updateRate = 0.2f; // Recompute path every 0.2s
NavMeshAgent agent;
Animator anim;
float nextUpdate;
void Awake()
{
agent = GetComponent<NavMeshAgent>();
anim = GetComponent<Animator>();
}
void Update()
{
float dist = Vector3.Distance(transform.position, player.position);
if (dist < chaseRange && Time.time >= nextUpdate)
{
nextUpdate = Time.time + updateRate;
agent.SetDestination(player.position);
}
// Drive Animator from agent speed
anim.SetFloat("Speed", agent.velocity.magnitude);
if (dist < attackRange)
{
agent.ResetPath(); // Stop moving
anim.SetTrigger("Attack");
}
}
}代码示例
进阶NavMesh 上的巡逻 AI——路点循环加玩家侦测。
using UnityEngine;
using UnityEngine.AI;
public class PatrolAI : MonoBehaviour
{
public Transform[] waypoints;
public float detectRange = 8f;
public Transform player;
NavMeshAgent agent;
int current = 0;
enum State { Patrol, Chase, Investigate }
State state = State.Patrol;
void Awake() => agent = GetComponent<NavMeshAgent>();
void Start() => GotoNextWaypoint();
void Update()
{
float dist = Vector3.Distance(transform.position, player.position);
switch (state)
{
case State.Patrol:
if (dist < detectRange) { state = State.Chase; break; }
if (!agent.pathPending && agent.remainingDistance < 0.5f)
GotoNextWaypoint();
break;
case State.Chase:
agent.SetDestination(player.position);
if (dist > detectRange * 1.5f)
state = State.Investigate;
break;
case State.Investigate:
if (!agent.pathPending && agent.remainingDistance < 0.5f)
{ state = State.Patrol; GotoNextWaypoint(); }
break;
}
}
void GotoNextWaypoint()
{
if (waypoints.Length == 0) return;
agent.SetDestination(waypoints[current].position);
current = (current + 1) % waypoints.Length;
}
}📌 快速记忆
- ▸NavMesh = 可走地图,从静态几何烘培
- ▸每帧 SetDestination() 很贵——用协程节流
- ▸remainingDistance:剩余路径长度
- ▸NavMeshObstacle + Carve = 运行时挖洞
- ▸使用 NavMeshAgent 时关闭 Rigidbody.gravity
⚠️ 常见错误
❌ SetDestination 正确但 Agent 不动
目标点不在 NavMesh 上,或从未烘培
✅ 用 NavMesh.SamplePosition() 找网格上最近点
❌ Agent 穿墙或卡住
NavMeshAgent 的 Agent Radius 小于烘培半径
✅ 让 NavMeshAgent Agent Radius 与烘培 Agent Radius 一致