Unity NavMesh Reference
NavMeshAgent setup, baking, runtime queries, and obstacle avoidance with copy-paste C# code.
Unity's NavMesh system handles pathfinding and obstacle avoidance for AI agents. This reference covers the most common properties, methods, and patterns you need when working with NavMeshAgent, NavMeshSurface, and runtime queries. All code targets the built-in NavMesh system and the AI Navigation package.
NavMeshAgent Properties
Key public properties on NavMeshAgent. Use the search box to filter.
| Property | Type | Default | Description |
|---|---|---|---|
| speed | float | 3.5 | Maximum movement speed in world units per second. |
| angularSpeed | float | 120 | Maximum rotation speed in degrees per second. |
| acceleration | float | 8 | How quickly the agent reaches top speed. |
| stoppingDistance | float | 0 | Agent stops when this close to the destination. |
| radius | float | 0.5 | Agent avoidance radius. Also affects baked NavMesh clearance. |
| height | float | 2 | Agent height for NavMesh clearance calculations. |
| isStopped | bool | false | When true, the agent halts but keeps its current path. |
| hasPath | bool | (read-only) | True when the agent has a valid, calculated path. |
| remainingDistance | float | (read-only) | Distance left along the current path to the destination. |
| pathStatus | NavMeshPathStatus | (read-only) | PathComplete, PathPartial, or PathInvalid. |
| areaMask | int | -1 (all) | Bitmask controlling which NavMesh areas the agent can traverse. |
Setting a Destination
Call SetDestination to send an agent to a world position. Always check pathPending before reading path data, and use remainingDistance to know when the agent has arrived.
using UnityEngine;
using UnityEngine.AI;
public class AgentMovement : MonoBehaviour
{
[SerializeField] private Transform target;
[SerializeField] private float arrivalThreshold = 0.5f;
private NavMeshAgent _agent;
void Awake()
{
_agent = GetComponent<NavMeshAgent>();
}
void Update()
{
// Set the destination each frame (or on demand)
_agent.SetDestination(target.position);
// Wait until the path is fully calculated
if (_agent.pathPending) return;
// Check if the agent has arrived
if (_agent.remainingDistance <= arrivalThreshold)
{
Debug.Log("Agent arrived at destination.");
}
}
}Runtime Baking
Runtime NavMesh baking requires the AI Navigation package (com.unity.ai.navigation). Add a NavMeshSurface component and call BuildNavMesh() or UpdateNavMesh() at runtime. This is essential for procedurally generated levels or destructible environments.
Requires package: Install via Package Manager or add com.unity.ai.navigation to your manifest.
using UnityEngine;
using Unity.AI.Navigation;
public class RuntimeNavMeshBaker : MonoBehaviour
{
[SerializeField] private NavMeshSurface surface;
void Start()
{
// Build the entire NavMesh at startup
surface.BuildNavMesh();
}
/// <summary>
/// Call after modifying the environment (e.g. placing a building).
/// UpdateNavMesh only recalculates dirty regions, so it is
/// faster than a full rebuild.
/// </summary>
public void RefreshNavMesh()
{
surface.UpdateNavMesh(surface.navMeshData);
}
}NavMesh Query
Use static methods on NavMesh to sample positions, calculate paths ahead of time, and validate destinations before sending agents.
SamplePosition
Find the closest point on the NavMesh within a given radius. Useful for snapping spawned objects or validating click targets.
// Find the nearest point on the NavMesh within 10 units
if (NavMesh.SamplePosition(worldPosition, out NavMeshHit hit, 10f, NavMesh.AllAreas))
{
Vector3 validPosition = hit.position;
agent.SetDestination(validPosition);
}
else
{
Debug.LogWarning("No NavMesh found near " + worldPosition);
}CalculatePath
Pre-calculate a path without moving an agent. Check NavMeshPathStatus to see if the destination is reachable.
NavMeshPath path = new NavMeshPath();
bool found = NavMesh.CalculatePath(
transform.position,
destination,
NavMesh.AllAreas,
path
);
switch (path.status)
{
case NavMeshPathStatus.PathComplete:
Debug.Log("Full path found.");
break;
case NavMeshPathStatus.PathPartial:
Debug.Log("Only a partial path is reachable.");
break;
case NavMeshPathStatus.PathInvalid:
Debug.Log("No path exists.");
break;
}NavMeshObstacle
NavMeshObstacle blocks agent paths without rebaking. It has two modes: carving and non-carving. Choosing the right mode matters for performance.
Non-Carving (default)
Agents steer around the obstacle using local avoidance only. The NavMesh itself is unchanged. Best for moving obstacles like patrolling enemies or rolling boulders.
Carving
Cuts a hole in the NavMesh so agents path around it globally. Use for stationary or rarely moving obstacles like placed barricades. Has a performance cost when the obstacle moves because the NavMesh is re-carved each time.
using UnityEngine;
using UnityEngine.AI;
public class BarricadeObstacle : MonoBehaviour
{
private NavMeshObstacle _obstacle;
void Awake()
{
_obstacle = GetComponent<NavMeshObstacle>();
// Enable carving for a stationary barricade
_obstacle.carving = true;
// Only re-carve when the obstacle moves more than 0.1 units
_obstacle.carvingMoveThreshold = 0.1f;
// Wait 0.5 seconds after stopping before carving
// (prevents expensive re-carves while sliding into place)
_obstacle.carvingTimeToStationary = 0.5f;
}
}Area Masks
NavMesh areas let you assign different traversal costs to surfaces (e.g. roads are cheap, mud is expensive). Agents use area masks as a bitfield to filter which areas they can walk on.
Setting area costs
// Make the agent prefer roads over mud
NavMeshAgent agent = GetComponent<NavMeshAgent>();
// Area indices are defined in Navigation > Areas tab
// Default areas: 0 = Walkable, 1 = Not Walkable, 2 = Jump
int mudAreaIndex = 3; // Custom area
int roadAreaIndex = 4; // Custom area
agent.SetAreaCost(mudAreaIndex, 5f); // Mud is 5x more expensive
agent.SetAreaCost(roadAreaIndex, 1f); // Roads are cheapFiltering areas with bitmask
Use areaMask to prevent an agent from entering certain areas entirely. Each area index corresponds to a bit in the mask.
// Only allow Walkable (bit 0) and Road (bit 4)
agent.areaMask = (1 << 0) | (1 << 4);
// Allow everything except Water (bit 5)
agent.areaMask = NavMesh.AllAreas & ~(1 << 5);
// Allow all areas (default)
agent.areaMask = NavMesh.AllAreas;Common Patterns
Copy-paste patterns for the most frequent NavMesh tasks.
Patrol Between Waypoints
Cycle through an array of waypoints. The agent moves to each in order, then loops back to the first.
using UnityEngine;
using UnityEngine.AI;
public class PatrolAgent : MonoBehaviour
{
[SerializeField] private Transform[] waypoints;
[SerializeField] private float waitTime = 1f;
private NavMeshAgent _agent;
private int _currentIndex;
private float _waitTimer;
void Awake()
{
_agent = GetComponent<NavMeshAgent>();
}
void Start()
{
if (waypoints.Length > 0)
_agent.SetDestination(waypoints[0].position);
}
void Update()
{
if (_agent.pathPending) return;
if (_agent.remainingDistance <= _agent.stoppingDistance)
{
_waitTimer += Time.deltaTime;
if (_waitTimer >= waitTime)
{
_waitTimer = 0f;
_currentIndex = (_currentIndex + 1) % waypoints.Length;
_agent.SetDestination(waypoints[_currentIndex].position);
}
}
}
}Flee From Target
Move the agent away from a threat by sampling the NavMesh in the opposite direction.
using UnityEngine;
using UnityEngine.AI;
public class FleeAgent : MonoBehaviour
{
[SerializeField] private Transform threat;
[SerializeField] private float fleeDistance = 10f;
private NavMeshAgent _agent;
void Awake()
{
_agent = GetComponent<NavMeshAgent>();
}
public void Flee()
{
// Direction away from the threat
Vector3 fleeDir = (transform.position - threat.position).normalized;
Vector3 fleeTarget = transform.position + fleeDir * fleeDistance;
// Snap to the nearest valid NavMesh point
if (NavMesh.SamplePosition(fleeTarget, out NavMeshHit hit, fleeDistance, NavMesh.AllAreas))
{
_agent.SetDestination(hit.position);
}
}
}Stop and Resume
Pause an agent without clearing its path. Set isStopped to true to halt, then false to resume along the same path.
// Stop the agent (keeps the current path)
agent.isStopped = true;
// Resume movement along the existing path
agent.isStopped = false;
// To fully cancel navigation, also reset the path:
agent.ResetPath();Related Tools
Scripting Order
Interactive Unity MonoBehaviour lifecycle diagram. When does Awake, Start, Update, and every callback run.
Unity Animator Reference
Animator Controller methods, parameters, layers, blend trees, and avatar masks with C# code.
Game Maths
Interactive cheat sheet with live visualisations. Distance, lerp, dot product, vectors, and more with Unity C# code.