Behavior Tree API Reference#

Purpose: Complete Python API for the Behavior Tree extension. For the visual authoring workflow and runtime debugging, see the User Guide.

Overview#

The behavior tree system has two layers:

  • Factory (bt.get_factory()) — the standalone core. Creates trees, descriptors, node libraries, and blackboards. No USD or timeline dependency. You build and tick trees yourself. Use this for unit tests, headless simulation, or any context where USD is not involved.

  • USD Runtime (bt.get_bt_runtime()) — the USD integration layer. Maps BehaviorTreeAPI prims to tree instances, BehaviorTreeBlackboard prims to blackboards, and BehaviorTreeNodeLibrary prims to node libraries (for stage-scoped, one-off node libraries). During timeline play, the runtime creates these objects from their USD attributes and ticks all trees each frame. Available only when a stage is open.

Both are accessed through a single import:

import omni.behavior.tree.core as bt

Most users interact with the runtime through the tree editor UI. The factory API is needed for programmatic tree building and custom node implementations. The runtime API is useful when you need to look up the live tree, blackboard, or node library that corresponds to a specific prim.

Tree Execution#

USD-Managed Trees#

The primary way to run behavior trees is through the UI and USD:

  1. In the tree editor, open a behavior tree JSON file and add it to compatible prims.

  2. Press Play — the runtime creates tree instances and ticks them each frame with the timeline.

From Python, use Kit commands to set up tree prims:

import omni.kit.commands
from pxr import Sdf

# Apply BehaviorTreeAPI to a prim with a descriptor file
omni.kit.commands.execute(
    "ApplyBehaviorTreeAPICommand",
    prim_path=Sdf.Path("/World/MyRobot"),
    tree_file_path=Sdf.AssetPath("path/to/tree.json"),
)

# Create a blackboard prim
omni.kit.commands.execute("CreateBlackboardCommand")

During Play, access live trees through the runtime:

import omni.behavior.tree.core as bt

runtime = bt.get_bt_runtime()
tree = runtime.get_tree("/World/MyRobot")
if tree:
    bb = tree.get_blackboard()
    bb["target"] = "/World/Goal"

The runtime also manages stage-scoped node libraries (BehaviorTreeNodeLibrary prims) and can reload them at runtime.

Available commands:

Command

Description

ApplyBehaviorTreeAPICommand(prim_path, tree_file_path=None)

Applies BehaviorTreeAPI to a prim. Validates tree constraints before applying. Optionally sets the descriptor file.

RemoveBehaviorTreeAPICommand(prim_path)

Removes BehaviorTreeAPI from a prim.

CreateBlackboardCommand(prim_path=None)

Creates a BehaviorTreeBlackboard prim.

Manual Tree Execution#

For advanced use cases — unit tests, headless simulation, or trees not tied to USD — create and tick trees directly through the factory:

import omni.behavior.tree.core as bt

factory = bt.get_factory()

# Build a tree programmatically
lib = factory.get_node_library("omni.behavior.tree.core").get()
seq = lib.get_node_type_handle("Sequence")
wait = lib.get_node_type_handle("Wait")

desc = factory.create_descriptor()
desc.push(seq, "root")
desc.add(wait, "step1")
desc.add(wait, "step2")
desc.pop()

tree = factory.create_tree(desc)
while tree.get_status() != bt.NodeStatus.SUCCESS:
    tree.tick(delta_time=1.0 / 60.0)

Trees can also be created from JSON:

json_str = open("tree.json").read()
desc = factory.create_descriptor_from_string(json_str)
tree = factory.create_tree(desc)

Building a Patrol Tree#

This example builds the following tree programmatically, combining core composites with behavior simulation nodes:

Patrol tree: Sequence with Repeat, MoveTo with RandomNavMeshPoint, and Wait

The agent picks a random point on the NavMesh, walks there, pauses, and repeats forever.

Note

The omni.anim.behavior.tree extension must be enabled for its node library to be registered. The get_node_library() call and all type handles obtained from it will fail if the extension is not loaded.

import omni.behavior.tree.core as bt

factory = bt.get_factory()

# --- 1. Acquire node libraries and type handles ---

core = factory.get_node_library("omni.behavior.tree.core").get()
anim = factory.get_node_library("omni.anim.behavior.tree").get()

seq_type      = core.get_node_type_handle("Sequence")
wait_type     = core.get_node_type_handle("Wait")
move_to_type  = anim.get_node_type_handle("MoveTo")

repeat_type    = core.get_modifier_type_handle("Repeat")
rand_pt_type   = anim.get_modifier_type_handle("RandomNavMeshPoint")

# --- 2. Build the tree topology ---
# push() opens a scope for children, add() creates a leaf, pop() closes.

desc = factory.create_descriptor()

root = desc.push(seq_type, "patrol")       # Sequence
go   = desc.add(move_to_type, "go")        #   MoveTo
wait = desc.add(wait_type, "pause")        #   Wait
desc.pop()                                 # close Sequence

