Behavior Trees
Behavior trees define the decision-making logic for characters, institutions, and other entities. They model how an entity chooses actions, responds to conditions, and adapts to its environment. Storybook uses behavior trees for character AI, NPC routines, and complex reactive behavior.
What is a Behavior Tree?
A behavior tree is a hierarchical structure that executes from root to leaves, making decisions based on success/failure of child nodes:
- Composite nodes (choose, then) have multiple children and control flow
- Condition nodes (if, when) test predicates
- Action nodes execute concrete behaviors
- Decorator nodes (repeat, invert, timeout, etc.) modify child behavior
- Subtree nodes (include) reference other behavior trees
Behavior trees are evaluated every tick (frame), flowing through the tree from root to leaves until a node returns success or failure.
Syntax
<behavior-decl> ::= "behavior" <identifier> <body>
<body> ::= "{" <prose-blocks>* <behavior-node> "}"
<behavior-node> ::= <selector>
| <sequence>
| <condition>
| <action>
| <decorator>
| <subtree>
<selector> ::= "choose" <label>? "{" <behavior-node>+ "}"
<sequence> ::= "then" <label>? "{" <behavior-node>+ "}"
<condition> ::= ("if" | "when") "(" <expression> ")"
<action> ::= <identifier> ( "(" <param-list> ")" )?
<param-list> ::= <field> ("," <field>)*
<decorator> ::= <decorator-type> ("(" <params> ")")? "{" <behavior-node> "}"
<subtree> ::= "include" <qualified-path>
<label> ::= <identifier>
Composite Nodes
Selector: choose
A selector tries its children in order until one succeeds. Returns success if any child succeeds, failure if all fail.
Execution:
- Try first child
- If succeeds -> return success
- If fails -> try next child
- If all fail -> return failure
Use case: “Try A, if that fails try B, if that fails try C…”
behavior GuardBehavior {
choose guard_actions {
AttackIntruder // Try first
SoundAlarm // If attack fails, sound alarm
FleeInPanic // If alarm fails, flee
}
}
Named selectors:
choose service_options {
then serve_regular {
CustomerIsWaiting
TakeOrder
}
then restock_display {
DisplayIsLow
FetchFromKitchen
}
}
Labels are optional but recommended for complex trees–they improve readability and debugging.
Sequence: then
A sequence runs its children in order until one fails. Returns success only if all children succeed, failure if any fails.
Execution:
- Run first child
- If fails -> return failure
- If succeeds -> run next child
- If all succeed -> return success
Use case: “Do A, then B, then C… all must succeed”
behavior BrewingSequence {
then brew_potion {
GatherIngredients // Must succeed
MixIngredients // Then this must succeed
Boil // Then this must succeed
BottlePotion // Finally this
}
}
Named sequences:
then prepare_sourdough {
MixDough
KneadDough
ShapeLoaves
}
Nesting Composites
Composite nodes can nest arbitrarily deep:
behavior ComplexAI {
choose root_decision {
// Combat branch
then engage_combat {
if(enemy_nearby)
choose combat_tactics {
AttackWithSword
AttackWithMagic
DefendAndWait
}
}
// Exploration branch
then explore_area {
if(safe)
choose exploration_mode {
SearchForTreasure
MapTerritory
Rest
}
}
// Default: Idle
Idle
}
}
Condition Nodes
Conditions test expressions and return success/failure based on the result.
if vs. when
Both are semantically identical–use whichever reads better in context:
behavior Example {
choose options {
// "if" for state checks
then branch_a {
if(player_nearby)
Attack
}
// "when" for event-like conditions
then branch_b {
when(alarm_triggered)
Flee
}
}
}
Condition Syntax
if(health < 20) // Comparison
when(is_hostile) // Boolean field
if(distance > 10 and has_weapon) // Logical AND
when(not is_stunned or is_enraged) // Logical OR with NOT
See Expression Language for complete expression syntax.
Action Nodes
Actions are leaf nodes that execute concrete behaviors. They reference action implementations in the runtime.
Basic Actions
behavior SimpleActions {
then do_things {
MoveForward
TurnLeft
Attack
}
}
Actions with Parameters
Actions can have named parameters using parenthesis syntax:
behavior ParameterizedActions {
then patrol {
MoveTo(destination: "Waypoint1", speed: 1.5)
WaitFor(duration: 5s)
MoveTo(destination: "Waypoint2", speed: 1.5)
}
}
Parameter passing:
- Parameters are passed as fields (name: value)
- All value types supported
- Runtime validates parameter types
behavior RichParameters {
then complex_action {
CastSpell(spell: "Fireball", target: enemy_position, power: 75, multicast: false)
Heal(amount: 50, target: self)
}
}
Decorator Nodes
Decorators wrap a single child node and modify its behavior. See Decorators for complete reference.
Common Decorators
behavior DecoratorExamples {
choose {
// Repeat infinitely
repeat {
PatrolRoute
}
// Repeat exactly 3 times
repeat(3) {
CheckDoor
}
// Repeat 2 to 5 times
repeat(2..5) {
SearchArea
}
// Invert success/failure
invert {
IsEnemyNearby // Returns success if enemy NOT nearby
}
// Retry up to 5 times on failure
retry(5) {
OpenLockedDoor
}
// Timeout after 10 seconds
timeout(10s) {
SolveComplexPuzzle
}
// Cooldown: only run once per 30 seconds
cooldown(30s) {
FireCannon
}
// If: only run if condition true
if(health > 50) {
AggressiveAttack
}
// Always succeed regardless of child result
succeed_always {
AttemptOptionalTask
}
// Always fail regardless of child result
fail_always {
ImpossipleTask
}
}
}
Subtree References
The include keyword references another behavior tree, enabling modularity and reuse.
Basic Subtree
behavior PatrolRoute {
then patrol {
MoveTo(destination: "Waypoint1")
MoveTo(destination: "Waypoint2")
MoveTo(destination: "Waypoint3")
}
}
behavior GuardBehavior {
choose guard_ai {
then combat {
if(enemy_nearby)
include combat::engage
}
include PatrolRoute // Reuse patrol behavior
}
}
Qualified Subtree References
behavior ComplexAI {
choose ai_modes {
include behaviors::combat::melee
include behaviors::combat::ranged
include behaviors::exploration::search
include behaviors::social::greet
}
}
Named Nodes
Both composite nodes (choose, then) can have optional labels:
behavior NamedNodeExample {
choose daily_priority { // Named selector
then handle_special_orders { // Named sequence
CheckOrderQueue
PrepareSpecialIngredients
BakeSpecialItem
}
choose regular_work { // Named selector
then morning_bread { // Named sequence
MixSourdough
BakeLoaves
}
}
}
}
Benefits:
- Readability: Tree structure is self-documenting
- Debugging: Named nodes appear in execution traces
- Narrative: Labels can be narrative (“handle_special_orders” vs. anonymous node)
Guidelines:
- Use labels for important structural nodes
- Use descriptive, narrative names
- Omit labels for trivial nodes
Complete Examples
Simple Guard AI
behavior GuardPatrol {
choose guard_logic {
// Combat if enemy nearby
then combat_mode {
if(enemy_nearby and has_weapon)
Attack
}
// Sound alarm if see intruder
then alarm_mode {
if(intruder_detected)
SoundAlarm
}
// Default: Patrol
then patrol_mode {
include PatrolRoute
}
}
}
Complex NPC Behavior
behavior Innkeeper_DailyRoutine {
---description
The innkeeper's behavior throughout the day. Uses selectors for
decision-making and sequences for multi-step actions.
---
choose daily_activity {
// Morning: Prepare for opening
then morning_prep {
when(time_is_morning)
then prep_sequence {
UnlockDoor
LightFireplace
PrepareBreakfast
WaitForGuests
}
}
// Day: Serve customers
then day_service {
when(time_is_daytime)
choose service_mode {
// Serve customer if present
then serve {
if(customer_waiting)
GreetCustomer
TakeOrder
ServeMeal
CollectPayment
}
// Restock if needed
then restock {
if(inventory_low)
ReplenishInventory
}
// Default: Clean
CleanTables
}
}
// Night: Close up
then evening_close {
when(time_is_evening)
then close_sequence {
DismissRemainingGuests
LockDoor
CountMoney
GoToBed
}
}
}
}
Morning Baking Routine
behavior Baker_MorningRoutine {
---description
Martha's morning routine: prepare dough step by step,
from mixing to shaping to baking.
---
then morning_baking {
// Start with sourdough
then prepare_starter {
CheckStarter
FeedStarter
WaitForActivity
}
// Mix the dough
then mix_dough {
MeasureFlour
AddWater
IncorporateStarter
}
// Knead and shape
then shape_loaves {
KneadDough
FirstRise
ShapeLoaves
}
// Bake
then bake {
PreHeatOven
LoadLoaves
MonitorBaking
}
}
}
Repeating Behavior
behavior Bakery_CustomerServiceLoop {
---description
The bakery's continuous customer service loop. Uses infinite
repeat decorator to serve customers throughout the day.
---
repeat {
then service_cycle {
// Check for customers
choose service_mode {
then serve_waiting {
if(customer_waiting)
GreetCustomer
TakeOrder
}
then restock_display {
if(display_low)
FetchFromKitchen
ArrangeOnShelves
}
}
// Process payment
CollectPayment
ThankCustomer
// Brief pause between customers
timeout(5s) {
CleanCounter
}
PrepareForNextCustomer
}
}
}
Retry Logic for Delicate Recipes
behavior Baker_DelicatePastry {
---description
Elena attempting a delicate pastry recipe that requires
precise technique. Uses retry decorator for persistence.
---
choose baking_strategy {
// Try when conditions are right
then attempt_pastry {
if(oven_at_temperature)
// Try up to 3 times
retry(3) {
then make_pastry {
RollDoughThin
ApplyFilling
FoldAndSeal
CheckForLeaks
}
}
}
// If oven not ready, prepare meanwhile
then prepare_meanwhile {
if(not oven_at_temperature)
then prep_sequence {
PrepareIngredients
MixFilling
ChillDough
}
}
}
}
Integration with Characters
Characters link to behaviors using the uses behaviors clause:
character Guard {
uses behaviors: [
{
tree: guards::patrol_route
priority: normal
},
{
tree: guards::engage_hostile
when: threat_detected
priority: high
},
{
tree: guards::sound_alarm
when: emergency
priority: critical
}
]
}
Behavior selection:
- Multiple behaviors evaluated by priority (critical > high > normal > low)
- Conditions (
whenclause) gate behavior activation - Higher-priority behaviors preempt lower-priority ones
See Characters for complete behavior linking syntax.
Execution Semantics
Tick-Based Evaluation
Behavior trees execute every “tick” (typically once per frame):
- Start at root node
- Traverse down to leaves based on composite logic
- Execute leaf nodes (conditions, actions)
- Return success/failure up the tree
- Repeat next tick
Node Return Values
Every node returns one of:
- Success: Node completed successfully
- Failure: Node failed
- Running: Node still executing (async action)
Stateful vs. Stateless
- Stateless nodes: Evaluate fresh every tick (conditions, simple actions)
- Stateful nodes: Maintain state across ticks (decorators, long actions)
Example of stateful behavior:
behavior LongAction {
timeout(30s) { // Stateful: tracks elapsed time
ComplexCalculation // May take multiple ticks
}
}
Validation Rules
- At least one node: Behavior body must contain at least one node
- Composite children:
chooseandthenrequire at least one child - Decorator child: Decorators require exactly one child
- Action exists: Action names must reference registered actions in runtime
- Subtree exists:
includemust reference a definedbehaviordeclaration - Expression validity: Condition expressions must be well-formed
- Duration format: Decorator durations must be valid (e.g.,
5s,10m) - Unique labels: Node labels (if used) should be unique within their parent
- Parameter types: Action parameters must match expected types
Best Practices
1. Prefer Shallow Trees
Avoid:
choose {
then { then { then { then { Action } } } } // Too deep!
}
Prefer:
choose root {
include combat_tree
include exploration_tree
include social_tree
}
2. Use Named Nodes for Clarity
Avoid:
choose {
then {
IsHungry
FindFood
Eat
}
Wander
}
Prefer:
choose survival {
then eat_if_hungry {
IsHungry
FindFood
Eat
}
Wander
}
3. Subtrees for Reusability
Avoid: Duplicating logic across behaviors
Prefer:
behavior Combat_Common {
then attack_sequence {
DrawWeapon
Approach
Strike
}
}
behavior Warrior {
include Combat_Common
}
behavior Guard {
include Combat_Common
}
4. Decorators for Timing
Avoid: Manual timing in actions
Prefer:
timeout(10s) {
ComplexTask
}
cooldown(30s) {
SpecialAbility
}
5. Guard for Preconditions
Avoid:
then problematic {
ExpensiveAction // Always runs even if inappropriate
}
Prefer:
if(can_afford_action) {
ExpensiveAction // Only runs when condition passes
}
Cross-References
- Decorators - Complete decorator reference
- Characters - Linking behaviors to characters
- Expression Language - Condition expression syntax
- Value Types - Parameter value types
- Design Patterns - Common behavior tree patterns
Related Concepts
- Reactive AI: Behavior trees continuously react to changing conditions
- Hierarchical decision-making: Composite nodes create decision hierarchies
- Modularity: Subtrees enable behavior reuse and composition
- Narrative-driven design: Named nodes make behavior trees readable as stories