# AI and navigation

> Author behaviour trees with sight, hearing and memory, assign them to objects or enemy types, and route agents across multi-floor levels.

Source: https://feather-engine.com/docs/gameplay/ai-and-navigation/

Feather's gameplay AI is authored data. A project stores reusable behaviour trees, each with its
own perception and blackboard defaults, and assigns them to scene objects or combat enemy types.
The editor, the CLI and MCP all edit the same `ai` data, and Play, exported games and
`game.simulate` run the same interpreter.

Navigation builds a walkable grid of the open level without a renderer. With layered floors on,
agents route across upper storeys, stairs, ramps, bridges and caves, and use authored links for
ladders, jumps and drops.

## What's included

- Behaviour trees with 21 node types: composites, decorators, conditions and actions.
- Three ready-made trees: **Guard**, **Coward** and **Sentry**.
- Perception: a sight cone with line of sight, close-range awareness, hearing and memory.
- A blackboard per agent that perception writes to and scripts can read and write.
- Script events when an agent sees, loses or forgets the player, or hears a noise.
- A layered navmesh with step and drop heights, and up to 256 off-mesh links.
- A flow field that steers crowds of enemies around walls toward the player.
- A live debugger that shows each agent's active node, blackboard, perception and route.

## Create and assign a tree

1. Open **Window → AI behaviour trees**.
2. Start from a preset (Guard, Coward or Sentry) or choose **+ Empty tree**.
3. Select nodes in the hierarchy. Use **+ Child**, **Duplicate**, the arrow buttons and **Delete**
   to shape the tree, then edit the selected node's settings.
4. Set the initial blackboard values, perception, and optional idle, walk and run clips.
5. Choose **Apply tree**. Edits stay in a local draft until you apply; **Revert** discards them.
6. Assign the tree to a scene object or a combat enemy type.
7. Press **Play**.

Applying and assigning use the `ai.upsert` and `ai.assign` commands, so validation and undo are
the same as for agents.

### Presets

| Preset | Behaviour |
| --- | --- |
| Guard | Patrols; attacks when close, chases what it sees, searches the last known position and investigates noises |
| Coward | Wanders between objects tagged `waypoint`; when hurt or when the player is close, emits `ai:help` and flees |
| Sentry | Stands still and emits `ai:alarm` whenever it sees the player (at most every 5 s); sight 40 m, 90° cone |

## Node types

The root is a single node. Composites and decorators hold `children`; other fields are used only
by the node types that list them.

### Composites and decorators

| Type | Parameters | Behaviour |
| --- | --- | --- |
| `selector` | — | Tries children in order every tick; the first that succeeds or runs wins, and a newly valid higher branch aborts a running lower one. Fails when all fail |
| `sequence` | — | Runs children in order and remembers the running one, re-checking the guard conditions before it every tick. Fails on the first failure |
| `parallel` | `policy` | Ticks every child. `all` (default) succeeds when all succeed and fails when one fails; `any` succeeds when one succeeds and fails when all fail |
| `inverter` | — | Swaps its child's success and failure |
| `cooldown` | `seconds` (0.05–600) | After its child finishes, fails for this long |
| `repeat` | `times` (0 forever, or 1–10,000) | Runs its child again each time it succeeds; fails if the child fails |

### Conditions

| Type | Parameters | Succeeds when |
| --- | --- | --- |
| `blackboard` | `key`, `op`, `value` | The key compares true. `op` is `eq`, `ne`, `lt`, `le`, `gt`, `ge`, `set` or `unset`; `eq` and `ne` also compare text, booleans and vectors |
| `has_target` | — | The agent remembers the player |
| `in_range` | `distance`, `key` | The target, or the vector in `key`, is within `distance` metres |
| `can_see` | — | The player is in the sight cone, in range and in line of sight now |
| `health_below` | `fraction` (0–1) | Health is below that fraction. Enemies use their health; objects use the `health` blackboard key |

### Actions

