Of course! Here is a comprehensive guide to implementing and using Behavior Trees in Python.

What is a Behavior Tree?
A Behavior Tree (BT) is a powerful and modular architecture for creating complex, yet understandable, AI for games, robotics, and other interactive systems. It models an agent's behavior as a tree of "nodes," where each node represents an action or a decision.
The core idea is to break down complex AI logic into small, reusable, and composable parts. This makes the AI easier to design, debug, and maintain.
Key Concepts
- Nodes: The fundamental building blocks of a BT. Each node performs a specific task or makes a decision.
- Execution Flow: The tree is traversed from top to bottom, left to right. Nodes are executed in sequence based on their type and the result of their children.
- Node States: Every node, when executed, returns one of three states:
SUCCESS: The task completed successfully.FAILURE: The task failed.RUNNING: The task is still running and needs to be checked again on the next tick.
- Node Types:
- Control Nodes: Manage the flow of execution to their children. They decide which children to run and how to interpret their results.
- Sequence: Runs its children in order. Succeeds if all children succeed. Fails on the first child that fails.
- Selector (Fallback): Tries its children in order. Succeeds if any child succeeds. Fails only if all children fail.
- Parallel: Runs all its children simultaneously. Its success/failure criteria can be customized (e.g., succeed if N children succeed).
- Action Nodes: Perform an action in the world (e.g., move, attack, play an animation). They return
RUNNINGwhile the action is in progress andSUCCESSorFAILUREwhen it's complete. - Condition/Leaf Nodes: Check a simple condition (e.g., "Is health below 50%?", "Is enemy visible?"). They don't have children. They return
SUCCESSorFAILURE.
- Control Nodes: Manage the flow of execution to their children. They decide which children to run and how to interpret their results.
Part 1: A Basic, Hardcoded Implementation
Let's start with a simple, self-contained implementation to understand the core mechanics.
# Part 1: Basic Behavior Tree Implementation
from enum import Enum, auto
# 1. Define the Node States
class NodeState(Enum):
SUCCESS = auto()
FAILURE = auto()
RUNNING = auto()
# 2. Define the base Node class
class Node:
def __init__(self, name="Node"):
self.name = name
self.state = NodeState.SUCCESS
self.children = []
self.parent = None
def tick(self):
"""This method is called on every tick of the behavior tree."""
raise NotImplementedError("You must implement the tick() method in a subclass.")
def add_child(self, child):
self.children.append(child)
child.parent = self
# 3. Define Action Nodes (Leaf Nodes)
class Action(Node):
def __init__(self, name, action_func):
super().__init__(name)
self.action_func = action_func
def tick(self):
print(f"-> Executing Action: {self.name}")
self.state = self.action_func()
return self.state
# 4. Define Condition Nodes (Leaf Nodes)
class Condition(Node):
def __init__(self, name, condition_func):
super().__init__(name)
self.condition_func = condition_func
def tick(self):
print(f"-> Checking Condition: {self.name}")
self.state = NodeState.SUCCESS if self.condition_func() else NodeState.FAILURE
return self.state
# 5. Define Control Nodes
class Sequence(Node):
def __init__(self, name="Sequence"):
super().__init__(name)
self.current_child_index = 0
def tick(self):
# Run children in sequence
for i in range(self.current_child_index, len(self.children)):
child = self.children[i]
state = child.tick()
if state == NodeState.RUNNING:
# If a child is still running, stop and wait for next tick
self.current_child_index = i
return NodeState.RUNNING
if state == NodeState.FAILURE:
# If a child fails, the whole sequence fails
self.current_child_index = 0 # Reset for next run
return NodeState.FAILURE
# If all children succeeded, the sequence succeeds
self.current_child_index = 0 # Reset for next run
return NodeState.SUCCESS
class Selector(Node):
def __init__(self, name="Selector"):
super().__init__(name)
self.current_child_index = 0
def tick(self):
# Try children in sequence
for i in range(self.current_child_index, len(self.children)):
child = self.children[i]
state = child.tick()
if state == NodeState.RUNNING:
# If a child is still running, stop and wait
self.current_child_index = i
return NodeState.RUNNING
if state == NodeState.SUCCESS:
# If a child succeeds, the whole selector succeeds
self.current_child_index = 0 # Reset for next run
return NodeState.SUCCESS
# If all children failed, the selector fails
self.current_child_index = 0 # Reset for next run
return NodeState.FAILURE
# --- Example Usage ---
# Define some simple action and condition functions
def is_enemy_visible():
print(" -> Is enemy visible? Yes.")
return True
def is_low_health():
print(" -> Is health low? No.")
return False
def attack_action():
print(" -> Attacking...")
return NodeState.SUCCESS
def find_cover_action():
print(" -> Finding cover...")
return NodeState.SUCCESS
def patrol_action():
print(" -> Patrolling...")
return NodeState.SUCCESS
# Build the Behavior Tree
# Goal: If enemy is visible, attack. Otherwise, if health is low, find cover. Otherwise, patrol.
# Create nodes
root = Selector("Root")
combat_sequence = Sequence("Combat Sequence")
find_cover_sequence = Sequence("Find Cover Sequence")
patrol_action_node = Action("Patrol", patrol_action)
# Build the tree structure
root.add_child(combat_sequence)
combat_sequence.add_child(Condition("Enemy Visible?", is_enemy_visible))
combat_sequence.add_child(Action("Attack", attack_action))
root.add_child(find_cover_sequence)
find_cover_sequence.add_child(Condition("Low Health?", is_low_health))
find_cover_sequence.add_child(Action("Find Cover", find_cover_action))
root.add_child(patrol_action_node)
# Simulate the AI running for a few ticks
print("--- Tick 1: Enemy Visible ---")
root.tick() # Should try to attack
print("\n--- Tick 2: No Enemy, Low Health ---")
# Simulate conditions changing
def is_enemy_visible_no():
print(" -> Is enemy visible? No.")
return True # Let's make this false for this tick
is_enemy_visible.__code__ = is_enemy_visible_no.__code__ # Hack to change behavior
def is_low_health_yes():
print(" -> Is health low? Yes.")
return True
is_low_health.__code__ = is_low_health_yes.__code__
root.tick() # Should try to find cover
print("\n--- Tick 3: No Enemy, Full Health ---")
def is_low_health_no():
print(" -> Is health low? No.")
return False
is_low_health.__code__ = is_low_health_no.__code__
root.tick() # Should patrol
Part 2: A More Flexible and Reusable Implementation
The first implementation is good for learning, but it's not very flexible. Let's create a more robust version that uses a common pattern in BT libraries: Decorators. Decorators are nodes that wrap around other nodes to modify their behavior.

This version will also be much cleaner to build.
# Part 2: Flexible Behavior Tree with Decorators
from enum import Enum, auto
class NodeState(Enum):
SUCCESS = auto()
FAILURE = auto()
RUNNING = auto()
class Node:
def __init__(self, name="Node"):
self.name = name
self.state = None
self.children = []
self.parent = None
def tick(self):
raise NotImplementedError
def add_child(self, child):
self.children.append(child)
child.parent = self
# --- Leaf Nodes (Actions & Conditions) ---
class Action(Node):
def __init__(self, name, action_func):
super().__init__(name)
self.action_func = action_func
def tick(self):
self.state = self.action_func()
return self.state
class Condition(Node):
def __init__(self, name, condition_func):
super().__init__(name)
self.condition_func = condition_func
def tick(self):
self.state = NodeState.SUCCESS if self.condition_func() else NodeState.FAILURE
return self.state
# --- Control Nodes ---
class Sequence(Node):
def __init__(self, *children, name="Sequence"):
super().__init__(name)
for child in children:
self.add_child(child)
def tick(self):
for child in
