Source Engine NavMesh: Why NPCs Stop Getting Stuck on Stairs
Source Engine NPCs get stuck because legacy nodes don’t cover continuous walkable space. A built-in NavMesh of convex polygons stored in a .nav file gives reliable A* pathfinding, but it must be generated and kept in sync with map geometry.
04 Jul 2026, 17:49 UTC

NPCs that walk straight into walls, hesitate on stair edges, or fall through seemingly solid floors are a classic Source Engine problem. The useful takeaway is that Source’s built-in navigation mesh replaces the legacy node graph with convex polygonal walkable surfaces derived from map geometry, and that mesh is only as good as the .nav file you generate and keep in sync with the map.
What NavMesh changes in Source AI
Source’s NavMesh is a set of convex polygons that cover walkable areas. Convex means each polygon has no interior angle greater than 180 degrees, which makes local movement and adjacency tests cheap. The mesh is stored per map in a .nav file alongside the .bsp.
AI agents query the mesh through the base NPC navigation interface. In practice that means pathfinding is A* over the polygon graph rather than over hand-placed nodes. The result is continuous coverage across floors, ramps and stairs instead of discrete points that must be linked manually.
NavMesh is supported in Source 2007 and later branches. Behavior varies by branch and game config, so verify against the specific game you are shipping.
Generation workflow and where it runs
There are two common ways to produce a .nav file.
Offline generation uses VBSP with the -navonly flag. Run this from a command prompt or build script where you have read access to the .vmf/.bsp source and write access to the maps output folder. The tool reads compiled geometry and writes a preliminary navigation file. This is the preferred path for CI or release builds because it does not require a running server.
Runtime generation uses the in-game console command nav_generate. Run this in a developer session with the map loaded and console enabled. The command requires write permission to the game’s maps directory and a map that is not read-only. It scans the loaded BSP and writes or overwrites .nav. Risk: generating on a complex map can take noticeable time and will block the server thread while it runs.
After any geometry change you must rebuild the .nav file. An outdated mesh causes NPCs to attempt to walk through moved brushes or to ignore new walkable areas.
Integration with NPCs
NPCs do not need special entities to use NavMesh. When a .nav file exists for the current map, the navigation system prefers mesh queries. If the file is missing, the engine falls back to legacy node navigation where available.
Design considerations:
- Keep walkable surfaces simple. Tiny details, grates, and overlapping brushes increase polygon count and can create holes.
- Mark non-walkable areas with appropriate surface properties. The generator relies on collision and surface data to decide what is traversable.
- Avoid extremely thin corridors that are smaller than the NPC’s hull. The mesh will generate, but agents will clip.
Worked example: simple room validation
This example illustrates the loop without claiming a tested result.
1. In Hammer, create a minimal test room: a floor brush, four wall brushes, and a ceiling. Name the map .vmf.
2. Compile to BSP using your normal pipeline, then run VBSP with -navonly to produce an initial navigation file. Place the resulting .nav in the game’s maps folder.
3. Load the map in a developer build. Open the console and run nav_show 1. This toggles the navigation overlay so you can inspect polygon coverage.
4. Spawn an agent with npc_create . Give the NPC a target and observe whether its path follows the polygon edges visible in the overlay.
5. To verify change detection, modify the room geometry in Hammer, recompile, and run nav_generate in the loaded map. Compare the .nav file timestamp and size before and after to confirm the file was rewritten.
Expected checks: the overlay should cover the floor polygon, the NPC should move across the room without requiring manual node placement, and regenerating after a geometry change should update the file.
Trade-offs and limitations
NavMesh improves reliability over node graphs but adds cost. Memory use grows with map complexity because the polygon graph is stored in RAM. Compile and generation time increase with brush count and detail.
Generation can fail on overly complex geometry or maps with many tiny details, producing holes where no polygon is created. NPCs encountering a hole will stop or attempt to find an alternative route, which can look like getting stuck.
Because the mesh is static, any post-compile edit to geometry requires a new .nav. Shipping a map without rebuilding the .nav after a geometry tweak is a common source of navigation bugs.
Practical verification steps you can repeat on your own maps: use nav_show 1 to visualize coverage, regenerate with nav_generate after edits and confirm the .nav file changes, and place an NPC with npc_create on a map with and without a .nav file to compare navigation behavior.
Keep the .nav under version control with the BSP, document your generation command in the build notes, and treat NavMesh as part of the map asset rather than an optional debug feature.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.