| Type | Parameters | Behaviour |
| --- | --- | --- |
| `move_to` | `key`, `position`, `speed`, `distance`, `seconds` | Walks the navmesh to a position or the vector in `key`; succeeds within `distance` (default 0.6 m), fails when unreachable or after `seconds` (0 = no limit) |
| `chase` | `speed`, `distance` | Runs at the target; succeeds within `distance` (default 1.5 m), fails without a target |
| `flee` | `speed`, `distance` | Runs from the target until `distance` away (default 15 m) |
| `patrol` | `points`, `tag`, `speed`, `seconds` | Loops through up to 64 points, or objects with `tag` sorted by name, pausing at each; keeps running and resumes where it left off |
| `attack` | `damage`, `distance` | Attacks within `distance` (default 2 m). Enemies use their archetype attack; objects deal `damage` (default 10) and emit `ai:attack` |
| `wait` | `seconds`, `random` | Waits, plus up to `random` extra seconds |
| `play_animation` | `clip`, `looping`, `seconds` | Plays a clip on the agent's model; succeeds after `seconds` |
| `emit_event` | `event` | Emits a script event with data `#{id, position}` |
| `run_function` | `function` | Calls the Rhai function `name(id)` next frame: the agent's own script if it defines it, otherwise every world script that does |
| `set_blackboard` | `key`, `value` | Sets a key; `null` removes it |

The interpreter is deterministic per agent. Only the nearest `max_agents` assigned agents think;
agents outside the budget wait until they enter it.

## Perception

| Field | Description | Range | Default |
| --- | --- | --- | --- |
| `sight_range` | Sight distance; 0 is blind | 0–500 m | 25 m |
| `fov_degrees` | Full width of the sight cone | 1–360° | 110° |
| `eye_height` | Eye height above the feet | 0–10 m | 1.6 m |
| `line_of_sight` | Colliders and thick smoke block sight | On/off | On |
| `close_range` | The player is noticed in any direction within this distance | 0–50 m | 1.5 m |
| `hearing_range` | Range for a noise of loudness 1; 0 is deaf | 0–1000 m | 30 m |
| `memory_seconds` | How long a seen player or heard noise is remembered | 0–600 s | 8 s |

### Blackboard keys written by perception

| Key | Value |
| --- | --- |
| `can_see` | The player is visible now |
| `has_target` | The player is remembered |
| `target` | The player's position while seen, otherwise the last known position |
| `target_distance` | Metres to the target |
| `last_known` | Where the player was last seen; remove it to forget |
| `seen_ago` | Seconds since the player was last seen |
| `awareness` | 0–1, fading over `memory_seconds` after losing sight |
| `heard`, `noise`, `noise_ago` | Whether a noise is remembered, where, and how long ago |
| `health` | Health fraction; live for enemies, set it yourself for objects |

A tree can define up to 64 initial blackboard values: numbers, booleans, text or `[x, y, z]`
vectors.

### Events

| Event | Raised when |
| --- | --- |
| `ai:saw_player` | The player comes into sight |
| `ai:lost_player` | The player goes out of sight; the position is the last known one |
| `ai:forgot_player` | Memory of the player runs out |
| `ai:heard_noise` | A noise is heard |
| `ai:attack` | An object agent attacks the player |

Events arrive through the normal script event system with data `#{id, position}`.

## Navigation

Open **Window → Navigation** to change settings and preview the walkable grid.

| Setting | Field | Range | Default |
| --- | --- | --- | --- |
| Enabled | `enabled` | On/off | On |
| Enemies route around walls | `enemies` | On/off | On |
| Agents navigate | `agents` | On/off | On |
| Layered floors | `multi_floor` | On/off | On |
| Cell size | `cell_size` | 0.5–4 m | 1 m |
| Max slope | `max_slope` | 0.05 (flat) to 1 (vertical) | 0.55 (about 47°) |
| Clearance | `agent_radius` | 0–5 m | 0.4 m |
| Head height | `head_height` | 0.5–20 m | 2.2 m |
| Step height | `step_height` | 0–2 m | 0.45 m |
| Drop height | `drop_height` | 0–20 m; one way | 1.2 m |
| Level margin | `margin` | 0–500 m | 40 m |
| Bounds | `bounds` | `[min_x, min_z, max_x, max_z]`; all zeros covers the level | Automatic |
| Flow radius | `flow_radius` | 5–400 m | 70 m |
| Flow refresh | `flow_interval` | 0.05–5 s | 0.3 s |

