NavMesh
NavMesh (Navigation Mesh) is a walkable mesh baked from scene geometry so AI Agents can pathfind, avoid obstacles, and move automatically across the level.
Imagine...
A NavMesh is a road map for a 3D world. Before play, Unity "paints" walkable areas (blue) and blocked ones (walls, obstacles). When AI needs to go from A to B, it looks up that map and runs A-star for an optimal path — around walls, up stairs, away from cliffs. NavMeshAgent is the GPS driver on that map.
The concept in detail
NavMesh Baking: Unity analyzes static scene geometry (marked “Navigation Static”) into a walkable mesh. Key parameters: Agent Radius (minimum wall clearance — an agent cannot squeeze through a gap smaller than 2×radius), Agent Height (head height — no crawling under low ceilings), Max Slope (steepest climb), Step Height (max stair). Bake via Window → AI → Navigation → Bake.
NavMeshAgent sits on an NPC/AI and queries the
NavMesh. Main API:
agent.SetDestination(target)
to start pathfinding;
agent.remainingDistance
for how far is left;
agent.pathStatus
(Complete/Partial/Invalid). speed, acceleration, stoppingDistance
control motion.
NavMeshObstacle is a dynamic blocker — no rebake. Enable Carve to cut a hole in the NavMesh at runtime (more CPU), or disable Carve for avoidance only (agents go around, the mesh stays). Good for opening doors and moving vehicles.
For dynamic NavMesh (runtime rebake), use the NavMesh Components package
(Unity-Technologies on GitHub) with
NavMeshSurface
— bake a region and update incrementally. Unity 6 folds multi-surface NavMesh into the
built-in package.
NavMesh system pieces
NavMesh
A walkable mesh baked from scene geometry. Static — does not change after bake.
Navigation Static flagNavMeshAgent
Component on AI — queries a path, moves automatically, avoids other agents (avoidance).
SetDestination(target)NavMeshObstacle
Dynamic obstacle — no rebake needed. Carve mode punches a hole in the NavMesh at runtime.
Carve = true/falseHands-on steps
Mark static geometry
Select static meshes (walls, floors, platforms) → Inspector → Static dropdown → Navigation Static (and Contribute GI if you need lightmaps).
Bake the NavMesh
Window → AI → Navigation → Bake tab → tune Agent Radius/Height/Slope → Bake. A blue NavMesh appears in Scene view.
Add a NavMeshAgent to the NPC
Add Component → NavMeshAgent. Match Agent Size to bake settings. Disable Rigidbody gravity if present (the agent owns movement).
Set Destination from script
agent.SetDestination(target.position). Check agent.remainingDistance <= agent.stoppingDistance to know they arrived.
Add NavMeshObstacle for moving blockers
Add Component → NavMeshObstacle → Shape: Capsule/Box → enable Carve if the NavMesh must update live (doors, parked cars).
Interactive simulator
Click the map to set a destination — the Agent pathfinds across the NavMesh with A*.
Code example
BasicSimple NavMeshAgent AI — chase the player and stop at stopping distance.
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");
}
}
}Code example
AdvancedPatrol AI on a NavMesh — waypoint loop plus player detection.
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;
}
}📌 Quick recap
- ▸NavMesh = walkable map, baked from static geometry
- ▸SetDestination() every frame is expensive — throttle with a coroutine
- ▸remainingDistance: leftover path length
- ▸NavMeshObstacle + Carve = a runtime hole
- ▸Disable Rigidbody.gravity when using NavMeshAgent
⚠️ Common mistakes
❌ The Agent does not move even though SetDestination is correct
The destination is off the NavMesh, or the NavMesh was never baked
✅ Use NavMesh.SamplePosition() to find the nearest point on the mesh
❌ The Agent walks through walls or gets stuck
Agent Radius on NavMeshAgent is smaller than the bake radius
✅ Match NavMeshAgent Agent Radius to the bake Agent Radius