Ember
Loading...
Searching...
No Matches
Behavior Trees: A Complete Guide

An in-depth, plain-language guide to behavior trees: what they are, how they work, how nodes communicate, and how Ember helps you build them.


What Problem Does Ember Solve?

The Challenge

When building AI for games, robots, or simulations, you need to model decision-making and actions. Simple "if-then" code quickly becomes messy:

  • "If enemy visible, attack; else if health low, retreat; else patrol; but what if we're reloading? And what about the objective?"

Traditional approaches (state machines, decision trees, scripts) become hard to maintain as complexity grows.

The Solution: Behavior Trees

Behavior trees are a visual, hierarchical way to organize logic. Instead of tangled code, you build a tree of nodes where:

  • Each node has a clear role
  • The structure shows the flow of control
  • You can see the whole logic at a glance
  • Changes are local—edit one branch without breaking others

What Ember Does

Ember is a visual editor for behavior trees. It lets you:

  • Create trees by clicking and connecting—no coding
  • Edit existing XML behavior tree files
  • Visualize structure, navigate between trees, and manage multi-file projects
  • Validate trees for common errors before you run them

What Is a Behavior Tree?

Definition

A behavior tree is a hierarchical structure of nodes. It looks like an upside-down tree:

[Root]
┌────────┴────────┐
[Sequence] [Selector]
│ │
┌────────┼────────┐ ┌─────┴─────┐
[Condition] [Action] [Action] [Action] [Action]
  • Root: The top node; execution starts here
  • Branches: Control nodes (Sequence, Selector, etc.) that decide which children run
  • Leaves: Action, Condition, or SubTree nodes that do actual work

Where Did Behavior Trees Come From?

Behavior trees were popularized in video game AI (Halo 2, 2004) as a replacement for finite state machines. They spread to:

  • Robotics — Robot decision-making (navigation, manipulation)
  • Simulation — Training, testing, virtual agents
  • Automation — Workflow and task orchestration

Why a Tree?

Benefit Explanation
Modularity Each branch can be designed and tested separately
Reusability SubTrees let you reuse logic across trees
Readability The structure shows intent: "try A, then B, then C"
Reactivity Trees can re-evaluate from the root each tick, adapting to changes

How Nodes Communicate

Nodes communicate in two ways: through the tree structure (parent-child) and through a blackboard (shared data).

1. Parent–Child Communication (Control Flow)

Parents tell children when to run and receive their result.

Parent (Sequence)
"Run your first child"
Child 1 returns Success
"Run your next child"
Child 2 returns Failure
Sequence stops, returns Failure to its parent

Flow of control:

  1. A tick arrives at the root
  2. The root passes the tick to its children (according to its type)
  3. Children execute and return Success, Failure, or Running
  4. The parent uses that result to decide what to do next
  5. The result propagates back up the tree

2. Blackboard Communication (Shared Data)

Nodes that are not parent-child can share data through a **blackboard**—a shared key-value store.

┌─────────────────────────────────────────────────────────────┐
│ BLACKBOARD │
│ target_position: (10, 5, 0) │
│ enemy_visible: true │
│ health: 75 │
└─────────────────────────────────────────────────────────────┘
▲ ▲ ▲
│ write │ read │ read
│ │ │
[GetTarget] [MoveToTarget] [Attack]
(Action) (Action) (Action)

How it works:

  • Output port: A node writes a value to the blackboard (e.g. target = position)
  • Input port: A node reads a value from the blackboard (e.g. goal = target)
  • In-out port: A node reads and writes the same variable

In Ember, you connect nodes to blackboard variables by typing {variable_name} in the Properties panel.

3. Combined Example: Data Flow

Sequence
├── GetTarget → writes {target} to blackboard
├── IsTargetReachable → reads {target}, returns Success/Failure
├── MoveToTarget → reads {target}, moves there
└── Attack → reads {target}, attacks

The Sequence controls the order. The blackboard passes data between nodes.


The Tick-Based Execution Model

Behavior trees run in ticks. Each tick:

  1. The game/robot/simulation calls tick() on the tree
  2. The tick propagates from root to leaves
  3. Nodes execute and return a status
  4. The status propagates back to the root
  5. Next tick, the process repeats

Tick Flow Diagram

Tick 1:
Root (Selector)
→ Try Child 1 (Sequence)
→ Child 1.1 (Condition): Success
→ Child 1.2 (Action): Running ← Action needs more time
← Sequence returns Running
← Selector returns Running
(Tree is "busy"—Action still running)
Tick 2:
Root (Selector)
→ Resume Child 1 (Sequence)
→ Child 1.1: (already succeeded, may be skipped in reactive mode)
→ Child 1.2 (Action): Success ← Action finished
← Sequence returns Success
← Selector returns Success
(Tree completed successfully)

Reactive vs Non-Reactive

Mode Behavior
Reactive Each tick starts from the root. Higher-priority branches can preempt running nodes. More responsive to changes.
Non-reactive Resumes from Running nodes. More efficient, but less responsive to external changes.

Node Types in Detail

Control Nodes

Sequence

Runs children in order. Stops on first Failure. Succeeds only if all succeed.

Sequence
├── Condition A → Success → continue
├── Condition B → Success → continue
├── Action C → Running → return Running (wait for next tick)
└── (Action C finishes) → Success → Sequence succeeds