# --- 3. Set port values on nodes ---
# override_node_instance_port(node, port_name, value_type, value)

desc.override_node_instance_port(
    wait, "duration", bt.NodePortValueType.VALUE, 2.0
)

# --- 4. Attach modifiers ---
# attach_modifier(owner, modifier_type, name, abort_mode)

rpt = desc.attach_modifier(root, repeat_type, "loop")

# count=0 means repeat forever
desc.override_modifier_port(
    rpt, "count", bt.NodePortValueType.VALUE, 0
)

rand_pt = desc.attach_modifier(go, rand_pt_type, "pick_dest")

# --- 5. Wire modifier output → node input ---
# RandomNavMeshPoint produces "point"; feed it into MoveTo's "target".

desc.override_node_instance_port_modifier(
    go, "target", "pick_dest", "point"
)

# --- 6. Save and apply to a prim ---
# Character animation nodes run through the USD runtime, so serialize
# the descriptor to a file and attach it to the agent prim.

import omni.kit.commands
from pxr import Sdf

json_str = factory.serialize_descriptor(desc)
desc_path = "/path/to/patrol_tree.json"
with open(desc_path, "w") as f:
    f.write(json_str)

omni.kit.commands.execute(
    "ApplyBehaviorTreeAPICommand",
    prim_path=Sdf.Path("/World/Character/SkelRoot"),
    tree_file_path=Sdf.AssetPath(desc_path),
)

# --- 7. Play the timeline ---
# The runtime ticks all trees automatically each frame.

import omni.timeline
timeline = omni.timeline.get_timeline_interface()
timeline.play()

Creating Node Libraries#

Node libraries declare the node types available to tree descriptors. Libraries are owned by Omniverse extensions that manage their lifecycle.

C++ Extension#

For best performance, implement nodes as a C++ Carbonite plugin. Define nodes with BT_NODE_DESC (see C++ API) and register them via BehaviorNodeLibraryBuilder. A thin Python extension layer calls into the plugin to manage the lifecycle.

C++ plugin:

// nodes/GoToAction.inl
struct GoToAction : public IBehaviorActionNode
{
    BT_NODE_DESC(
        type = "GoTo",
        doc = "Move agent toward target position",
        constraints = BT_CONSTRAINTS("BehaviorAgentBaseAPI"),
        ports = BT_PORTS(
            valuePort("target", PortValue({ eFloat3, eSdfPath }, Variant{})),
            valuePort("speed", 1.0f)))

    NodeStatus onTick(IBehaviorActionNodeContext& ctx) override { /* ... */ }
};

// Extension.cpp
static UniqueNodeLibraryPtr g_library;

void registerNodeLibrary()
{
    g_library = BehaviorNodeLibraryBuilder("my_extension.nodes")
                    .registerNode<GoToAction>()
                    .validate()
                    .build();
}

void unregisterNodeLibrary()
{
    g_library.reset();
}

Python extension — acquires the plugin interface and calls register/unregister:

# extension.py
import omni.ext
import my_extension.bindings as _bindings

class MyNodesExtension(omni.ext.IExt):
    def on_startup(self, ext_id):
        self._iface = _bindings.acquire_interface()
        self._iface.register_node_library()

    def on_shutdown(self):
        self._iface.unregister_node_library()
        _bindings.release_interface(self._iface)
        self._iface = None

Python Extension#

For rapid prototyping or ad-hoc nodes that don’t need native performance, implement nodes in Python. The extension creates the library on startup, registers decorated node classes, and releases the library on shutdown.

# extension.py
import omni.ext
import omni.behavior.tree.core as bt

from .my_nodes import GoTo, Patrol

class MyNodesExtension(omni.ext.IExt):
    def on_startup(self, ext_id):
        factory = bt.get_factory()
        self._lib = factory.create_node_library("my_extension.nodes")
        bt.register_node(self._lib, GoTo)
        bt.register_node(self._lib, Patrol)

    def on_shutdown(self):
        self._lib = None  # releases the library

Node classes are defined in separate modules using the @bt.node decorator. Alternatively, the extension can load a script file with bind_node_library_with_script:

import pathlib

script = (pathlib.Path(__file__).parent / "my_nodes.py").read_text()
factory.bind_node_library_with_script(self._lib, script)

API Reference#

High-Level Helpers#

Function

Description

bt.get_factory()

Returns the singleton IBehaviorTreeFactory

bt.node(type, role=None, ports=[], doc="", keywords=[], constraints=[])

Decorator that declares and registers a node or modifier type on a class (subclass of ActionNode, CompositeNode, or Modifier) or a plain function

bt.value_port(name, value=None, accepted_types=[], enum=[])

Creates an input port descriptor with a literal default value

bt.blackboard_ref(name, key=None)

