Navigation performance
Separate search, map update, and baking costs; measure a query.
Optimize the stage you measured. Fewer polygons will not fix assigning every agent a target every frame, and background baking does not remove source-geometry parsing costs.
Separate five costs
| Stage | Measure | First change to compare |
|---|---|---|
| Source-geometry parsing | Parsing-call time separately from baking. | Restrict source areas and types; use simple collision or simplified geometry. |
| Baking | Time until a result is ready, separately from map publication. | Check area size, CellSize, CellHeight, and required passages. |
| Map updates | Server processing when regions and connections change. | Stop changing static geometry repeatedly; reduce unnecessary edges. |
| Path search | Query-call duration and queries per frame. | Remove redundant queries; test reachable and unreachable goals. |
| Avoidance | Server time with avoidance on/off in the same scenario. | Enable only where needed; check neighbor counts and prediction horizons. |
Server time is not search time
Performance.GetMonitor(Performance.Monitor.TimeNavigationProcess) returns seconds: multiply by 1000 for milliseconds. In 1.0.0 the main loop accumulates the maximum duration of 2D and 3D navigation processing over the statistics refresh interval, approximately one second. It is neither an average per frame nor a measurement of the latest MapGetPath().
Time a synchronous query against a published map around the call itself. This file also provides a 2D counter snapshot:
using Reckfell;
public static class NavigationMeasurements
{
// Invoke after the map has published its geometry; this measures one synchronous query.
public static (Vector2[] Path, ulong Microseconds) Query2D(
Rid map, Vector2 start, Vector2 target, uint layers = 1)
{
ulong before = Time.GetTicksUsec();
Vector2[] path = NavigationServer2D.MapGetPath(map, start, target, true, layers);
return (path, Time.GetTicksUsec() - before);
}
public static (int Maps, int Regions, int Polygons) Snapshot2D() => (
NavigationServer2D.GetProcessInfo(NavigationServer2D.ProcessInfo.ActiveMaps),
NavigationServer2D.GetProcessInfo(NavigationServer2D.ProcessInfo.RegionCount),
NavigationServer2D.GetProcessInfo(NavigationServer2D.ProcessInfo.PolygonCount));
}Call Query2D with a valid map after the intended geometry is published. Microseconds is elapsed call time in microseconds, including wrapper overhead and any waiting; a short call may return 0 because of timer resolution. Check Path normally: having points does not prove arrival at the goal.
For comparison, warm up and then run repeated identical queries without printing inside the measured section. Record the build, hardware, map, mask, query count, and result distribution. Compare the median and high percentiles. The executed example confirms that the wrapper works; it does not establish engine speed or an advantage over another engine.
Do not reset paths without a reason
Assigning TargetPosition again requests replanning even when the value is unchanged. For a moving goal, retain the last submitted position and update after meaningful movement or on a schedule. Choose the threshold for gameplay needs; an excessive threshold makes pursuit less responsive.
Keep regular path following: limit new target assignments, not the required GetNextPathPosition() call in the physics loop. Spread new assignments for large groups over frames while keeping reaction delay acceptable.
IsTargetReachable() can update agent navigation. It is not a separately guaranteed cheap preflight check. If a path is needed anyway, use its result and endpoint instead of a second independent search.
Reduce geometry without losing passages
Baking does not need every detail of the visible model. A simpler source reduces parsing work; validate bake-setting changes on narrow passages, steps, and region boundaries. Increasing 3D CellSize/CellHeight lowers voxel-grid resolution but may remove a required passage. Character dimensions must remain correct.
Scaling is not a substitute for simplifying a model: it does not reduce source triangle count. However, claiming that navigation always ignores scale is wrong: the checked 2D region-scale example changed route bounds. Keep source dimensions, transforms, and map settings consistent; baking.
For costly region connections, compare EdgeMergeCount, EdgeConnectionCount, and EdgeFreeCount before and after a change. Matching grid keys and additional proximity connections are different mechanisms. Do not blindly increase cell size or margins: surfaces that should remain separate may connect. Executed connection examples.
Confirm an improvement
- Keep a reproducible scenario and baseline measurements.
- Change one source of cost.
- Repeat under the same conditions and check path correctness.
- Include unreachable goals and group-wide agent updates: an easy single route may hide the problem.
- Check total frame time. Fast path search does not guarantee fast physics, animation, or rendering.
The query wrapper, layer mask, and aggregate counters were executed on 1.0.0. This measurement procedure is not a published benchmark; crowd load, asynchronous baking, and visualization costs were not measured here.