NPCs

Usage guide of npc.core and npc.pedestrian: the NPC document, behaviours, perception, navigation, the pedestrian crowd, settings and defaults, events, hooks (npc.spawn, npc.switch, npc.respawn, pedestrian.spawn, pedestrian.hit), engines.

Source: module-kits:kits/npc_core/usage/en.mdView Markdown source

Also read offline — CLI: grip-forge docs npcsMCP tool: gripforge_docs { "topic": "npcs" }

Non-player characters (npc.* kits) #

People that walk, see, hear and react, as data: the base kit, the ambient crowd of a city, and the conversations. Readable offline: gripforge docs npcs, or the MCP tool gripforge_docs (topic npcs).

What it is for #

KitWhat it adds
npc.corethe npcs collection: spawning in zones, locomotion on the ground or in the air, a sight cone and hearing, memory of the last known player position, a behaviour state machine (idle, wander, patrol, flee, investigate) other kits extend
npc.pedestriana crowd wandering the sidewalks of a road network, fleeing fast vehicles, knocked down when hit
npc.dialoguetyped lines and choices when the player talks to an NPC (described in the Quests and dialogue guide)

Enemies are NPCs with health and attacks: see the Enemies guide.

Turn it on #

gripforge_gamekit_install   { project_id: "gkp_…", id: "npc.core" }
gripforge_gamekit_configure { project_id: "gkp_…", id: "npc.core", config: { "maxNpcs": 48 } }
gripforge_gamekit_install   { project_id: "gkp_…", id: "npc.pedestrian" }

npc.core requires world.ground, spawn.entities, physics.port and game.engine; npc.pedestrian requires npc.core and a road network (world.roads, from the district kit). When the game has ai.navigation, NPCs follow collision-derived routes (closed or moving colliders invalidate them; an unreachable goal stops the NPC instead of cutting straight through walls).

The NPC document #

{ "schema": "gripforge.npc/1", "id": "guard", "faction": "town",
  "locomotion": { "type": "walk", "speed": 1.6, "runSpeed": 4 },
  "perception": { "sight": { "range": 18, "fov": 110 }, "hearing": 4, "memory": 8 },
  "behavior": { "initial": "patrol", "behaviors": ["patrol", "idle", "investigate", "flee"],
                "params": { "patrol": { "route": [[0,0,0],[10,0,0]], "wait": 2, "loop": true } } },
  "assets": { "model": "guard" },
  "spawn": { "zones": ["gate"], "count": 2 } }

assets.model names a model slot of the project; spawn.zones name zones of world.zones. locomotion.type is walk, static (it does not move) or fly; fly makes an NPC move freely in the air (it keeps the altitude it is sent to and never goes below the ground). A behaviour is registered by name: another kit adds its own (chase, hunt, ship_attack...), and the document lists which ones the NPC may use and which one it starts in. Placing: an archetype of kind npc with the component npc { doc }, or a spawn block in the documents of a scene (spawned on entering the scene, removed on leaving).

Settings and defaults #

KitKeyDefaultMeaning
npc.coretickHz10brain update rate (1 to 30); locomotion runs every tick
npc.coremaxNpcs64live NPC cap
npc.coredebugfalseemit npc.state on every transition (always on in tests)
npc.pedestriancount12pedestrians on the sidewalks (0 to 64)
npc.pedestrianwalkSpeed / fleeSpeed1.5 / 4m/s
npc.pedestrianfleeRadius8a vehicle closer than this and faster than 5 m/s makes them flee
npc.pedestrianvehicleImpactstrueweb: continuous car and pedestrian hit detection, knockdown, recovery
npc.pedestrianbloodEffectsfalseweb: stylized droplets and temporary stains on impacts
npc.pedestrianbodyLifetime8seconds a lethally hit pedestrian stays visible
npc.pedestrianlocalPopulationfalseweb: keep the crowd around the controlled player or car in a large world (populationRadius 100, recycleRadius 180)

Services, events, variables #

  • npc.locomotion: moveTo, stop, path following (a flying NPC keeps its altitude). npc.perception: canSee(entity, target), heard(entity), lastKnown(entity). npc.behavior: register a behaviour, spawn, switch.
  • pedestrian.ai: pedestrians(), count(), setDensity(n).
  • Events: npc.spawned, npc.state ({ entity, npc, from, to, target }), npc.despawned (reason despawn, died or removed), and from pedestrians npc.hit ({ npc, entity, by, speed, direction, lethal, blood }), which a wanted-level kit counts as heat.
  • Condition npc_state { npc, state }, verb npc_state.
  • Slot pedestrian (a rigged character, several allowed); a capsule is drawn until a model is bound.

Hooks and modding #

HookPayloadWhat a tap may do
npc.spawn{ npcId, faction, behavior }cancel one NPC (the others still spawn)
npc.switch{ entity, npcId, faction, from, to }change to, cancel (the NPC stays in from)
npc.respawn{ entity, npcId, faction, reason, delay }change delay, cancel (it does not come back)
pedestrian.spawn{ reason, x, y, z }cancel (reason crowd, replace or recycle)
pedestrian.hit{ npc, by, speed, damage }change damage, cancel (no damage, no knockdown, no panic)
{ "id": "brave_guards", "hook": "npc.switch", "match": { "faction": "town", "to": "flee" }, "set": { "to": "investigate" } }

Moddable: tickHz, maxNpcs, the pedestrian settings (bloodEffects aside), and the collection npcs (add, patch or remove documents).

Engines #

The brains, perception and the built-in behaviours are the same Rust logic on the web and in the native player, and are also delivered to Godot and Unity as files and to Unreal through the shared kit library. The local population of the pedestrians and the blood effects are web options; the native adapters keep the world-wide crowd. The hooks run on the web and in the Rust player.

When it does not do what you expect #

SymptomCause and fix
An NPC never changes behaviourthe behaviour is not listed in its document's behavior.behaviors
No NPC appearsthe spawn zone does not exist, maxNpcs is reached, or a tap cancelled npc.spawn
An NPC walks through wallsthe game has no ai.navigation: add it for routes that follow the collisions
No pedestriansthe world has no road network, or the pedestrian slot is unbound (a capsule is drawn but the project is not reported playable)
A pedestrian is never hitvehicleImpacts is off, or a pedestrian.hit tap cancels it