Creates an input port descriptor that references a blackboard key

bt.output_port(name, value=None, accepted_types=[])

Creates an output port descriptor (for modifier output wiring)

bt.register_node(lib, node_impl)

Registers a @bt.node-decorated class or function on a node library; returns the type handle

bt.serialize_variant(obj)

Serializes a Python object to a JSON string via the factory

bt.deserialize_variant(str)

Deserializes a JSON string to a Python object via the factory

bt.variant_to_string(obj)

Converts a Python object to its variant display string (empty string for None)

IBehaviorTreeFactory#

Singleton factory for creating all behavior tree objects. Acquired via bt.get_factory().

Method

Description

create_descriptor()

Creates an empty descriptor for programmatic building with push/add/pop

create_descriptor_from_string(serialized_str, node_libraries=[], overrides=[])

Deserializes a descriptor from JSON

create_blackboard()

Creates an empty blackboard

create_blackboard_from_string(serialized_str)

Deserializes a blackboard from JSON

create_tree(descriptor, blackboard=None)

Creates a runtime tree instance

create_node_library(name, visibility=NodeLibraryVisibility.PUBLIC)

Creates and registers an unversioned node library

create_node_library(name, version)

Creates and registers a versioned node library (version is a semver string, ext id string, or (major, minor) tuple)

create_node_library_from_string(serialized_str)

Creates and registers a node library from JSON

bind_node_library_with_script(node_library, script_string)

Associates a node library with a Python script string; updates declarations and implementations

get_node_library(name)

Returns a WeakNodeLibraryHandle by name

get_node_libraries()

Returns all registered libraries as weak references

serialize_descriptor(descriptor)

Serializes a descriptor to JSON string

serialize_blackboard(blackboard)

Serializes a blackboard to JSON string

serialize_node_library(node_library)

Serializes a node library to JSON string

serialize_descriptor_overrides(overrides)

Serializes descriptor overrides to JSON string

deserialize_descriptor_overrides_from_string(serialized_str)

Deserializes descriptor overrides from JSON

serialize_variant(data, extra_settings={})

Serializes a variant value to string

deserialize_variant(str, extra_settings={})

Deserializes a variant value from string

Note

Node libraries are automatically unregistered when the Python object is garbage collected. There is no explicit unregister_node_library method in Python.

IBehaviorTree#

Runtime tree instance. Created via factory.create_tree().

Method

Description

tick(delta_time=0.0)

Executes one tick; returns NodeStatus. Raises on ERROR.

reset()

Resets the tree to idle state

get_status()

Returns current root node status

get_last_error()

Returns the last error message

get_blackboard()

Returns the tree’s blackboard

get_descriptor()

Returns the tree’s descriptor

get_node(node_path)

Returns a writable IBehaviorTreeNode view by path, or None

get_nodes(node_path_or_pattern)

Returns a list of matching IBehaviorTreeNode views (supports wildcards)

get_subtree(subtree_node_path)

Returns a subtree IBehaviorTree instance by path, or None

get_subtree(subtree_node)

Returns a subtree IBehaviorTree instance by node context, or None

get_modifier(modifier_path)

Returns an IBehaviorTreeModifier view by path (format: <owner_path>:<modifier_name>), or None

IBehaviorTreeDescriptor#

Defines tree structure. Created via factory.create_descriptor() for programmatic building, or factory.create_descriptor_from_string() from JSON. See Manual Tree Execution for a complete example.

Builder API#

The descriptor supports a push/pop scope model: push opens a scope (adds children), pop closes it. add creates a leaf without opening a scope.

Method

Description

push(type, name)

Create a node and open its scope for adding children; returns NodeDescriptorHandle

push(type)

Create a node with auto-generated name and open its scope

push(node)

Re-enter scope for an already-created node

add(type, name)

Create a leaf node and add to current scope; returns NodeDescriptorHandle

add(type)

Create a leaf node with auto-generated name and add to current scope

pop()

Close the current scope

Topology Mutation#

Method

Description

get_root()

Returns the root NodeDescriptorHandle, or None

get_children(parent)

Returns list of child handles

get_parent(node)

Returns the parent handle

remove_child(parent, child)

Detach a child from its parent

get_node_handle(path)

Returns handle by node path

Node Instance Queries#

Method

Description

get_node_type_name(node)

Returns the type name of a node instance

get_node_name(node)

Returns the instance name

get_node_role(node)

Returns the NodeRole, or None

get_node_instance_port(node, name)

Returns the resolved NodePortDescriptor for a node, or None

get_port_override_names(node)

Returns names of ports with instance-level overrides

Port Overrides#

Method

Description

override_node_instance_port(node, name, type, value)

Override a port’s value for a specific node instance

override_node_instance_port_modifier(node, port_name, modifier_name, output_port_name)

Override a node port to reference a modifier’s output port

clear_port_override(node, port_name)

