Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/ProwlEngine/Prowl/llms.txt

Use this file to discover all available pages before exploring further.

Prowl’s navigation system is built on DotRecast, a C# port of the industry-standard Recast & Detour libraries. Two components handle everything: NavMeshSurface bakes walkable polygonal mesh data from your scene geometry and manages a DtCrowd simulation, while NavMeshAgent registers a moving entity with that crowd and drives the GameObject’s transform along the computed path. Because the underlying library is the same technology used in major commercial engines, you get proven tile-based pathfinding and local obstacle avoidance out of the box. NavMeshSurface is the scene component that owns the baked navigation data. You add it to any GameObject in the scene (the transform of this object defines the local coordinate space used when building the mesh), configure its BuildSettings, point it at source geometry, then press Rebuild in the Inspector.

Build Settings

All build parameters live in NavMeshSurface.BuildSettings, exposed in the Inspector under navSettings.
PropertyDefaultDescription
cellSize0.3Horizontal voxel resolution (metres). Smaller = finer mesh, higher cost.
cellHeight0.2Vertical voxel resolution (metres).
agentHeight1.0Minimum clearance above the floor the agent requires (metres).
agentRadius0.5Distance from the agent centre to the nearest wall (metres).
agentMaxClimb0.9Maximum step height the agent can climb (metres).
agentMaxSlope45.0Maximum walkable slope (degrees).
agentMaxAcceleration8.0Crowd acceleration (m/s²).
agentMaxSpeed3.5Default crowd maximum speed (m/s).
tileSize32Tile width/height in voxel cells.
minRegionSize8Smallest valid walkable region (voxels²).
mergedRegionSize20Regions smaller than this are merged with neighbours.
agentRadius and agentHeight are baked into the mesh at build time. If you have agents of very different sizes, create separate NavMeshSurface components with different settings on the same scene.

Source Geometry

NavMeshSurface supports two geometry source modes, selected by the useStaticGeometry toggle.
Collect Rigidbody3D components and extract their collider geometry (box, sphere, cylinder, capsule). This avoids loading separate visual meshes and stays in sync with the physics setup.
NavMeshSurface surface = GetComponent<NavMeshSurface>();

// Use rigidbody colliders as source geometry
surface.useStaticGeometry = true;

// The inspector button "Add All Geometry" populates this list automatically
// Alternatively, populate it in code:
foreach (var rb in FindObjectsOfType<Rigidbody3D>())
    surface.staticGeometry.Add(rb);

// Filter by layer so walkways go in but trigger volumes stay out
surface.GeometryLayers = LayerMask.Everything;

Baking the NavMesh

1

Add NavMeshSurface

Add NavMeshSurface to a scene-level GameObject. The origin of this object becomes the local coordinate frame for pathfinding.
2

Populate geometry

Click Add All Geometry in the Inspector to auto-fill staticGeometry (or meshGeometry) with every eligible object in the open scene.
3

Tune build settings

Adjust agentRadius, agentHeight, agentMaxSlope, and cellSize to match the characters that will walk this surface.
4

Rebuild

Click the Rebuild button in the Inspector. The engine calls RebuildNavMesh(), which voxelises the geometry with DotRecast’s RcBuilder, partitions regions, builds the detail mesh, and stores a tiled DtNavMesh on the component.
// Trigger a bake at runtime (e.g. after procedural level generation)
GetComponent<NavMeshSurface>().RebuildNavMesh();
RebuildNavMesh() is synchronous and can take several hundred milliseconds for large scenes. Call it during a loading screen or offload it to a background task rather than invoking it during gameplay.
NavMeshAgent attaches to any GameObject that should navigate the baked mesh. During LateUpdate, it reads the crowd-simulated position from the DtCrowdAgent and writes it straight to Transform.position.

Properties

NavMeshAgent agent = GetComponent<NavMeshAgent>();

agent.radius               = 0.5f;   // Physical radius used for crowd separation
agent.height               = 1.0f;   // Agent height for path clearance
agent.maxAcceleration      = 8.0f;   // Acceleration (m/s²)
agent.maxSpeed             = 3.5f;   // Maximum movement speed (m/s)
agent.collisionQueryRange  = 5.0f;   // Radius for detecting nearby crowd agents
agent.pathOptimizationRange = 15.0f; // Look-ahead distance for path shortcutting