Use case: "Do A, then B, then C. If any step fails, abort."

Example: "Patrol"
Sequence
├── GetNextWaypoint (get next point)
├── MoveToWaypoint (walk there)
└── WaitAtWaypoint (pause, then repeat)

Flow diagram:

[Sequence]
┌─────┴─────┐
│ │
▼ ▼
[Child 1] [Child 2] ...
Success? ──► Run next
Failure? ──► Stop, return Failure
Running? ──► Return Running, resume next tick

Selector (Fallback)

Runs children in order. Stops on first Success. Fails only if all fail.

Selector
├── Condition A → Failure → try next
├── Condition B → Failure → try next
└── Action C → Success → Selector succeeds

Use case: "Try A. If that fails, try B. If that fails, try C."

Example: "Choose behavior"
Selector
├── IsEnemyVisible? → Attack
├── IsHealthLow? → Retreat
├── HasObjective? → GoToObjective
└── Patrol (default)

Flow diagram:

[Selector]
┌─────┴─────┐
│ │
▼ ▼
[Child 1] [Child 2] ...
Success? ──► Stop, return Success
Failure? ──► Try next child
Running? ──► Return Running
All failed? ──► Return Failure

Parallel

Runs all children at the same time. Succeeds or fails based on a threshold.

Parallel (success_threshold=2)
├── Action A → Success
├── Action B → Running
└── Action C → Success
→ 2 succeeded, threshold met → Parallel succeeds

Use case: "Do several things at once. Need at least N to succeed."


Decorator Nodes

Decorators have one child and modify its behavior.

Decorator Input Output
Inverter Success Failure
Failure Success
Running Running
Repeat(N) Run child N times Success when all succeed
Retry(N) Run child up to N times Success when child succeeds
Timeout(T) Run child, max T seconds Failure if time exceeded
ForceSuccess Any Success
ForceFailure Any Failure

Example: Inverter

"Inverter" makes a condition mean the opposite:
IsDoorClosed? → Success when closed
Inverter(IsDoorClosed?) → Success when OPEN

Leaf Nodes

Action

Performs an action. Returns Success, Failure, or Running.

  • Success: Action completed (e.g. reached destination)
  • Failure: Action failed (e.g. path blocked)
  • Running: Action in progress (e.g. still walking)

Condition

Checks something. Returns only Success or Failure (never Running).

  • Should be fast and side-effect free
  • Examples: IsEnemyVisible?, IsHealthLow?, HasAmmo?

SubTree

References another behavior tree. Enables modular design and reuse.

MainTree
├── Sequence
│ ├── SubTree(PatrolBehavior)
│ └── SubTree(CombatBehavior)

Node States Summary

State Meaning Who returns it
Success Task completed successfully All node types
Failure Task failed or cannot be done All node types
Running Task in progress, need more ticks Actions (and thus parents)

Real-World Example: Game Enemy AI

Root (Selector) "What should I do?"
├── Selector "Combat"
│ ├── Sequence "Attack"
│ │ ├── IsEnemyVisible? (Condition)
│ │ ├── IsInRange? (Condition)
│ │ └── Attack (Action, uses {target} from blackboard)
│ │
│ └── Sequence "Chase"
│ ├── IsEnemyVisible? (Condition)
│ ├── SetTarget (Action, writes {target})
│ └── MoveToTarget (Action, reads {target})
├── Selector "Survival"
│ ├── Sequence "Heal"
│ │ ├── IsHealthLow? (Condition)
│ │ ├── GoToHealthPack (Action)
│ │ └── UseHealthPack (Action)
│ │
│ └── Sequence "Flee"
│ ├── IsHealthCritical? (Condition)
│ └── RunAway (Action)
└── Patrol (Action) "Default: just patrol"

Flow: Combat has priority. If no combat, try survival. If no survival needed, patrol.


Blackboard in Depth

Port Types

Port Direction Example
Input Read only goal — "Where should I move?"
Output Write only target — "I found the enemy here"
In-out Read and write counter — "Increment this"

Scoping

Scope Visibility
Local Only within one tree
Shared Across SubTrees in a project
Global Project-wide

Example: Passing Data

Sequence
├── FindEnemy
│ Output: {enemy_position}
├── MoveTo
│ Input: {enemy_position} (reads what FindEnemy wrote)
└── Attack
Input: {enemy_position}

How Ember Helps You

Visual Editing

  • See the whole tree at once
  • Drag to pan, scroll to zoom
  • Right-click to add nodes
  • Select to edit properties

No Coding for Basic Use

  • Add nodes from the context menu
  • Edit IDs, names, and ports in the Properties panel
  • Connect to blackboard with {variable_name}

Validation

Ember checks for:

  • Missing SubTree references
  • Circular dependencies
  • Invalid configurations
  • Blackboard type mismatches

Multi-File Projects

  • Split trees across files
  • Reference trees with filename.xml::TreeID
  • Shared blackboards across files

Quick Reference: Decision Guide

I want to... Use...
Do A, then B, then C Sequence
Try A, if fail try B Selector
Do A and B at the same time Parallel
Flip Success/Failure Inverter
Repeat something N times Repeat
Do something with a time limit Timeout
Reuse another tree SubTree
Check something Condition
Do something Action
Pass data between nodes Blackboard {variable}

See Also