Revert a port to its type-level default

Modifiers#

Method

Description

attach_modifier(owner, modifier_type, modifier_name, abort_mode=ObserverAbortMode.NONE)

Attach a modifier to a node; returns ModifierHandle

detach_modifier(modifier)

Detach a modifier from its owner

get_modifiers(owner)

Returns list of modifier handles for a node

get_modifier_handle(path)

Returns handle by modifier path

get_modifier_owner(modifier)

Returns the owner node handle

get_modifier_type_name(modifier)

Returns the type name

get_modifier_name(modifier)

Returns the instance name

get_modifier_abort_mode(modifier)

Returns the ObserverAbortMode, or None

get_modifier_instance_port(modifier, name)

Returns the resolved NodePortDescriptor, or None

get_modifier_port_override_names(modifier)

Returns names of ports with overrides

override_modifier_port(modifier, port_name, type, value)

Override a modifier’s port value

clear_modifier_port_override(modifier, port_name)

Revert a modifier port to type-level default

Validation & Libraries#

Method

Description

validate()

Validates the descriptor; returns (bool, error_message)

get_node_libraries()

Returns referenced node libraries as weak references

are_libraries_valid()

Checks if all referenced node libraries are still alive

are_libraries_up_to_date()

Checks if referenced libraries match their snapshot versions from creation

IBehaviorNodeLibrary#

Collection of node type declarations and implementations. Create a library with factory.create_node_library(name), then register @bt.node-decorated classes with bt.register_node(lib, NodeClass). To load from a script, use factory.bind_node_library_with_script(lib, script_content).

Type Queries#

Method

Description

get_node_types()

Returns all declared node type handles

get_modifier_types()

Returns all declared modifier type handles

get_type_name(handle)

Returns the type name for a node or modifier handle

get_node_ports(handle)

Returns NodePortDescriptor list for a node or modifier type

get_node_role(handle)

Returns the NodeRole of a node type

get_node_type_handle(name)

Returns NodeTypeHandle by type name, or None

get_modifier_type_handle(name)

Returns ModifierTypeHandle by type name, or None

get_node_library_name()

Returns the library name

is_concrete(handle)

Checks if a specific type has an implementation

is_concrete()

Checks if the entire library has implementations for all types

is_hidden()

Returns True if the library visibility is HIDDEN

is_valid(handle)

Returns True if the node type handle is valid

copy_from(other, flags)

Copies declarations/implementations from another library

Metadata#

Method

Description

get_library_doc()

Returns the library documentation string

set_library_doc(doc)

Sets the library documentation string

get_node_doc(handle)

Returns documentation for a node or modifier type

get_node_keywords(handle)

Returns search keywords for a node type

get_node_port_doc(handle, port_name)

Returns documentation for a specific port

get_port_display_name(handle, port_name)

Returns the display name for a port

set_port_display_name(handle, port_name, display_name)

Sets a custom display name for a port

get_port_enum_values(handle, port_name)

Returns the enum choices for a port (for dropdown UI)

Constraints#

Method

Description

get_node_type_constraints(handle)

Returns constraint strings (USD schema/API names) for a node or modifier type

get_library_constraints()

Returns constraint strings for the entire library

Versioning & Migration#

Method

Description

get_version()

Returns the version string of this library

register_migration(to_version, fn)

Registers a migration function; to_version is a (major, minor) tuple, fn receives (descriptor, node_handle)

UI Customization#

Python-only methods for custom icons and port widgets (not persisted in JSON).

Method

Description

set_node_icon(handle, icon_path)

Sets a custom icon for a node or modifier type (supports carb tokens)

get_node_icon(handle)

Returns the custom icon path, or None

set_port_drawer(handle, port_name, draw_fn)

Sets a custom widget drawer for a port on a node or modifier type

get_port_drawer(handle, port_name)

Returns the custom widget drawer, or None

IBehaviorBlackboard#

Typed key-value store shared across tree nodes. Supports Python dict-like syntax.

Operation

Description

bb[key] = value

Sets a value (__setitem__)

bb[key]

Returns the value (__getitem__)

key in bb

Returns True if the key exists (__contains__)

del bb[key]

Removes a key (__delitem__)

len(bb)

Returns number of entries (__len__)

copy.deepcopy(bb)

Returns a deep copy (__deepcopy__)

clear()

Removes all entries

get_keys()

Returns all key names

get_event_name(event_type)

Returns the event name for a BlackboardEvent type

Node Callback Classes#

Base classes for implementing custom node behavior in Python. Subclass one of these and decorate with @bt.node().

Class

Description

ActionNode

Base class for leaf nodes that perform work

CompositeNode

Base class for branch nodes that control child execution

Modifier

Base class for modifiers (conditions, decorators) that wrap a target node

All three classes support these callbacks:

Callback

Description

on_init(self, context)

Called once when the node instance is created

on_tick(self, context)

