Skip to content

NPC behavior trees ​

Add AI Brain to an NPC, choose Blank behavior tree or Guard behavior tree, then New behavior. Save creates the same reusable .opbrain asset used by state machines. Each NPC keeps its own memory, active branches, tasks, and cooldowns.

A state runs a tree as its task. Use states for modes such as Alive, Strike, and Dead; use a tree to choose work within a mode. Leaving the state cancels the entire tree before entry actions in the next state can run.

Authoring ​

Open Flow → Behavior, or use Edit behavior in the AI Brain Inspector. The graph fills the editor stage, and switching workspaces preserves its draft. Use the graph selector to open a tree. Add node, right-click, or Shift+A opens a searchable palette of control nodes, OpalScript actions and conditions, and built-in actions. New nodes stay where you place them. Drag a parent's output port to a child's input to connect them; dragging onto empty space opens a palette to create and connect a node in one edit.

The numbers on connections show branch order. Select the parent and use Branch order to move a child earlier or later. Priority children run in priority order; Sequence children run in execution order. Click a wire to select it, drag a selected endpoint to reconnect it, or press Delete to disconnect. Keyboard ports support Enter to start and finish a connection and Escape to cancel. Tidy arranges the graph; Fit frames it. All accepted graph edits participate in Undo and Redo.

Each node has one parent; the root has none. Every node must be reachable from the root. Cycles and recursive Subtree references are invalid and cannot be saved. Subtree references select another tree in the same Behavior asset.

NodeBehavior
SequenceRuns children in order, resuming its running child. Succeeds when all succeed; fails when one fails.
PriorityRechecks branch eligibility each decision and runs the first eligible child. A higher eligible branch interrupts lower work.
ConditionChecks NPC memory, a component field, or a pure OpalScript method. Returns success or failure.
ActionCalls an existing component action or an exposed OpalScript task and owns its cancellation.
WaitSucceeds after the specified amount of unpaused simulation time.
CooldownAllows one child, then blocks it for the specified time after that child succeeds.
SubtreeRuns another tree with the same NPC memory and its own invocation state.

Put a branch's conditions at the beginning of a Sequence. Priority can recheck these guards without running actions speculatively. For example, put an inRange == true Condition before attack windup. When it stops matching, the windup is canceled before a chase action starts.

A branch that fails is skipped while its leading guards remain eligible. It can retry after those guards become ineligible and eligible again, or when the enclosing tree invocation restarts. Changing an action parameter alone does not retry a failed branch. This keeps a failing higher-priority action from repeatedly interrupting valid lower-priority work.

Action arguments use typed fields. Each argument offers Value or Memory; the memory picker includes only compatible variables. Structured OpalScript records and lists get their own controls. Advanced JSON is reserved for unstructured or unrecognized values.

Native task failure stays a failure, and cancellation stays cancellation. Arbitrary Promises and Flow functions are not tree tasks; use exposed OpalScript tasks for asynchronous custom work.

A tree runs once. Its root success or failure feeds the owning state's Task completed or Task failed transition. To repeat, add a completed transition back to that state. Cooldowns survive these tree/state restarts within the same running Brain and advance with the NPC's simulation time. Restarting the entire Brain resets them. Failed or canceled work does not consume a cooldown.

Custom OpalScript nodes ​

Choose New OpalScript action… or New OpalScript condition… in the palette. Opal creates a project script and opens it in Script Studio. Return to Behavior to continue the same draft. Its public methods appear by name in the palette; actions become Action nodes, and pure boolean methods become Condition nodes. The same action picker is available for state tasks and entry/exit work.

For example:

csharp
class GuardActions : Component {
    [Property] bool Ready = true;

    [Pure] public bool CanAttack(float distance, float range) {
        return Ready && distance <= range;
    }

    public async Task Attack(float windup) {
        await Time.Wait(windup);
        // Apply the attack effect here.
    }
}

Save the script, add Can Attack and Attack from the palette, then configure their typed arguments. Bind distance to NPC memory and set a literal range. A Condition node can require either True or False. Transition conditions can call the same pure methods. The [Pure] check prevents conditions from changing game state while Priority rechecks candidate branches.

The NPC needs the script's component. Creating a script from an NPC's Behavior attaches it to that NPC; Add to NPC attaches an existing action's component. For a shared behavior opened directly from the Library, add the required script component to each NPC that uses it. Edit script beside an action or condition reopens its source. Successfully compiled changes refresh the available methods and argument controls when you return to Behavior.

An asynchronous Task keeps running until it finishes or its branch/state exits. Exiting cancels its awaited work before replacement work starts. Entry tasks are owned by the state too; exit actions must finish synchronously. No custom node registration or JavaScript wrapper is needed.

Guard behavior tree ​

Attach AI Mover and a Character Controller with Controlled By: AI. The template's Think state runs these priorities:

  1. While stunned is true, wait instead of pursuing or attacking.
  2. When inRange is true and the attack cooldown is ready, run attack windup.
  3. While alert is true, move toward targetX, targetY.
  4. Otherwise run the reusable patrol subtree between the patrol coordinates.

Set stunned, inRange, and alert with Flow's Set Behavior Boolean action. Set movement coordinates with Set Behavior Number. Perception and range calculations belong to your game; the template does not acquire targets or continuously retarget a running movement task.

After a 0.4-second windup, the tree requests Strike. The state transition checks that the NPC is still in range and not stunned. Connect AI Brain's State Entered event with stateId equal to strike to damage or a projectile action. The template does not inflict damage itself. Send the die behavior event to enter Dead and cancel all remaining tree work.

The template repeats successful tree runs. If every branch fails, it stops and shows a diagnostic. Correct the cause, then Restart Behavior, or author a task-failed transition with the recovery appropriate to your game.

Inspecting a running tree ​

Open the NPC's Behavior in Play and use Live to inspect active nodes, node outcomes, cooldowns, and interruption reasons alongside the owning state and memory. Pausing the NPC pauses its tree and cooldown clock. Stop, scene replacement, and component retirement cancel its owned tasks.

Tree definitions and typed action asset references use the existing server, IndexedDB, desktop-folder, and exported-player paths. The additive tree fields keep existing state-only .opbrain version 1 assets valid.