Skip to content
Bit Quirky Behavior Trees and
State Machines

Search documentation

All topics and APIs

Close returns focus to Search.

What are you looking for?

Try a common topic or enter a complete API identifier.

Browse the reference instead

Esc closes · Tab reaches results · Enter opens the focused link

v0.6.0
Menu

Interrupt

Let a higher-priority branch take the agent away from running work the moment its condition becomes true, and let a gate drop its own branch when the fact stops holding.

Your guard is halfway along its patrol when a coin lands behind it. Without an interrupt it finishes the walk and then notices. With one, it turns around while the coin is still rolling.

A running branch keeps the agent. That is the right default, because work that gets abandoned every frame never finishes anything. It is also the reason a patrolling guard can walk past the player: the patrol action is Running, so the tree never gets back up to the sight check above it.

Dungeon Heist needs the other behavior. Guard2.bqbehavior puts catch, chase, and investigate branches above search, return, and patrol under one priority Selector. The sight and stimulus conditions in those higher branches each carry a Lower Priority abort, so they get re-read while patrol runs. A thrown coin becomes a stimulus, the check becomes true, and the guard drops its patrol mid-step.

  • A threat or opportunity that must beat whatever the agent is doing: the player comes into view, a distraction lands, a teammate calls for help.
  • A recovery branch that has to win instantly, like Racing Line’s vehicle recovery above ordinary racing.
  • Work that stops being valid while it runs, which is the Self case: stop chasing the moment you lose sight, rather than arriving at where the player used to be.
The patrol branch is running when the sight condition turns true, the Lower Priority abort hands the tick to the chase branch, and when the predicate goes false again the Self conditional drops the chase.
The patrol branch is running when the sight condition turns true, the Lower Priority abort hands the tick to the chase branch, and when the predicate goes false again the Self conditional drops the chase.

Two moments, frame by frame. DocumentationReactiveAborts.bqbehavior has a Selector at the root of the tree, named choose chase or patrol and labelled ROOT on the canvas, with the chase branch first and patrol the route second.

  1. Patrol holding the running status pill while the chase branch is dimmed
    Figure 1. Patrol holding the running status pill while the chase branch is dimmed

    The sight guard in the higher branch is unsatisfied, so the Selector fell through to patrol. Patrol carries the status pill at RUNNING and the chase branch is dimmed.

  2. Chase carrying the running pill with the patrol branch dimmed
    Figure 2. Chase carrying the running pill with the patrol branch dimmed

    player_visible becomes true. The guard condition is re-checked before the active node ticks, the Lower Priority abort hands the cursor to the chase branch on that same update, and the chase keeps RUNNING from there on. Nothing about the patrol branch is remembered.

  1. Open Assets/Platformer/Behavior/DocumentationReactiveAborts.bqbehavior and select the Selector called choose chase or patrol.

  2. Read the Reactive abort heading in the rail and its Abort control, set to Lower priority. The help line under it says what the runtime does: while a lower-priority child runs, higher-priority children are re-checked each tick.

  3. Confirm the branch order in Children: the chase Sequence first, patrol the route second. Priority is what makes an interrupt meaningful, so the branch that should win has to be above the branch that should lose.

  4. Select the condition player is visible inside the chase branch. Its rail holds variable, comparison, and value: the reactive opt-in lives on the Selector above it, and one opt-in covers every condition inside that Selector’s branches.

  5. Enter Play Mode, leave player_visible false, and let patrol start. Then set it true in the Live blackboard.

The Selector carrying Reactive abort set to Lower priority, with the chase branch above the patrol branch
Figure 3. The opt-in lives on the Selector. The order of the Children list decides who can interrupt whom.
Guard_2 is waiting out a patrol waypoint when the coin noise lands, and the suspicion and investigation branch takes the tick: the dwell holds first, and the look around at the landing spot follows it.
Guard_2 is waiting out a patrol waypoint when the coin noise lands, and the suspicion and investigation branch takes the tick: the dwell holds first, and the look around at the landing spot follows it.