Called each tick; must return NodeStatus. Can be a generator (yields imply RUNNING, return value is the final status).

on_reset(self, reason)

Called during normal execution when the node is reset (NodeResetReason). Not called during tree destruction.

on_destroy(self)

Called exactly once when the tree is destroyed. The sole teardown callback — use it for resource cleanup and undoing side effects.

Modifier additionally supports:

Callback

Description

on_check_condition(self, context)

Called to evaluate the condition; returns True or False. Used with abort modes.

Node Execution Contexts#

Context objects passed to node callbacks during execution.

_INodeExecutionContext (base)#

All contexts share these methods:

Method

Description

get_node_path()

Returns this node’s path in the tree

get_node_name()

Returns this node’s name

get_node_role()

Returns this node’s NodeRole

get_status()

Returns current node status

get_last_status()

Returns status from previous tick

get_last_reset_reason()

Returns why the node was last reset

get_last_error()

Returns the last error message

get_delta_time()

Returns the tick delta time

get_blackboard()

Returns the tree’s blackboard

get_tree()

Returns the tree instance

get_event_name(event_type)

Returns the event name for a TreeNodeEvent type

IBehaviorActionNodeContext#

Passed to ActionNode.on_tick(). Inherits all base context methods, plus:

Method

Description

get_input(name)

Returns input port value

IBehaviorCompositeNodeContext#

Passed to CompositeNode.on_tick(). Inherits all base context methods, plus:

Method

Description

get_input(name)

Returns input port value

get_child_count()

Returns number of children

get_child(child_index)

Returns child context (read-only)

tick_child(child_index)

Ticks a child with default abort behavior; returns NodeStatus

tick_child(child_index, abort_behavior)

Ticks a child with specified ChildAbortBehavior; returns NodeStatus

reset_child(child_index, reason=NodeResetReason.EXPLICIT)

Resets a child

get_child_last_reset_reason(child_index)

Returns why a child was last reset

IBehaviorModifierContext#

Passed to Modifier.on_tick() and Modifier.on_check_condition(). Inherits all base context methods, plus:

Method

Description

get_input(name)

Returns input port value

set_output(name, value)

Sets an output port value (for modifier-to-node port wiring)

tick_target()

Ticks the owned node; returns NodeStatus

reset_target()

Resets the owned node

get_target()

Returns the owned node’s context (read-only)

get_target_last_reset_reason()

Returns why the target was last reset

Runtime Node Views#

IBehaviorTreeNode#

Writable view of a node at runtime, returned by tree.get_node(). Inherits all _INodeExecutionContext methods, plus:

Method

Description

get_input(name)

Returns input port value

set_input(name, value)

Overrides an input port value at runtime

IBehaviorTreeModifier#

View of a modifier at runtime, returned by tree.get_modifier():

Method

Description

get_input(name)

Returns input port value

set_input(name, value)

Overrides an input port value at runtime

get_owner()

Returns the owning IBehaviorTreeNode view

get_blackboard()

Returns the tree’s blackboard

get_tree()

Returns the tree instance

get_name()

Returns the modifier instance name

get_path()

Returns the full modifier path

get_status()

Returns current execution status

get_last_status()

Returns previous execution status

Helper Functions#

Function

Description

bt.child_was_aborted(ctx, child_index)

Returns True if a child was aborted (for use in composite on_tick)

bt.target_was_aborted(ctx)

Returns True if the modifier’s target was aborted

Enums#

NodeStatus#

Value

Description

SUCCESS

Node completed successfully

FAILURE

Node failed

RUNNING

Node still executing

IDLE

Node not yet ticked

ERROR

Node encountered an error

NodeRole#

Value

Description

ACTION

Leaf node that performs work

COMPOSITE

Branch node controlling child execution

NodeResetReason#

Value

Description

EXPLICIT

Tree or node was explicitly reset

ABORTED

Node was aborted by parent or modifier

ChildAbortBehavior#

Controls how child abort signals propagate:

Value

Description

PROPAGATE

Propagate abort to children

IGNORE

Ignore abort signal

ObserverAbortMode#

Controls when a modifier’s condition triggers an abort:

Value

Description

NONE

No abort behavior

SELF

Abort this node when condition changes

LOWER_PRIORITY

Abort lower-priority siblings

BOTH

Abort both self and lower-priority siblings

NodePortValueType#

Value

Description

VALUE

Port holds a literal value

BLACKBOARD_REFERENCE

Port references a blackboard key

MODIFIER

Port references a modifier’s output port

NodeLibraryVisibility#

Value

Description

PUBLIC

Library is visible in the node catalog

HIDDEN

Library is hidden from the node catalog

TreeNodeEvent#

Value

Description

ON_STATUS_CHANGED

Fired when a node’s status changes

BlackboardEvent#

Value

Description

ON_ENTRY_ADDED

A new key was added to the blackboard

