Skip to content

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.

  • 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.
  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.

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

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

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
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
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.

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
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.

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}.

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.

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.

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.

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.

  • 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.
Terminal window
# 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.