User Guide#
Goal: Author behavior trees, attach them to USD prims, and debug their execution at runtime.
Prerequisites#
Omniverse Kit 110+
Enable omni.behavior.tree.bundle (pulls in
omni.behavior.tree.ui,omni.behavior.tree.core, andomni.behavior.tree.schema).
For Behavior Simulation nodes (Humans, Props), also enable omni.anim.behavior.bundle.
Adding Trees to Prims#
To execute a behavior tree on a prim:
Select a prim in the stage.
In the tree editor toolbar, click Add To Prim and choose the target prim.
This applies BehaviorTreeAPI to the prim and links it to the currently open behavior tree file.
The command validates that the prim satisfies any constraints declared by the tree’s node libraries
before applying.
You can also do this from Python using Kit commands (undoable):
import omni.kit.commands
from pxr import Sdf
omni.kit.commands.execute(
"ApplyBehaviorTreeAPICommand",
prim_path=Sdf.Path("/World/MyRobot"),
tree_file_path=Sdf.AssetPath("path/to/tree.json"),
)
Additional commands:
CreateBlackboardCommand(prim_path=None)— creates aBehaviorTreeBlackboardprim.CreateNodeLibraryCommand(prim_path=None)— creates aBehaviorTreeNodeLibraryprim.RemoveBehaviorTreeAPICommand(prim_path)— removesBehaviorTreeAPIfrom a prim.
Property Inspector#
When a prim with BehaviorTreeAPI or a BehaviorTreeBlackboard prim is selected, the
Properties panel shows a dedicated widget for editing behavior tree settings.
Behavior Tree Properties#
Selecting a prim with BehaviorTreeAPI applied shows the Behavior Tree section with:
Behavior Tree — the path to the
.jsonbehavior tree file. Use the tree editor to modify the tree structure. Click Edit Behavior Tree in the header to open the editor for this prim’s tree.Blackboard — relationship to a
BehaviorTreeBlackboardprim. Change this to point the tree at a different blackboard.Enabled — enable or disable automatic execution of this tree. The tree can still be ticked manually if needed.
Auto Restart — when checked, the tree automatically restarts after reaching a terminal status (success or failure).
Instance Override — per-prim port value overrides, described below.
Instance Overrides#
Multiple prims can share the same behavior tree file but need different port values — for example, two agents using the same patrol tree but with different wait durations or target positions. Instance overrides let you change port values on a specific prim without editing the shared tree file.
Click the + button on the Instance Override row to add an override. Each override row shows
the node path and port name (e.g. /Root/Wait:duration means the duration port of the /Root/Wait node),
the port type selector, and a value field. Modifier ports are also overridable: in the override
menu, expand a node to find a submenu per attached modifier listing its input ports. Overrides
are applied at tree creation time.
The top of the + menu also exposes a Variables submenu listing every key in the
tree’s Variables panel (a.k.a. local blackboard). Picking one adds a
row that overlays that variable’s seed value for this prim only — useful for per-prim tuning
of shared trees. Variable overrides serialize as localBlackboardOverrides in the
override JSON and are always constant values (no binding modes).
Blackboard Properties#
Selecting a BehaviorTreeBlackboard prim shows the Behavior Tree Blackboard section.
The blackboard is a weakly typed key-value store: any key can hold any type, and the user is
responsible for ensuring that the type stored in a key is compatible with the port that will
read it at runtime.
Use + to add a new entry and - to remove the selected entry. Each row has:
Key — the entry name that nodes reference through Blackboard Reference ports.
Type — the value type (Integer, Float, Boolean, String, Float2, Float3, etc.). Changing the type resets the value to the new type’s default.
Array — toggle to make the entry an array of the selected type.
Value — the initial value. Nodes can read and write this at runtime.
To use a blackboard, create a BehaviorTreeBlackboard prim (via the Create Blackboard command
or the UI) and link it to the tree prim through the omni:behavior:tree:blackboard relationship.
Nodes reference blackboard keys through Blackboard Reference ports. For example, a SetBlackboard
node can write a value, and a CheckBlackboard modifier can read it to control flow.
Variables (Tree-Local Blackboard)#
Every tree carries a hidden, per-instance key-value store separate from the externally-provided main blackboard. The editor calls this the Variables panel; the code, schema, and rest of these docs call it the local blackboard. The two terms refer to the same thing and are used interchangeably.
Authors seed it on the tree descriptor (it serializes as the localBlackboard
field in the tree JSON), and the runtime materializes a fresh copy per tree
instance — so two prims attached to the same tree file get independent local
state without any per-prim authoring.
Editing Variables in the editor#
Open the Variables tab in the editor’s inspector pane (next to Node Inspector). The panel works like the blackboard editor: + adds an entry, - removes the selected one, and each row has Key, Type, Array, and Value columns. Edits write to the tree descriptor’s seed, so every prim referencing the tree picks up the new variable at the next tree creation.
While the timeline is playing the panel switches to Variables (live) and shows the current per-instance values read-only.
Binding ports to Variables#
Bind a port to a variable by setting Blackboard Reference mode and choosing
Local in the scope dropdown. BlackboardRef("local", k) resolves to the
per-tree local blackboard at runtime; the same key written via a setOutput
(with the same scope) can be read back through any port bound to it.
Local vs. External#
Scope |
What it is |
When to use |
|---|---|---|
External |
The |
Data shared across prims, scenes, or external callers (e.g. perception writing to a blackboard read by several trees). |
Local (Variables) |
Per-tree-instance store seeded from the descriptor. Not addressable from outside the tree. |
Per-instance state that should not leak (counters, sampled goals, cached choices) — the default when you just need scratch storage scoped to one tree. |
Pattern: Variables as tree inputs#
A tree’s Variables panel doubles as its parameter interface. Define each tunable knob as a Variable, bind ports inside the tree to those Variables, then override per-prim from the property inspector. The shared tree file stays unchanged; per-prim differences live entirely in instance overrides.
For example, a wander tree might expose:
Inside the tree, the movement node’s target port binds to Point_m (Local
scope), the wander sampler’s radius port binds to WanderRadius, and the
locomotion node’s speed port binds to Speed. Per-prim tuning then
happens via Instance Override, without touching the tree file:
Two prims that share the same wander tree can differ entirely in their wander
radius and speed by overriding Variables:WanderRadius / Variables:Speed
on each prim.
Pattern: Sharing modifier output across the tree#
A modifier’s output port is normally readable only by the node the modifier is attached to (via the Modifier Output binding mode on that node’s input ports). To make a modifier’s output reachable from any node in the tree, set the modifier output’s binding to Blackboard with Local scope and pick a Variable key — the modifier now writes into that Variable each tick, and any port anywhere in the tree can read it via Local-scope Blackboard Reference.
Example using PopQueue: suppose a planner branch fills a Variable queue
targets with destinations (via PushQueue writing to
Variables:targets). A worker branch elsewhere in the tree has a “do-work”
action with a PopQueue modifier on it; PopQueue reads from
Variables:targets and writes the popped value to its item output. By
routing that item output to Variables:current_target, any other node
in the tree — even outside the worker subtree — can read the current target
via a Local-scope blackboard reference on its own input port. The Variable
acts as a tree-internal mailbox; the modifier-owner relationship no longer
gates who can see the value.
Runtime Debugging#
Attaching a Debug Target#
While the tree editor is open and the timeline is playing:
Click the Debug Target dropdown in the toolbar.
A list shows all prims on the stage that reference the currently open behavior tree file.
Select a prim to attach the debug overlay to that running tree instance.
If you select a debug target before pressing Play, the overlay automatically attaches when playback begins.
Execution Overlay#
Once a debug target is attached, the tree editor shows a live overlay:
In this example, the Selector’s first Sequence node shows a red X — its CheckBlackboard condition failed, so the Sequence itself failed without ticking its children. The Selector falls through to the second branch, which is actively running (green checkmarks).
Node status is shown as an icon beside the node: green checkmark for success, red X for failure. A light blue tint on the node background means it is currently executing.
Modifier status is shown purely by the tag color: blue means the modifier is executing, red means failure.
Terminal status icons (success/failure) hold briefly before clearing, so rapid transitions remain visible.
Live Port Values#
While a debug target is attached, the Node Inspector switches to a read-only view showing live runtime values for the selected node’s ports. Select any node or modifier in the tree to see its current input values. Values refresh automatically during playback.
Timeline Integration#
Trees managed by the USD runtime tick automatically during Play:
Trees start when the timeline begins playing.
Trees tick each frame with the simulation delta time.
Trees stop and reset when the timeline stops.
Set omni:behavior:tree:autoRestart to true on the prim to automatically restart
trees that reach a terminal status (success or failure).
Next Steps#
Behavior Tree API Reference — Full Python API reference, including programmatic tree building and manual execution.