ON_ENTRY_CHANGED

An existing key’s value was modified

ON_ERASE_ENTRY

A key was removed from the blackboard

CloneNodeLibraryFlags#

Flags for copy_from():

Value

Description

NONE

Copy declarations only

KEEP_IMPLEMENTATION

Also copy node implementations

KEEP_METADATA

Also copy metadata (doc, keywords, etc.)

Type Handles#

Type

Description

NodeTypeHandle

Handle to a declared node type within a library

ModifierTypeHandle

Handle to a declared modifier type within a library

NodeDescriptorHandle

Handle to a node instance within a descriptor

ModifierHandle

Handle to a modifier instance within a descriptor

NodePortDescriptor#

Describes a port on a node or modifier type:

Field

Description

name

Port name (string)

type

NodePortValueType (VALUE, BLACKBOARD_REFERENCE, or MODIFIER)

value

Default value (Python object)

accepted_types

List of accepted variant type name strings

enum_values

List of enum choice strings (for dropdown UI)

is_output

True if this is an output port

is_array

True if this port accepts array values

output_port_name

Name of the referenced output port (for MODIFIER type)

BehaviorTreeDescriptorOverride#

Represents a port value override for a specific node instance:

Field

Description

id

Node instance identifier

name

Port name

port_value_type

NodePortValueType

value

Override value (Python object)

output_port_name

Referenced output port name (for MODIFIER type)

Port Helpers#

Port descriptors are created with bt.value_port(), bt.blackboard_ref(), and bt.output_port(), then passed to @bt.node(..., ports=[...]).

bt.value_port(name, value=None, accepted_types=[], enum=[])#

Creates an input port with a literal default value.

Expression

accepted_types

default

optional?

bt.value_port("x", "hello")

["string"] (inferred)

"hello"

no

bt.value_port("x", 1.0)

["double"] (inferred)

1.0

no

bt.value_port("x")

[] (any type)

None

yes

bt.value_port("x", 1.0, accepted_types=["double", "int64"])

["double", "int64"]

1.0

no

bt.value_port("x", None, accepted_types=["double"])

["double"]

None

yes

bt.value_port("mode", enum=["fast", "slow"])

["string"] (forced)

"fast" (first)

no

Rules:

  • When value is not None and accepted_types is empty, the type is inferred from the value.

  • When value is None, the port is optionalget_input() returns None until explicitly overridden.

  • When enum is provided, accepted_types is forced to ["string"] and the default is the first enum value (if not specified).

bt.blackboard_ref(name, key=None)#

Creates a port that references a blackboard key. If key is None, it defaults to the port name.

bt.output_port(name, value=None, accepted_types=[])#

Creates an output port. Output ports are set by modifiers via ctx.set_output(name, value) and can be wired to other nodes’ input ports using NodePortValueType.MODIFIER.

Examples:

@bt.node(type="MyAction", ports=[
    bt.value_port("speed", 1.0),                              # required float
    bt.value_port("label"),                                    # optional, any type
    bt.value_port("mode", enum=["fast", "slow"]),              # string dropdown
    bt.blackboard_ref("target", key="enemy_pos"),              # blackboard ref
])
class MyAction(bt.ActionNode):
    def on_tick(self, ctx):
        speed = ctx.get_input("speed")   # always has a value (1.0 default)
        label = ctx.get_input("label")   # None if not overridden
        return bt.NodeStatus.SUCCESS

Event System#

The behavior tree system fires events via carb.eventdispatcher. Event name and key constants are available as static properties on these classes:

Class

Constants

NodeLibraryEventNames

ON_NODE_LIBRARY_CREATED, ON_NODE_LIBRARY_REMOVED, ON_NODE_DECLARED, ON_NODE_LIBRARY_RELOADED, ON_TREE_MIGRATED

NodeLibraryEventKeys

NODE_LIBRARY_NAME, TYPE_NAME, IS_OVERRIDE, IS_MODIFIER, TYPES_ADDED, TYPES_MODIFIED, TYPES_REMOVED, DECLARATION_VERSION, SAVED_VERSION, CURRENT_VERSION

NodeEventPayloadKeys

NODE_PATH, NODE_NAME, OLD_STATUS, NEW_STATUS

BlackboardEventKeys

EVENT_TYPE, KEY, OLD_VALUE, NEW_VALUE

TreeEventNames

ON_TREE_CREATED, ON_TREE_DESTROYED

TreeEventKeys

PRIM_PATH

Built-in Node Types#

See Built-in Node Library for the complete list of composite nodes, action nodes, and modifier types with their ports.

Variant Type Registry#

Python-side type registry for UI metadata on variant types:

Function

Description

bt.register(info)

Registers a VariantTypeInfo for a variant type

bt.unregister(canonical_name)

Unregisters a variant type

bt.lookup(canonical_name)

Returns VariantTypeInfo for a type, or None