With layered floors, each grid cell can hold several walkable floors — terrain plus collider tops —
so an upper floor does not erase the ground below it. Neighbours connect across steps up to the
step height and along walkable ramps. Head height keeps clearance under ceilings, and a failed
layered route stays failed rather than falling back to a path through the floor below.

The grid includes terrain and static, non-sensor colliders. Structures spawned at runtime are added
while the game runs.

### Off-mesh links

Links connect two points for traversals the grid can't infer. Choose
**+ Ladder / jump / drop link**, set the `from` and `to` points, and select **Both ways** for
ladders. Leave it off for one-way drops. A level can have up to 256 links.

## Debug AI live

During Play, **Window → AI behaviour trees** becomes the **AI live debugger**. For each agent it
shows the current node path, blackboard, perception state, route, timeline and recent AI events.
Turn on **Viewport debug** to draw the selected agent's sight, target, noise and route, and
**Nearby navmesh** to draw the walkable floors around it.

## Extend it

| Function | Description |
| --- | --- |
| `ai_set(agent, key, value)` | Writes a blackboard key; `()` removes it |
| `ai_get(agent, key)` | Reads a blackboard key |
| `ai_tree(agent, tree)` | Switches the agent's tree for this Play session; an empty string disables it |
| `ai_active(agent)` | The agent's current node path, as text |
| `make_noise(at[, loudness])` | Makes a noise; loudness 1 carries each tree's hearing range |
| `find_path(x1, z1, x2, z2)` | A flat route as `[#{x, z}…]`; empty when unreachable |
| `find_path_3d(x1, y1, z1, x2, y2, z2)` | A layered route as `[#{x, y, z}…]` over bridges, storeys, stairs and caves |
| `nav_floor(x, y, z)` | The walkable floor height there, or `()` |
| `nav_walkable(x, z)` | Whether the ground there is walkable |
| `move_to(id, position, speed)` | Walks a script-driven character to a position |

```rust
// A world script: make a noise on T, and log when an agent spots the player.
fn on_key(key) {
    if key == "T" {
        make_noise(player_pos(), 2.0);
    }
}

fn on_event(name, data) {
    if name == "ai:saw_player" {
        print("Spotted by " + data.id);
    }
}
```

Use the `run_function` action to call your own Rhai function from a tree, and `emit_event` to
notify world scripts and Blueprints.

## Limits

- Up to 128 trees per project and 256 nodes per tree.
- Composites hold up to 32 children; decorators hold exactly one.
- Up to 64 blackboard keys per tree, each 1–48 characters, and up to 64 patrol points per node.
- `max_agents` is 1–1024 (default 64); `repath_seconds` is 0.05–10 s (default 0.5 s).
- A tree can't be removed while an object or enemy type is assigned to it.

## Automate it

```bash
# Create a tree from the Guard preset and give it longer sight
feather-cli --connect .feather-session.json command ai.upsert \
  --json '{"preset":"guard","value":{"id":"gate_guard","name":"Gate guard","perception":{"sight_range":35}}}'

# Assign it to object 42 (use enemy_id to assign a combat enemy type)
feather-cli --connect .feather-session.json command ai.assign \
  --json '{"object_id":42,"tree":"gate_guard"}'

# Add a two-way ladder link between the yard and a rooftop
feather-cli --connect .feather-session.json command navigation.set \
  --json '{"patch":{"multi_floor":true,"links":[{"from":[4,0,10],"to":[4,8,10.5],"bidirectional":true}]}}'

# Check that the rooftop is reachable
feather-cli --connect .feather-session.json command navigation.query \
  --json '{"from":[0,0,0],"to":[6,8,12]}'
```

`ai.get` returns the settings, tree summaries, assignments, presets, every node type with its
parameters, the perception keys and the events; pass `id` for one complete tree. `navigation.get`
reports grid and layered navmesh statistics, and adds an ASCII map with `map_columns` or `floor`.
`game.simulate` reports each agent's active nodes, perception, route, blackboard and events, so you
can verify behaviour without a renderer. See [Simulation](/docs/agents/simulation/).
