Navigation layers: numbers and masks
Filter region access without off-by-one bit errors.
Navigation layers filter which regions a path query may use. They do not create a passage, disable physics collisions or define character clearance. Different character sizes need suitable navigation geometry, rather than only different masks.
Layer number versus mask
Interface layers are numbered 1–32. The C# NavigationLayers property is a uint; one layer is 1u << (number - 1).
| Selected layers | Mask |
|---|---|
| 1 | 1 |
| 2 | 2 |
| 4 | 8 |
| 1 and 4 | 9 |
| None | 0 |
Do not use layer number 4 as a mask: the value 4 selects layer 3. Shifting 1u << 4 selects layer 5. Use unsigned 1u for the highest bit and reject invalid numbers before shifting.
Save NavigationLayers.cs in a Reckfell 1.0.0 C# project:
using System;
public static class NavigationLayers
{
public static uint Bit(int layerNumber)
{
if (layerNumber < 1 || layerNumber > 32)
throw new ArgumentOutOfRangeException(nameof(layerNumber));
return 1u << (layerNumber - 1);
}
public static uint Enable(uint mask, int layerNumber) => mask | Bit(layerNumber);
public static uint Disable(uint mask, int layerNumber) => mask & ~Bit(layerNumber);
public static bool Contains(uint mask, int layerNumber) => (mask & Bit(layerNumber)) != 0;
}Enable(1, 4) returns 9; Disable(9, 1) returns 8. Updating an existing mask preserves its other bits. NavigationRegion2D/3D and NavigationAgent2D/3D also provide SetNavigationLayerValue(number, enabled), which accepts a layer number rather than a mask.
Configure access
- Prepare connected navigation regions with existing polygons/meshes. They must belong to the same map and have connected geometry.
- Give the first region layer 1 and the second layer 2. A region mask describes membership; a query mask selects allowed layers.
- Set query
NavigationLayers = 3to allow both regions, or 1 to allow only the first. A region is eligible when(regionMask & queryMask) != 0. - After region changes, wait for the updated map to be published. Changing only the next query mask does not require rebuilding geometry.
- Check the returned endpoint, rather than only whether points exist: with mask 1, a route towards the second region may stop at the first region boundary.
In the adjacent-rectangle fixture, mask 3 connects the start and target. Mask 1 produces a nonempty partial path, while mask 0 admits no region. This was checked separately in 2D and 3D.
When changes become visible
A query mask change applies to its next execution. Region changes pass through NavigationServer synchronization. A new map with MapGetIterationId(map) == 0 is not ready for queries. A nonzero ID establishes that a version has been published, but does not prove that newly submitted changes are included. Waiting exactly one frame is not a universal readiness guarantee.
See path query objects for a query that checks both endpoints.
Diagnose incorrect paths
- Masks overlap but no passage exists: check map assignment, boundaries and navigation region connections.
- A character crosses a physical wall: navigation layers do not replace physics; align navigation geometry with collisions and movement.
- A path exists but does not reach the target: an endpoint was projected onto an allowed surface, or the route is partial.
- A changed region still returns the old result: wait for the updated map version and issue a new query.
Executed checks cover layer numbers 1/4/32, preservation of other bits, rejection of 0/33, masks and partial paths in 2D/3D, and a 2D region-mask change after synchronization. Baking, editor UI, NavigationLink, agent movement and avoidance are outside this example's coverage.