# Navigation and paths (ai.navigation) Paths for characters and units, computed from the live collision of your game: layered floors, agent size, slopes, steps, moving blockers. Readable offline: `gripforge docs ai-navigation`, or the MCP tool `gripforge_docs` (topic `ai-navigation`). ## What it is for This is the path search of the catalogue: a kit or a game never writes another one. It bakes a layered grid from the collision boxes and triangle meshes registered in the physics port, finds a route between two points for an agent of a given size, and keeps a transient path per actor that it hands out waypoint by waypoint. It never moves anyone: the actor's own collision controller applies the motion, and nothing is teleported. Who uses it: - `npc.core`: NPC goals follow its paths (installing `ai.navigation` is what turns path following on). - `character.top_down`: click to move (the player is the actor, around walls and through doors). Without the service, or with `clickPath` set to straight, the path is the straight line. - `moba.lanes`: with `pathfinding` set to navigation, lanes ask for `world.navigation` paths; its default stays its map grid, since a bake of a large MOBA map costs seconds per route against milliseconds for the grid. - `units.command` keeps its own flow field on purpose: one search from a goal shared by every unit of a group order. It requires only `game.engine`. Visual models do not become colliders: a replacement model must bring its real collision through the physics port. ## Turn it on ```text gripforge_gamekit_install { project_id: "gkp_…", id: "ai.navigation" } gripforge_gamekit_configure { project_id: "gkp_…", id: "ai.navigation", config: { "agent": { "radius": 0.5, "height": 2, "maxSlope": 40, "stepHeight": 0.4 } } } ``` Only the keys that differ from the defaults need to be written. The full reference is `gripforge kit api ai.navigation`. ## Settings and defaults | Key | Default | Meaning | | --- | --- | --- | | `agent` | radius 0.35, height 1.8, maxSlope 45, stepHeight 0.35 | the default agent: capsule radius and total height in metres, steepest slope in degrees, highest step | | `cellSize` | 1 | grid cell in metres (0.25 to 4); reduce it for narrow passages | | `padding` | 8 | metres around the requested endpoints baked with them | | `ground` | `true` | include the `world.ground` height surface (for analytic-ground controllers); `false` for collision-only interiors or terrain with holes | | `approach` | 3 | a goal inside a body (a parked car) or off the floor ends at the nearest reachable floor within this distance; 0 means exact endpoints | | `smooth` | `true` | straighten grid steps into straight segments checked like baked edges | | `retrySeconds` | 0.5 | an actor whose route is not ready asks again when its goal or the static world changes, otherwise at most this often | | `maxCells`, `maxLayers` | 16384, 8 | bounds of a bake (floors per cell up to 16) | | `plansPerTick` | 4 | routes planned per tick | | `sharedBakeCells` | 16384 | a world of at most this many cells is baked once for every agent | | `bakeTile` | 16 | the baked region is widened to whole tiles of this size; 0 bakes the region as asked | | `bakeCellsPerTick`, `bakeBudgetMs` | 1024, 4 | how `steer()` spreads heavy work over ticks while the actor waits | | `runtime` | `worker` | in a browser, where bakes and checks run: `worker` or `sync` (URL parameter `navRuntime=sync|worker` overrides) | | `layerOverflow` | `block` | Rust: a cell with too many floors is made non-navigable (`block`) or the bake is rejected (`reject`) | Budgets are not moddable. ## The service `route(from, to, agent?)` answers at once with `ready` and the exact endpoints and waypoints, or `unreachable`, `outside`, `unavailable`, `budget_exceeded`. `steer(actorId, from, to, agent?)` keeps a path per actor and returns the next waypoint, or `waiting` while the bake is made (a click-to-move hero walks straight meanwhile; an actor whose goal moved by 2 m at most keeps its route). `forget(actorId)` drops a path and `invalidate()` throws the bakes away (needed after ground samplers were changed from outside). On the web, `request(from, to, agent)` answers a promise. Bakes are kept until the static world or the agent changes; a request inside an already baked region of the same agent reuses it. Moving bodies never cause a bake: they are prepared once per revision, and the next swept segment of a followed path is checked before it is used. A blocked segment requests a new route, and a door registered as a physics body (closed, removed or moved) is detected by itself. Terrain edits, scene changes and streamed chunks invalidate the bake. Saves keep NPC goals and the paths being followed, so a loaded game takes the same steps. ## Hook and event Hook `navigation.plan { actor, from, to, radius, height, maxSlope, stepHeight, approach }` runs before each plan: a tap may move the goal, change the clearance or the approach; cancelled, the route is `unreachable` with the reason `cancelled: `. ```json { "id": "giants_wide", "hook": "navigation.plan", "match": { "actor": "troll" }, "set": { "radius": 1.2, "height": 3.5 } } ``` Event `navigation.blocked { actor, status, reason }`: once, when a steered actor gets a route that is not ready. An NPC then stops rather than walk through an obstacle. A mod may set `cellSize`, `padding`, `ground`, `approach`, `smooth`, `retrySeconds` and `agent`. ## What it is not A layered sampled grid with cardinal connectivity, not Recast, not a polygon union, not a crowd solver and not a jump or climb planner. Each X/Z cell can hold several supported floors (bridges and interiors do not merge with the ground below). Thin walls and too little headroom are rejected by triangle tests; curved primitives are conservative box blockers, not walkable supports; moving bodies block but are not platforms. A square footprint around the capsule makes narrow passages conservative: reduce `cellSize`. Holes smaller than the cell need an explicit collision blocker. `unreachable` means no validated path inside the queried region, not proof that a distant detour does not exist. A bake is bounded by cells, layers and 200000 triangles; an oversized query fails explicitly. ## Engines | Engine | Support | | --- | --- | | Web | the service, with the worker runtime in a browser | | Rust (native player, libgfkits) | the same service, hook, event and save, checked against the web by parity fixtures | | Godot, Unity, Unreal | no navigation service of this kit; the collision data contract could feed other adapters | ## When it does not do what you expect | Symptom | Cause and fix | | --- | --- | | NPCs do not walk to their goal | `ai.navigation` is not installed, or the models have no collision in the physics port | | `unreachable` through a narrow gap | `cellSize` too coarse for the agent; reduce it or reduce `agent.radius` | | The hero walks straight at first | `waiting` while the bake is made; that is the design, the route arrives a tick or two later | | Paths ignore a changed ground | call `invalidate()` after changing a ground sampler from outside | | A goal inside a parked car fails | raise `approach`, or set it to 0 for exact endpoints and handle the failure | | Stairs are not climbed | `stepHeight` is lower than the stair, or the slope is over `maxSlope` |