Assigning a Surface

Each NavMeshAgent must reference the NavMeshSurface it navigates. Set the Surface property in the Inspector or at runtime:
agent.Surface = FindObjectOfType<NavMeshSurface>();
When Surface is set, the agent is automatically registered with the crowd (NavMeshSurface.RegisterAgent) as soon as the surface is ready. On OnDisable the agent is cleanly unregistered.

Moving to a Destination

Use NavMeshSurface.MoveToTarget to send a specific agent to a world-space position. Because the nav mesh is built in the surface’s local coordinate space, convert the world-space destination first:
NavMeshSurface surface = agent.Surface;
Vector3 worldDestination = player.Transform.position;

// Convert world position to surface local space before passing to MoveToTarget
Vector3 localPos = surface.Transform.InverseTransformPoint(worldDestination);

RcVec3f rcPos = new RcVec3f(
    (float)localPos.x,
    (float)localPos.y,
    (float)localPos.z);

surface.MoveToTarget(agent, rcPos, adjust: false);
Setting adjust: true asks the crowd to match a target velocity rather than seek a point — useful for kinematic steering or formation movement.
NavMeshSurface also exposes MoveAllToTarget(pos, adjust) to command every registered agent to the same destination simultaneously — handy for group rushes or horde AI.

Complete Example: Enemy Following the Player

The following script ties together everything covered above. The enemy uses a NavMeshAgent to chase the player, updating the destination every second to keep the path fresh without hammering the crowd each frame.
using Prowl.Runtime;
using DotRecast.Core.Numerics;

public class EnemyChase : MonoBehaviour
{
    [SerializeField] private NavMeshSurface navSurface;
    [SerializeField] private GameObject     player;

    private NavMeshAgent _agent;
    private float        _updateTimer;
    private const float  UpdateInterval = 1.0f; // re-path every second

    public override void Awake()
    {
        _agent = GetComponent<NavMeshAgent>();
        _agent.Surface = navSurface;

        // Tune agent movement
        _agent.maxSpeed        = 3.5f;
        _agent.maxAcceleration = 12.0f;
    }

    public override void Update()
    {
        if (player == null || navSurface == null || !navSurface.IsReady)
            return;

        _updateTimer += Time.deltaTimeF;
        if (_updateTimer < UpdateInterval)
            return;

        _updateTimer = 0f;
        SetDestination(player.Transform.position);
    }

    private void SetDestination(Vector3 worldPos)
    {
        // Convert world position to NavMeshSurface local space
        Vector3 localPos = navSurface.Transform.InverseTransformPoint(worldPos);

        RcVec3f rcPos = new RcVec3f(
            (float)localPos.x,
            (float)localPos.y,
            (float)localPos.z);

        navSurface.MoveToTarget(_agent, rcPos, adjust: false);
    }
}
1

Bake the surface

Add a NavMeshSurface to an empty root GameObject, populate geometry, and click Rebuild.
2

Add NavMeshAgent to the enemy

Add the NavMeshAgent component to your enemy GameObject. Assign the NavMeshSurface reference in the Inspector.
3

Attach the script

Add the EnemyChase script and assign the navSurface and player fields.
4

Play

Enter Play mode. The enemy will path toward the player, recalculating the route once per second. Crowd avoidance keeps multiple enemies from stacking on top of each other.

Architecture Reference

DotRecast (Recast)

Voxelises geometry, partitions regions, and builds the polygon navigation mesh. Invoked during RebuildNavMesh() via RcBuilder.BuildTiles.

DotRecast (Detour)

A* pathfinding on the tiled polygon mesh. DtNavMeshQuery finds nearest polygons and computes straight-path corridors for each agent.

DtCrowd

Simulates all registered agents simultaneously, handling local steering, collision avoidance, and velocity smoothing. Updated every frame inside NavMeshSurface.Update.

NavMeshSurface

Prowl wrapper that owns the DtNavMesh, DtNavMeshQuery, and DtCrowd instances, exposes the BuildSettings struct, and bridges agent transforms to the crowd simulation.

Build docs developers (and LLMs) love