Tags: unity, navigation, navmesh, pathfinding, a-star, waypoints, obstacle-avoidance, game-dev Last updated: 2026-06-27

Navigation & Pathfinding Cheatsheet

Quick Reference

ApproachBest ForComplexity
Unity NavMeshBuilt-in, baked static worldsLow
Waypoint graphSimple patrol routesLow
A* gridCustom tile/grid-based gamesMedium
NavMesh + off-mesh linksDynamic traversal (ladders, jumps)Medium
NavMeshComponentsRuntime-baked NavMeshHigh

Unity NavMesh

Setup Checklist

  1. Mark static geometry as Navigation Static.
  2. Window → AI → Navigation → Bake tab → Bake.
  3. Add NavMeshAgent component to moving NPC.
  4. Call agent.SetDestination(target).

NavMeshAgent Properties

PropertyDescription
SpeedMax movement speed (m/s)
Angular SpeedRotation speed (deg/s)
AccelerationAcceleration (m/s²)
Stopping DistanceDistance from target to stop
Auto BrakingDecelerate before reaching target
RadiusAgent radius for obstacle avoidance
HeightAgent height
Base OffsetVertical offset from pivot
Obstacle AvoidanceNone / Low / Medium / High Quality
Path PriorityPriority for path queue (0–99)
Auto Traverse Off-Mesh LinksAuto-use jump/ladder links
Auto RepathRecalculate path if blocked

Core API

NavMeshAgent agent = GetComponent<NavMeshAgent>();

agent.SetDestination(target.position);          # Move to point
agent.destination = target.position;            # Same as above
agent.ResetPath();                              # Stop moving
agent.isStopped = true;                         # Pause movement
agent.remainingDistance;                        # Distance to target
agent.pathPending;                              # Is path computing?
agent.velocity;                                 # Current velocity
agent.desiredVelocity;                          # Steering target velocity

# Check if reached
if (!agent.pathPending && agent.remainingDistance <= agent.stoppingDistance)
    // Reached destination

Path Status

NavMeshPath path = new NavMeshPath();
if (agent.CalculatePath(target.position, path)) {
    if (path.status == NavMeshPathStatus.PathComplete) {
        agent.SetPath(path);
    } else if (path.status == NavMeshPathStatus.PathPartial) {
        // Move to closest reachable point
    }
}

NavMesh Baking

Area Types

AreaCostUse Case
Walkable1.0Default ground
Not WalkableObstacles, walls
Jump2.0Off-mesh link areas
Water / Mud3.0+Slow movement areas
User 3–31CustomCustom terrain costs
# Sample area at position
NavMesh.SamplePosition(position, out NavMeshHit hit, 1f, NavMesh.AllAreas);
int areaMask = hit.mask;
bool isWalkable = (areaMask & 1) != 0;

Off-Mesh Links

# Manual traversal
IEnumerator TraverseLink() {
    OffMeshLink link = GetComponent<OffMeshLink>();
    agent.isStopped = true;
    agent.updatePosition = false;
    agent.updateRotation = false;

    Vector3 start = link.startPos;
    Vector3 end = link.endPos;
    float t = 0;

    while (t < 1f) {
        t += Time.deltaTime * 2f;
        transform.position = Vector3.Lerp(start, end, t);
        yield return null;
    }

    agent.updatePosition = true;
    agent.updateRotation = true;
    agent.CompleteOffMeshLink();
}

Off-Mesh Link Types

TypeUse Case
Auto-generatedDrop-down edges, jumps across gaps
Manual placementLadders, teleporters, ziplines
NavMeshLink (component)Runtime links, procedural generation

Waypoint System

public class WaypointPatrol : MonoBehaviour {
    public Transform[] waypoints;
    int index = 0;
    NavMeshAgent agent;

    void Start() { agent = GetComponent<NavMeshAgent>(); GoNext(); }

    void Update() {
        if (!agent.pathPending &&
            agent.remainingDistance <= agent.stoppingDistance)
            GoNext();
    }

    void GoNext() {
        agent.SetDestination(waypoints[index].position);
        index = (index + 1) % waypoints.Length;
    }
}

Waypoint Patterns

PatternDescription
LoopCycle through all waypoints
Ping-PongForward, then reverse back
RandomPick random waypoint each time
Weighted RandomFavour nearby or “interesting” waypoints

A* Pathfinding (Grid-Based)

Core Algorithm

List<Node> AStar(Node start, Node goal) {
    var open = new PriorityQueue<Node, float>();
    var closed = new HashSet<Node>();
    start.gCost = 0; start.hCost = Heuristic(start, goal);
    open.Enqueue(start, start.fCost);

    while (open.Count > 0) {
        Node current = open.Dequeue();
        if (current == goal) return ReconstructPath(current);
        closed.Add(current);

        foreach (Node neighbor in current.Neighbors) {
            if (closed.Contains(neighbor) || !neighbor.Walkable) continue;
            float tentativeG = current.gCost + Distance(current, neighbor);
            if (tentativeG < neighbor.gCost) {
                neighbor.parent = current;
                neighbor.gCost = tentativeG;
                neighbor.hCost = Heuristic(neighbor, goal);
                open.Enqueue(neighbor, neighbor.fCost);
            }
        }
    }
    return null;
}

Heuristic Functions

HeuristicUse Case
Manhattan4-directional grid (|dx| + |dy|)
EuclideanAny-direction (sqrt(dx² + dy²))
Diagonal8-directional grid (max(|dx|,|dy|))
WeightedFaster but suboptimal (h * weight > 1)

Obstacle Avoidance

NavMeshObstacle

PropertyDescription
ShapeBox or Capsule
CarveCarve a hole in the NavMesh (static)
Carve Only StationaryOnly carve when not moving
NavMeshObstacle obstacle = GetComponent<NavMeshObstacle>();
obstacle.enabled = true;    # Carve hole, agents avoid
obstacle.enabled = false;   # Remove hole, agents can pass

Local Avoidance (RVO)

NavMeshComponents (Runtime Baking)

NavMeshSurface surface = GetComponent<NavMeshSurface>();
surface.BuildNavMesh();

surface.layerMask = LayerMask.GetMask("Ground");
surface.useGeometry = NavMeshCollectGeometry.PhysicsColliders;

Performance Tips