bt.get_all_canonical()

Returns all registered canonical type names

VariantTypeName provides constants for built-in types: NULL, INT64, DOUBLE, BOOL, STRING, FLOAT2, FLOAT3, FLOAT4, SDF_PATH, VARIANT_ARRAY.

USD API#

Schema Helpers#

Function

Description

set_tree_descriptor_file(prim, path)

Sets the tree descriptor file path on a prim with BehaviorTreeAPI

get_tree_descriptor_file(prim)

Returns the descriptor file path as Sdf.AssetPath

set_blackboard_data(prim, data)

Sets serialized blackboard data on a BehaviorTreeBlackboard prim

get_blackboard_data(prim)

Returns serialized blackboard data string from a blackboard prim

IBehaviorTreeUsdRuntime#

Acquired via bt.get_bt_runtime(). Available during Play. See Tree Execution for usage.

Method

Description

get_tree(prim_path)

Returns the IBehaviorTree for a prim, or None

get_blackboard(prim_path)

Returns the IBehaviorBlackboard for a prim, or None

get_tree_prim_path(tree)

Returns the USD prim path string for a tree

get_tree_prim_paths()

Returns prim paths of all tracked BehaviorTreeAPI prims

get_blackboard_prim_paths()

Returns prim paths of all tracked BehaviorTreeBlackboard prims

sync_tree(prim_path)

Re-reads USD attributes for a tree prim

get_node_library(prim_path)

Returns a WeakNodeLibraryHandle for a stage-scoped node library prim

get_stage_node_libraries()

Returns all stage-scoped node libraries

reload_node_library(prim_path)

Reloads a node library by re-reading its script file

collect_tree_constraints(descriptor_file_path)

Collects all constraint strings from node libraries used in a descriptor

validate_tree_constraints(prim_path, descriptor_file_path)

Validates whether a prim satisfies all constraints; returns ConstraintValidationResult

get_current_per_tick_data()

Returns UsdPerTickData (delta_time, current_time) from the timeline

USD Schema Attributes#

BehaviorTreeAPI (applied schema):

Attribute

Type

Description

omni:behavior:tree:descriptorFile

asset

Path to tree descriptor JSON file

omni:behavior:tree:blackboard

rel

Relationship to blackboard prim

omni:behavior:tree:enabled

bool

Enable/disable tree execution (default: true)

omni:behavior:tree:autoRestart

bool

Auto-restart on completion (default: false)

omni:behavior:tree:descriptorOverride

string

JSON serialized descriptor overrides

BehaviorTreeNodeLibrary prim type:

Attribute

Type

Description

scriptFile

asset

Path to Python node library script

BehaviorTreeBlackboard prim type:

Attribute

Type

Description

data

string

JSON serialized blackboard data

C++ API#

Headers are in include/omni/behavior/tree/. Include BehaviorTreeNodeUtils.h for the node definition macros and BehaviorNodeLibraryUtils.h for the library builder.

BT_NODE_DESC#

Declares node metadata (type name, ports, documentation) as a static method on a node class. Used by BehaviorNodeLibraryBuilder to auto-register nodes.

Uses named-parameter syntax — all parameters except type are optional and reorderable:

#include <omni/behavior/tree/BehaviorTreeNodeUtils.h>

struct MyAction : public IBehaviorActionNode
{
    BT_NODE_DESC(
        type = "MyAction",
        doc = "Move agent toward a target position",
        keywords = BT_KEYWORDS("movement", "navigation"),
        constraints = BT_CONSTRAINTS("BehaviorAgentBaseAPI"),
        ports = BT_PORTS(
            valuePort("target", PortValue({ eFloat3, eSdfPath }, Variant{}))
                .displayName("Target")
                .doc("Target position or prim path"),
            valuePort("speed", 1.0f)
                .displayName("Speed")
                .doc("Movement speed in m/s"),
            valuePort("mode")
                .strEnum("walk", "run")
                .displayName("Mode"),
            blackboardRef("status_key", "agent_status"),
            outputPort("distance", PortValue(kFloatingPointTypes, 0.0f))
                .displayName("Distance")))

    NodeStatus onTick(IBehaviorActionNodeContext& ctx) override { /* ... */ }
};

Parameters:

Parameter

Description

type = "..."

(required) The node type name as it appears in the editor and JSON.

doc = "..."

Documentation string shown in the node catalog.

ports = BT_PORTS(...)

Port declarations (see below). Wrap in BT_PORTS() to protect commas.

keywords = BT_KEYWORDS(...)

Search keywords for the node catalog.

constraints = BT_CONSTRAINTS(...)

USD schema/API names the target prim must satisfy.

is_coroutine = true

Enable coroutine mode: onTick runs on a worker thread and may yield() across ticks. Do not use on nodes that access USD (risk of deadlock).

debug_name = "..."

Internal debug name for logging.

Port Helpers (C++)#