Dungeon Heist, Guard_2 running Guard2.bqbehavior. The guard patrols, a coin lands, the stimulus check in a higher branch becomes true, and the investigation branch preempts the patrol while the Live tree shows the takeover beside the game.

The Dungeon Heist chapter walks the whole guard graph, including the catch branch above the chase.

Self abort is the other half of the family. Instead of a rival branch taking over, the branch’s own gate gives up.

  1. The Self conditional holding its chase child while the fact is true
    Figure 4. The Self conditional holding its chase child while the fact is true

    while visible is a Conditional with abort mode set to self. Its fact is true, so the chase under it runs.

  2. The chase child dropped, with the cursor back on the patrol route
    Figure 5. The chase child dropped, with the cursor back on the patrol route

    The player breaks line of sight. The gate is re-evaluated on this update, stops succeeding, and the chase is cancelled mid-run; the hosting Selector re-decides from its first child and patrol carries the pill again.

The Conditional’s rail with its condition variable and abort mode set to self, over its chase child
Figure 6. Self belongs to the Conditional. It cancels its own subtree; it never promotes a sibling.

Result

Success
Nothing here reports Success. Both modes are about stopping work, and the branch that takes over reports its own result.
Failure
A Self gate that stops succeeding refuses its child, so the Conditional reports Failure to its parent.
Running
After a Lower Priority takeover, the winning branch is the Running branch from that update onward.

The scan runs once per update, before the active node ticks.

Lower Priority

  • Opt in on the Selector through Reactive abort > Abort. That one setting covers every Condition and Member condition inside its branches; the conditions themselves carry no abort control.
  • The observing condition has to sit in a branch that has at least one lower-priority sibling, under a Selector with two or more branches. A Lower priority Selector with nowhere to preempt is reported by validation.
  • It fires only when the active node is inside a strictly lower-priority sibling branch of that Selector, and only when the condition has just become Success. A condition that was already true when the branch started does not fire again.
  • On firing, pending work owned by the interrupted branch is cancelled with a Lower Priority reason, the interrupted node and the causing condition are recorded for the Live view, and the cursor descends into the winning branch. Composite cursors, Repeat counts, Time Limit clocks, and Random Selector draws in the interrupted branch are reset.
  • With several conditions becoming true at once, the highest-priority branch wins, then the earlier authored condition within that branch.

Self

  • Opt in on a Conditional through abort mode > self. The Conditional has to sit inside a Selector branch, which is where the cancellation hands control back to.
  • Its fact is evaluated every update, whether or not the branch is the active one. It cancels when the fact stops succeeding while the active node is inside the guarded subtree.
  • On cancelling, pending work owned by that subtree is cancelled with a Self reason and the cursor re-enters the hosting Selector at its first child.
  • A Lower Priority takeover and a Self cancellation in the same update both happen, and the takeover wins the cursor, so a higher-priority branch is never delayed by a gate letting go.

Abort mode has two settings in this release: None and Lower priority on a Selector, none and self on a Conditional. There is no combined mode, so a branch that needs both puts a Self Conditional inside a branch of a Lower priority Selector, which is the shape DocumentationReactiveAborts.bqbehavior and Guard2.bqbehavior both use.

When it goes wrong

SymptomCheckFix
The higher branch never takes over.Read Abort on the Selector that owns both branches.Set it to Lower priority, then confirm the winning branch sits above the losing one.
The interrupt fires once and never again.Check whether the condition stays true the whole time.Nothing to repair. Firing needs a change to true, so let the fact go false before you expect another takeover.
The wrong branch wins.Read the Children list order on the Selector.Move the branch that should win above the branch that should lose.
Validation rejects a Lower priority opt-in.Look for a Selector with at least two branches and a lower-priority sibling below the observing branch.Add the lower branch, or set Abort back to None.
A movement request keeps running after the interrupt.Confirm the movement adapter implements the interruptible service contract.Handle cancellation in the adapter so the character stops when its request is cancelled.
Progress is lost every time the branch is interrupted.Composite cursors and decorator counters in the interrupted branch reset by design.Keep progress your agent must not forget in your own component, and let the graph own the decision.
Full-size image