---
title: "NPC and enemy navigation chase and patrol behavior"
description: "An entity should move itself toward a point or a target without a player driving it. `NavAgentComponent` is the one navigation behavior; it steers through the existing mover over the recast navmesh (there is no navmesh-as-component and no second mover)."
engineVersion: v1.0.234
date: 2026-09-28
license: "(c) Gessa, proprietary. Cite with attribution to https://gessa.ai/docs/. Terms: https://gessa.ai/terms/."
canonical: https://gessa.ai/docs/knowledge/playbooks/npc-enemy-navigation/
---

# NPC and enemy navigation chase and patrol behavior

## When to use

An entity should move itself toward a point or a target without a player driving
it. `NavAgentComponent` is the one navigation behavior; it steers through the
existing mover over the recast navmesh (there is no navmesh-as-component and no
second mover).

## The recipe

1. Attach `NavAgentComponent` with `project_add_component`
   (`project.entity.component.set`):
   `{ "type":"NavAgentComponent", "enabled": true, "speed": 4,
   "arrivalRadius": 0.5, "destination": {"x":0,"y":0,"z":0} }`. `speed` and
   `arrivalRadius` are 0..1000.
2. To chase a specific entity, set `targetEntityKey` to that entity's project
   graph key; the agent follows it. To walk to a fixed point, set `destination`.
3. Give the agent a body: a `RenderableComponent` to be seen and a
   `ColliderComponent` so it collides. Do not add
   `CharacterMovementComponent`; the nav system already owns the mover.
4. For decisions (acquire target, switch patrol point, give up), author a script
   with `addTickHandler` `{ "handlerKey":"onThink" }`. Inside a GessaScript body
   use `ctx.world.find` / `ctx.world.query` to locate the player,
   `ctx.navigation.isReachable` / `ctx.navigation.findPath` to test the route,
   and patch the `NavAgentComponent` `destination`/`targetEntityKey` with
   `ctx.entity.patchComponent`.
5. For a patrol, keep a small list of points in a script variable and advance to
   the next when the agent is within `arrivalRadius`.

## Pitfalls

- `NavAgentComponent` needs a walkable navmesh in the world; over empty space or
  off-mesh, the agent cannot path. Confirm the ground is navigable.
- `targetEntityKey` is a project graph key (semantic `project_graph_key`), not a
  raw UUID; use the entity's key.
- Steering happens through the shared mover; do not stack a second movement
  component expecting additive motion.
- Path decisions belong in a throttled tick handler, not every frame of heavy
  work; keep per-tick queries bounded (SWE-agent ACI discipline: small, purposeful
  steps).

## Verify

- `engine_get_component_schema` on `NavAgentComponent` for the exact fields and
  bounds.
- `simulation_run` (`qa.run.start`) to prove the agent reaches its destination or
  closes on its target.
- `qa_capture_renderer_viewport` to see the agent move.
