Development of Navigation Systems (NavMesh/Pathfinding) in Mobile Games
An enemy walking through walls or an NPC stuck in a corner isn't a bug—it's the absence of a proper navigation system. NavMesh and pathfinding solve a specific problem: give an agent a route from point A to point B while avoiding obstacles, and do it efficiently.
NavMesh: Static Navigation Geometry
NavMesh is a simplified representation of the level that agents can traverse. It's built once at level load (or baked in the editor beforehand). Unity offers built-in NavMesh with NavMeshAgent component. Godot has NavigationServer3D / NavigationServer2D.
Key NavMesh baking parameters affecting quality:
Agent Radius: 0.4 // capsule radius—NavMesh is offset by this
Agent Height: 1.8 // for detecting low passages
Max Slope: 45° // maximum climb angle
Step Height: 0.4 // step height agent can climb
On mobile, critical: NavMesh is baked beforehand in the Editor, not at runtime. Runtime baking (via NavMeshBuilder.BuildNavMeshAsync) exists but takes 100–500ms on weak devices and creates garbage collection pressure.
A* and Its Variants
Unity uses built-in A* with Euclidean distance heuristic for pathfinding on NavMesh. This is optimal for most mobile games. But scenarios exist where standard A* struggles:
Dynamic Obstacles — player places barricades, door closes. Standard NavMesh needs rebuilding. Solution: NavMeshObstacle with Carve = true cuts the component from NavMesh at runtime, but the recalculation is expensive. For frequent changes, Flow Field pathfinding: calculate a vector field for the target once, agents follow the field without individual pathfinding.
Many Agents, One Target — zombie games, tower defense. A* for each agent separately with 100+ agents kills performance. Flow Field calculates once for the field—agents read their cell's value. O(1) per agent versus O(n log n) for A*.
// Unity: basic Flow Field calculation for tile-based maps
public class FlowField {
private Vector2[,] directions;
private int width, height;
public void Calculate(Vector2Int target, bool[,] obstacles) {
// BFS from target, calculate cost and direction for each cell
var costField = new int[width, height];
var queue = new Queue<Vector2Int>();
queue.Enqueue(target);
costField[target.x, target.y] = 0;
while (queue.Count > 0) {
var current = queue.Dequeue();
foreach (var neighbor in GetNeighbors(current)) {
if (!obstacles[neighbor.x, neighbor.y] &&
costField[neighbor.x, neighbor.y] == int.MaxValue) {
costField[neighbor.x, neighbor.y] = costField[current.x, current.y] + 1;
queue.Enqueue(neighbor);
}
}
}
// Fill directions based on costField...
}
public Vector2 GetDirection(Vector2Int position) => directions[position.x, position.y];
}
Steering Behaviours: Smooth Movement
Pathfinding provides a route—a list of waypoints. Steering behaviours turn this into smooth motion:
- Seek / Arrive: move toward target, slow down approaching
- Obstacle Avoidance: avoid dynamic obstacles via Raycast
- Separation: agents don't cluster at one point
- Cohesion: group stays together (for flocking behavior)
NavMeshAgent in Unity includes basic steering behaviours. For finer control, use RVOSimulator from com.unity.ai.navigation package (ORCA algorithm for reciprocal velocity obstacles).
Mobile Device Performance
Don't recalculate paths every frame. NavMeshAgent.SetDestination() on every call triggers a new pathfinding request. For chasing the player, recalculate every 0.3–0.5 seconds:
private float pathUpdateTimer = 0f;
private const float PATH_UPDATE_INTERVAL = 0.3f;
void Update() {
pathUpdateTimer += Time.deltaTime;
if (pathUpdateTimer >= PATH_UPDATE_INTERVAL) {
agent.SetDestination(player.position);
pathUpdateTimer = 0f;
}
}
LOD for Navigation. Agents off-camera disable NavMeshAgent and use simplified teleportation to waypoints. Enable full pathfinding only when in frustum.
Unity Job System for A.* For custom pathfinding on large maps, IJob + NativeArray<> offloads from main thread. Burst Compiler gives ~10x speedup for pathfinding math.
Debugging and Visualization
Pathfinding is hard to debug without visualization. In Editor mode, draw the NavMesh path:
void OnDrawGizmos() {
if (agent != null && agent.hasPath) {
Gizmos.color = Color.yellow;
var corners = agent.path.corners;
for (int i = 0; i < corners.Length - 1; i++) {
Gizmos.DrawLine(corners[i], corners[i + 1]);
}
}
}
Process Overview
Analyze the level: static geometry, dynamic obstacles, number of agents.
Choose algorithm: NavMesh + A* for typical scenarios, Flow Field for mass agents.
Configure NavMesh parameters, integrate with game geometry.
Implement steering behaviours, tune movement smoothness.
Optimize: navigation LOD, update intervals, Job System for custom pathfinding.
Test on target devices with Unity Profiler.
Timeline Estimates
Basic navigation via NavMeshAgent for 5–10 agent types: 3–5 days. Custom system with Flow Field, dynamic obstacles, and LOD optimization: 2–4 weeks.







