This document focuses on extending BehaviorTree Studio and running it locally in a development environment.
- Node.js 22+ recommended
- npm (ships with Node.js)
npm installnpm run electron:devThis starts the React dev server and the Electron shell together.
npm run devThe web UI can export XML via downloads, but workspace file operations require the Electron shell.
Built-in node definitions live in src/data/nodeLibrary.ts. These definitions populate the Node Palette in the Nodes view.
- Open src/data/nodeLibrary.ts.
- Add a new object to the
nodeLibraryarray. - Save the file and reload the app if the dev server does not hot reload the palette.
{
id: 'move_to_target',
type: 'MoveToTarget',
category: 'action',
name: 'Move To Target',
description: 'Moves the agent to a target position',
fields: [
{
name: 'target_x',
type: 'number',
valueType: 'literal',
value: 0,
description: 'X coordinate of target'
},
{
name: 'target_y',
type: 'number',
valueType: 'literal',
value: 0,
description: 'Y coordinate of target'
}
]
}typeis the XML tag name that will be used on export.categorycontrols palette filtering and color.fieldsdescribe editable parameters shown in the Node Properties panel.
Subtrees are not defined in nodeLibrary.ts. They come from the workspace library file subtree_library.xml.
- Open a workspace.
- Switch to the Subtree Library tab in the Node Palette.
- Click New Subtree, then provide a name and optional description.
The new subtree is added to the library and becomes available in the current file.
- Open the Subtree Library tab.
- Click a subtree to open it in the editor.
- Modify nodes or variables as needed.
- Save to propagate updates to the library and all referencing files.
Nodes support custom instance names that replace the category label in the node display.
- Default Behavior: Nodes show their category (e.g., "control", "action") by default
- Custom Names: When you assign a name to a node, it replaces the category label
- Empty Names: Clearing the name returns to showing the category
- Select a node by clicking on it
- In the Node Properties panel (right side), find the "Node Name" section
- Enter a custom name or leave blank to show category
- The name updates immediately on the node
Node names are properly exported as the name attribute in BehaviorTree.cpp XML format:
<Sequence name="MainLoop">
<Action name="MoveForward"/>
<Condition name="CheckObstacle"/>
</Sequence>This is fully compatible with BehaviorTree.cpp v4.
Before (showing category):
Sequence
└─ control
After (with descriptive name):
Sequence
└─ MainLoop
Complex Tree:
Sequence (MainBehavior)
├─ Fallback (SafetyCheck)
│ ├─ Condition (BatteryOK)
│ └─ Action (ReturnToBase)
└─ SubTree (PatrolRoute)
// Node data now includes optional nodeName
interface BTNodeData {
// ... existing fields
nodeName?: string; // Custom instance name
}- Node names are stored in the node's data object
- Updated through
onUpdateNamecallback in NodePropertiesPanel - Persisted through undo/redo operations
- Export:
nameattribute is written first (before other attributes) - Import:
nameattribute is parsed and stored separately from regular fields - SubTrees: Also support the
nameattribute
BehaviorTree.cpp doesn't require specific attribute ordering, but BTstudio follows this convention:
nameattribute (if present)- Other attributes in order they appear in node definition
- Port mappings for SubTrees
If you want a specialized node UI beyond the default BTNode:
- Create a component in
src/components/. - Register it in the
nodeTypesmap in src/components/TreeEditor.tsx. - Set the
typeof the ReactFlow node to your new component key.
This is optional; most custom nodes only require a nodeLibrary definition.
If you add a new NodeField type or change how values are handled:
- Update
NodeFieldin src/types/index.ts. - Update rendering in src/components/BTNode.tsx.
- Update editing logic in src/components/NodePropertiesPanel.tsx.
Each component has a dedicated CSS file under src/components/. Global styles are in src/App.css and src/index.css.
Edit CATEGORY_COLORS in src/data/nodeLibrary.ts.
export const CATEGORY_COLORS: Record<NodeCategory | 'unknown', string> = {
root: '#F44336',
action: '#4CAF50',
condition: '#2196F3',
control: '#FF9800',
decorator: '#9C27B0',
subtree: '#00BCD4',
unknown: '#757575',
};