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
Section titled “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
Section titled “Create and assign a tree”- Open Window → AI behaviour trees.
- Start from a preset (Guard, Coward or Sentry) or choose + Empty tree.
- Select nodes in the hierarchy. Use + Child, Duplicate, the arrow buttons and Delete to shape the tree, then edit the selected node’s settings.
- Set the initial blackboard values, perception, and optional idle, walk and run clips.
- Choose Apply tree. Edits stay in a local draft until you apply; Revert discards them.
- Assign the tree to a scene object or a combat enemy type.
- Press Play.
Applying and assigning use the ai.upsert and ai.assign commands, so validation and undo are
the same as for agents.
Presets
Section titled “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
Section titled “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
Section titled “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
Section titled “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
Section titled “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
Section titled “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
Section titled “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
Section titled “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
Section titled “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
Section titled “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
Section titled “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
Section titled “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 |
// 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
Section titled “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_agentsis 1–1024 (default 64);repath_secondsis 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
Section titled “Automate it”# Create a tree from the Guard preset and give it longer sightfeather-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 rooftopfeather-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 reachablefeather-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.