Available in the omni::behavior::tree::kw namespace (auto-imported inside BT_NODE_DESC):

valuePort — input port with a typed default:

Expression

Result

valuePort("x", 1.0f)

Type inferred as ["double"], default 1.0, required.

valuePort("x", omni::string{})

Type inferred as ["string"], default "", required.

valuePort("x")

Untyped, default null, optional. getInput<T>() returns nullopt until overridden.

valuePort("x", PortValue(kFloatingPointTypes, 1.0f))

Explicit accepted types ["float", "double"], default 1.0.

valuePort("x", PortValue(kFloatingPointTypes, Variant{}))

Explicit types, default null, optional.

blackboardRefreference-style blackboard port (auto-deref). The runtime treats this as “the value at the slot the port references”: getInput returns the dereferenced value, setOutput writes to that slot. Use this when the node consumes or produces a typed value through a blackboard slot (e.g. SetBlackboard’s data port, CheckBlackboard’s read port).

Expression

Result

blackboardRef("target")

Key defaults to port name "target".

blackboardRef("target", "enemy_pos")

Key is "enemy_pos".

Pointer-style blackboard ports. When a node needs the slot identity itself rather than the dereferenced value (e.g. PushQueue / PopQueue invoke methods on the slot), declare a value port carrying a BlackboardRef instead:

valuePort("queue", BlackboardRef{}).displayName("Queue")

The handler receives the raw BlackboardRef via ctx.getInput<BlackboardRef> and resolves it to its target blackboard with ref.resolve(ctx.getTree()).

Blackboard scopes. A BlackboardRef carries (blackboard, key); the blackboard field selects a scope:

  • empty: the tree’s externally-provided main blackboard (tree->getBlackboard()).

  • "local": the tree’s per-instance hidden local blackboard, seeded from the descriptor at createTree time.

Authors seed the local blackboard via entry-level methods on the descriptor — setLocalEntry / getLocalEntry / getLocalKeys / eraseLocalEntry. The seed lives as a plain dictionary on the descriptor (no IBehaviorBlackboard instance, so it cannot be aliased into another tree’s main blackboard). Entries serialize to / from the descriptor JSON’s optional localBlackboard field. At createTree the factory clones the seed into a per-instance hidden blackboard owned by the tree; BlackboardRef("local", k) is the only way to reach it at runtime.

Note

In the editor UI this concept is surfaced as the Variables panel and the Variables submenu under Instance Override. Variables and local blackboard are interchangeable names for the same store.

outputPort — modifier output port:

Expression

Result

outputPort("value", PortValue(kFloatingPointTypes, 0.0f))

Typed output with default.

outputPort("value")

Untyped output.

Chaining methods on any port:

Method

Description

.displayName("Label")

Display name in the editor (otherwise uses port name).

.doc("...")

Tooltip documentation.

.strEnum("A", "B", "C")

Restricts to string enum choices. Default becomes the first value.

.makeArray()

Marks the port as accepting array values.

Pre-defined type sets:

Constant

Types

kFloatingPointTypes

float, double

kSignedIntegralTypes

int8, int16, int32, int64

kStringTypes

string, char*

eFloat2, eFloat3, eFloat4

Individual vector types

eSdfPath

USD prim path type

PortValue#

Wraps a default value and accepted types for port declarations:

Constructor

Behavior

PortValue()

Untyped, null default (optional port).

PortValue(T value)

Type inferred from value. Required port.

PortValue(acceptedTypes, value)

Explicit types. Required if value is non-null, optional if Variant{}.

BehaviorNodeLibraryBuilder#

Fluent builder for creating and registering a complete node library from C++.

#include <omni/behavior/tree/BehaviorNodeLibraryUtils.h>

static UniqueNodeLibraryPtr g_library;

void registerNodeLibrary()
{
    g_library = BehaviorNodeLibraryBuilder("my_extension.nodes")
                    .setLibraryDoc("Nodes for my extension")
                    .registerNode<MyAction>()
                    .registerNode<MyComposite>()
                    .registerModifier<MyModifier>()
                    .validate()
                    .build();
}

Method

Description

BehaviorNodeLibraryBuilder(name)

Create builder for an unversioned library.

BehaviorNodeLibraryBuilder(name, carb::Version{M, m})

Create builder for a versioned library (major.minor).

BehaviorNodeLibraryBuilder(name, extId)

Create builder with Kit extension id as version (e.g. "omni.foo-2.1.0").

setLibraryDoc(doc)

Set the library documentation string.

registerNode<T>()

Register a node class with BT_NODE_DESC. Type name, role, ports, doc, keywords, and constraints are all read from the descriptor.

registerModifier<T>()

Register a modifier class with BT_NODE_DESC.

validate()

Check that all declared types have implementations. Errors are deferred to build().

build()

Finalize and return UniqueNodeLibraryPtr. Logs errors and returns null on failure.