All tutorials Mighty Professional
Tutorial 15 · Game AI

Behavior Trees

The decision structure behind every modern NPC brain. Behavior trees replaced hand-crafted FSMs in AAA because they decompose AI into reusable, debuggable modules. We build one from scratch: sequences, selectors, decorators, parallel nodes, blackboards, reactive evaluation, and the tick loop that drives it all. Live widgets, C++ and Rust code, cited sources from Isla 2005 to Colledanchise 2018.

Time~45 min LevelJunior to mid; review for senior PrereqsYou can read C++ or pseudocode. Trees and recursion. Basic game-loop awareness. HardwareNone.

01Why behavior trees

Every NPC in a shipped AAA title makes decisions. Patrol a route, chase a target, take cover, reload, call for backup, retreat when hurt. In the early 2000s, those decisions lived in finite state machines: one state per behavior, explicit transitions between them. The approach works at five states. At fifteen it becomes unmanageable. At fifty it is a maintenance hazard that no one on the team wants to touch.

Behavior trees solved this by replacing the explicit-transition model with a tree of modular nodes evaluated top-down every frame. Adding a new behavior means grafting a subtree onto an existing branch, not wiring transitions to every other state. Halo 2[1] shipped the first production BT (or, more precisely, a behavior DAG, but the design pattern is the same one the industry adopted). Unreal Engine's built-in AI system[6] is a behavior tree editor. Unity lacks a built-in BT, but Behavior Designer and NodeCanvas fill the gap as middleware. Most AAA studios that hand-roll their AI systems (Naughty Dog[8], Guerrilla[9], IO Interactive) use some variant of the pattern.

The core advantages over FSMs:

What you'll have by the end

A working understanding of every node type in a behavior tree. Sequence, selector, decorator, parallel. The tick loop and the three return statuses. Blackboards for inter-node communication. Reactive vs memory evaluation. Utility-based selection for scoring branches instead of using fixed priority. The trade-off against GOAP and HTN planning. Code in C++ and Rust. Five interactive widgets. And the case studies: Halo, Unreal, The Last of Us, Horizon Zero Dawn, Hitman.

02A short history

Behavior trees emerged from the intersection of game AI engineering and robotics research. The timeline:

2002
Halo 2 development begins using hierarchical decision structures. Damian Isla at Bungie develops what he later calls a "behavior DAG" for the Covenant AI. The system replaces Halo 1's simpler FSM approach with a modular, priority-ordered decision tree. The details are published at GDC 2005 in "Handling Complexity in the Halo 2 AI."[1]
2005
Isla presents at GDC. The talk makes the case for behavior-centric AI architectures over state-centric ones. The core insight: define behaviors as modules, let the evaluation order encode priority, and avoid explicit state transitions. The industry takes notice.
2007
Champandard writes "Understanding Behavior Trees" on AiGameDev.com.[2] The article formalizes the node taxonomy (composite, decorator, leaf) and names the Sequence/Selector pair. The later Game AI Pro chapter by Champandard and Dunstan[3] becomes the reference implementation most studios start from.
2014
Unreal Engine 4 ships a built-in behavior tree editor.[6] The system includes a visual graph editor, a paired blackboard asset, decorator nodes that attach directly to composites, and an event-driven evaluation model that avoids polling. BTs become accessible to designers, not just programmers.
2014
Marzinotto, Colledanchise, Smith, and Ogren formalize BTs for robotics. "Towards a Unified Behavior Trees Framework for Robot Control," IEEE ICRA 2014[4]. The paper gives BTs a formal semantics grounded in hybrid dynamical systems, bridging the gap between the game-AI community's informal descriptions and the robotics community's need for mathematical rigor.
2018
Colledanchise and Ogren publish the first BT textbook. Behavior Trees in Robotics and AI: An Introduction, CRC Press[5]. Covers formal properties (safety, liveness), equivalence with FSMs and decision trees, and extensions for learning and task planning. The canonical academic reference.

03The FSM problem

An with n states has up to n(n - 1) directed transitions. Three states: 6 transitions. Eight states: 56. Not every pair needs a transition in practice, but every time a designer adds a new state, they have to answer "can the agent transition here from every existing state, and can it transition from here to every existing state?" Missing a transition is a bug (agent gets stuck in a state). Adding a redundant one is also a bug (agent escapes a state it shouldn't leave). The cost of answering the question is O(n) per new state; the total is O(n2).

Hierarchical FSMs (HFSMs) mitigate this by nesting sub-FSMs inside states, but the transition explosion still applies within each level. Pushdown automata (PDA-style stacks of states) help with "go to X, then return," but the core problem remains: the interaction between states is encoded in transitions, and transitions scale quadratically.

The widget below makes this visible. Add states to the FSM on the left and watch the transition arrows multiply. The behavior tree on the right adds a leaf for each new behavior, and the edge count grows linearly.

Live · FSM vs BT scaling
FSM states
3
FSM transitions
6
BT nodes
4
BT edges
3
The FSM shows every possible transition (worst case). Real FSMs have fewer, but "fewer" still grows faster than linearly. The BT is a flat selector for simplicity; a real tree would have nested composites, but the edge count still grows O(n) with behavior count. The structural advantage is that each new BT branch is isolated from its siblings.

04Tree structure and tick

A behavior tree is a rooted tree. Every node has zero or more children. The tree is evaluated from the root every tick (typically once per frame, though some engines tick AI at a lower rate to save CPU). Each node's tick() function returns one of three statuses:

Node types fall into three categories:

Evaluation is top-down, left-to-right. The root ticks its children according to its composite type. Children tick their children in turn. The result propagates back up. One walk through the tree per tick.

The widget below shows this evaluation in action. A selector at the root tries three branches: Attack (sequence of condition + action), Chase (sequence of condition + action), and Patrol (single action). Switch the scenario to change which conditions pass, then step through or auto-play to watch the tick propagate.

Live · BT tick visualizer
step
0
total steps
0
scenario
patrol
In the "patrol" scenario, both conditions fail. The selector tries Attack (fails at EnemyClose?), then Chase (fails at EnemySeen?), then falls through to Patrol (succeeds). In "chase," EnemySeen passes, so the Chase sequence runs. In "attack," EnemyClose passes, so the selector never reaches Chase or Patrol. Priority is encoded by left-to-right order.

05Sequences

A Sequence is AND logic. It executes its children left to right. If a child returns SUCCESS, the sequence moves to the next child. If a child returns FAILURE, the sequence stops and returns FAILURE. If all children return SUCCESS, the sequence returns SUCCESS. If any child returns RUNNING, the sequence returns RUNNING.

The mental model: "do A, then B, then C. If any step fails, abort." A patrol sequence might be: [HasWaypoint?, MoveToWaypoint, Wait(2s), NextWaypoint]. If HasWaypoint? fails, the entire patrol sequence fails and the parent selector tries something else.

06Selectors (fallbacks)

A Selector (sometimes called Fallback or Priority) is OR logic. It executes children left to right. If a child returns FAILURE, the selector moves to the next child. If a child returns SUCCESS, the selector stops and returns SUCCESS. If all children fail, the selector returns FAILURE. If any child returns RUNNING, the selector returns RUNNING.

The mental model: "try A. If that fails, try B. If that fails, try C." The leftmost child has the highest priority. A top-level selector with [AttackSequence, ChaseSequence, PatrolAction] tries combat first, pursuit second, and only patrols if neither applies.

The widget below puts a sequence and a selector side by side with the same three children. Toggle each child's result to see how the composite outcome differs.

Live · Sequence vs Selector
Sequence result
···
Selector result
···
With the default (OK, OK, FAIL): the sequence stops at Fire and returns FAILURE (AND logic, one failure kills it). The selector stops at Check Ammo and returns SUCCESS (OR logic, one success is enough). Toggle the first child to FAIL and both composites change: the sequence fails immediately, but the selector moves on to the next child.

07Decorators

A decorator wraps exactly one child and modifies its behavior or result. Common decorators:

Unreal's BT system uses decorators differently from the academic standard. In Unreal, decorators attach to composite nodes as auxiliary nodes rather than being tree nodes in their own right. The effect is the same (the decorator gates or modifies the composite's evaluation), but the visual layout in the editor differs. Decorators in Unreal also serve as abort triggers: a decorator can specify that when its condition changes, it aborts the subtree below and forces re-evaluation.[6]

08Parallel nodes

A Parallel node ticks all its children every tick, regardless of individual results. Two success policies are common:

A typical use: [Parallel: MoveTo(cover), PlayAnimation(sprint)]. The agent moves and plays the sprint animation at the same time. When MoveTo finishes, the parallel node reports SUCCESS and the animation can be interrupted.

Parallel does not mean multi-threaded

"Parallel" refers to the evaluation policy: all children are ticked in the same frame. The ticking itself happens sequentially on one thread. No concurrency primitives, no race conditions. The name is unfortunate because it invites confusion with parallel programming. Some codebases rename it to "Concurrent" or "SimpleParallel" (Unreal) to reduce this confusion.

09Blackboards

Nodes in a behavior tree need to share data: the target entity, its last-known position, the agent's health, ammo count, whether a perception query returned a result. The is a key-value store that serves this purpose. Every node can read from and write to the blackboard. The blackboard is scoped to one agent (one blackboard instance per entity), though some systems allow parent scopes for squad-level data.

Unreal's blackboard is a first-class asset paired with the BT. You define typed keys (Object, Vector, Float, Bool, Enum) in the blackboard definition. Decorators can observe specific keys and trigger re-evaluation when the value changes, which is the mechanism behind Unreal's event-driven BT model.[6]

The widget below shows a behavior tree reading from a live blackboard. Adjust the sliders to change perception data, and watch the tree re-evaluate in real time.

Live · Blackboard inspector
active behavior
···
The Attack branch requires target.visible AND distance below 15 AND ammo above 0. Chase requires target.visible only. Patrol is the fallback. Set distance to 10 and watch the tree switch from Chase to Attack. Set ammo to 0 and it falls back to Chase. Toggle visibility off and it falls through to Patrol.

10Actions and conditions

Leaf nodes are where the actual work happens. Two kinds:

The RUNNING status is what makes BTs handle multi-frame actions cleanly. A MoveTo action that takes 200 frames to complete returns RUNNING on each of those frames. The parent sequence sees RUNNING, returns RUNNING itself, and the tree pauses evaluation of that branch until next tick. This propagation continues all the way up to the root.

Code: base node, sequence, selector

Node hierarchy + composites
enum class Status { Success, Failure, Running };

// Base node. Every node in the tree implements tick().
class Node {
public:
    virtual ~Node() = default;
    virtual Status tick() = 0;
};

// Sequence: AND logic. Fail on first failure, succeed when all succeed.
class Sequence : public Node {
    std::vector<std::unique_ptr<Node>> children;
public:
    void addChild(std::unique_ptr<Node> child) {
        children.push_back(std::move(child));
    }

    Status tick() override {
        for (auto& child : children) {
            Status result = child->tick();
            if (result != Status::Success)
                return result;  // FAILURE or RUNNING propagates up
        }
        return Status::Success;  // all children succeeded
    }
};

// Selector: OR logic. Succeed on first success, fail when all fail.
class Selector : public Node {
    std::vector<std::unique_ptr<Node>> children;
public:
    void addChild(std::unique_ptr<Node> child) {
        children.push_back(std::move(child));
    }

    Status tick() override {
        for (auto& child : children) {
            Status result = child->tick();
            if (result != Status::Failure)
                return result;  // SUCCESS or RUNNING propagates up
        }
        return Status::Failure;  // all children failed
    }
};
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum Status { Success, Failure, Running }

// Base trait. Every node implements tick().
pub trait Node {
    fn tick(&mut self) -> Status;
}

// Sequence: AND logic. Fail on first failure, succeed when all succeed.
pub struct Sequence {
    children: Vec<Box<dyn Node>>,
}

impl Sequence {
    pub fn new(children: Vec<Box<dyn Node>>) -> Self {
        Sequence { children }
    }
}

impl Node for Sequence {
    fn tick(&mut self) -> Status {
        for child in &mut self.children {
            let result = child.tick();
            if result != Status::Success {
                return result;  // FAILURE or RUNNING propagates up
            }
        }
        Status::Success  // all children succeeded
    }
}

// Selector: OR logic. Succeed on first success, fail when all fail.
pub struct Selector {
    children: Vec<Box<dyn Node>>,
}

impl Node for Selector {
    fn tick(&mut self) -> Status {
        for child in &mut self.children {
            let result = child.tick();
            if result != Status::Failure {
                return result;  // SUCCESS or RUNNING propagates up
            }
        }
        Status::Failure  // all children failed
    }
}

Code: a concrete action (MoveTo with RUNNING)

Multi-frame action node
// MoveTo: navigate to a target position read from the blackboard.
// Returns RUNNING while pathfinding/moving, SUCCESS on arrival,
// FAILURE if the path is invalid or the target is unreachable.
class MoveTo : public Node {
    Blackboard& blackboard;
    NavAgent&   navAgent;
    bool started = false;
public:
    MoveTo(Blackboard& blackboard, NavAgent& navAgent)
        : blackboard(blackboard), navAgent(navAgent) {}

    Status tick() override {
        if (!started) {
            Vec3 target = blackboard.get<Vec3>("target.position");
            if (!navAgent.requestPath(target))
                return Status::Failure;  // no valid path
            started = true;
        }
        if (navAgent.hasArrived()) {
            started = false;          // reset for next invocation
            return Status::Success;
        }
        return Status::Running;      // still moving
    }
};
// MoveTo: navigate to a target position read from the blackboard.
// Returns Running while pathfinding/moving, Success on arrival,
// Failure if the path is invalid or the target is unreachable.
pub struct MoveTo {
    started: bool,
}

impl MoveTo {
    pub fn new() -> Self { MoveTo { started: false } }
}

impl Node for MoveTo {
    fn tick_with(&mut self, blackboard: &Blackboard, nav: &mut NavAgent) -> Status {
        if !self.started {
            let target = blackboard.get::<Vec3>("target.position");
            if !nav.request_path(target) {
                return Status::Failure;  // no valid path
            }
            self.started = true;
        }
        if nav.has_arrived() {
            self.started = false;     // reset for next invocation
            return Status::Success;
        }
        Status::Running              // still moving
    }
}

Code: blackboard with typed access

Blackboard key-value store
// Blackboard: type-erased key-value store.
// Production code would use a fixed-layout struct for cache locality;
// the std::any version here prioritizes flexibility for prototyping.
class Blackboard {
    std::unordered_map<std::string, std::any> data;
public:
    template<typename T>
    void set(const std::string& key, T value) {
        data[key] = std::move(value);
    }

    template<typename T>
    T get(const std::string& key) const {
        auto it = data.find(key);
        if (it == data.end())
            return T{};               // default-constructed if missing
        return std::any_cast<T>(it->second);
    }

    bool has(const std::string& key) const {
        return data.count(key) > 0;
    }
};
use std::any::Any;
use std::collections::HashMap;

// Blackboard: type-erased key-value store.
// Production code would use a fixed-layout struct for cache locality;
// the Any version here prioritizes flexibility for prototyping.
pub struct Blackboard {
    data: HashMap<String, Box<dyn Any>>,
}

impl Blackboard {
    pub fn new() -> Self {
        Blackboard { data: HashMap::new() }
    }

    pub fn set<T: 'static>(&mut self, key: &str, value: T) {
        self.data.insert(key.to_string(), Box::new(value));
    }

    pub fn get<T: 'static + Clone>(&self, key: &str) -> Option<T> {
        self.data.get(key)?.downcast_ref::<T>().cloned()
    }

    pub fn has(&self, key: &str) -> bool {
        self.data.contains_key(key)
    }
}
What's intentionally missing

The code above skips: thread safety (real blackboards need atomic reads or lock guards if ticked from a job system), key-change notifications (Unreal's observer pattern for event-driven re-evaluation), typed key definitions (catching "target.positon" typos at compile time), and scope hierarchies (per-entity vs per-squad vs global). A shipping blackboard also avoids std::string keys in favor of hashed IDs for O(1) lookup without string comparison.

11Memory and reactive behavior

Two evaluation strategies:

Most production systems use a hybrid. Unreal's BT is memory-based by default, but decorators can specify an AbortType: LowerPriority (abort branches to the right), Self (abort this branch), or Both. When a decorator's observed blackboard key changes, the specified abort fires and forces re-evaluation. This gives reactive behavior where needed without the cost of re-checking every condition every frame.[6]

The widget below demonstrates the interrupt. An agent patrols (RUNNING). Click "Spot Enemy" and watch the higher-priority Combat branch abort Patrol and take over.

Live · Priority interrupt
agent state
···
enemy spotted
no
The selector evaluates left to right. While no enemy is spotted, the Combat sequence fails at its condition and the selector falls through to Patrol (RUNNING). The moment EnemySpotted becomes true, the next tick evaluates Combat first, its condition passes, and Engage takes over. Patrol is never reached. This is priority-based preemption: the leftmost branch that succeeds wins.

12Utility-based selection

Standard selectors pick children by fixed left-to-right priority. replaces that with a scoring function: each child is assigned a utility value (a float computed from threat level, distance, ammo count, health, cooldown timers), and the child with the highest score wins. Dave Mark's Infinite Axis Utility System (GDC AI Summit 2013, refined at GDC 2015[7]) is the canonical reference for this approach.

Hybrid BT+Utility trees are common in production. The tree structure handles the coarse decision hierarchy (combat vs exploration vs dialogue), while utility scoring handles the fine-grained choice within a category (which target to engage, which cover point to use). This avoids the combinatorial explosion of scoring everything against everything, while still getting smooth, context-sensitive decisions where they matter.

Implementation: replace the Selector's left-to-right loop with a "score all children, sort by score, evaluate in score order" loop. The child evaluation still follows the standard tick protocol (SUCCESS/FAILURE/RUNNING), so the rest of the tree machinery is unchanged.

13BT vs GOAP vs HTN

Behavior trees are not the only game AI architecture. Two planning-based alternatives ship in production: (Goal-Oriented Action Planning) and (Hierarchical Task Networks).

PropertyBehavior TreeGOAPHTN
Behavior authored by Designer builds tree structure Designer defines actions + preconditions; planner finds the sequence Designer defines task decompositions; planner picks decomposition path
Emergent behavior Low. The tree encodes the decision space explicitly. High. The planner can combine actions in ways the designer didn't anticipate. Medium. Constrained by decomposition hierarchy but can still surprise.
Debuggability High. One path through a tree. Visual debuggers show the active branch. Low. Plans are generated at runtime. Hard to explain why the planner chose a specific action sequence. Medium. The decomposition tree is readable, but plan selection can be opaque.
Shipped in Halo 2/3, Unreal BT, Hitman F.E.A.R.[10] Transformers: Fall of Cybertron[11]
CPU cost per tick Low. One tree walk. O(depth) for memory BTs, O(nodes) for reactive. Variable. Planning is a search, potentially expensive. Usually amortized by caching plans. Low to medium. Decomposition is cheaper than GOAP's open-ended search.

F.E.A.R. (Monolith, 2005) is the canonical GOAP reference. Jeff Orkin's GDC 2006 talk[10] describes a system where the FSM has only three states (Goto, Animate, UseSmartObject) and A* plans over the action space to decide what to do. The result is impressive emergent behavior (soldiers flanking, suppressing, flushing with grenades) but the debugging story is harder: "why did the AI throw a grenade?" requires replaying the planner's search.

HTN planning (Humphreys, Game AI Pro[11]) sits between BTs and GOAP. High-level tasks decompose into subtasks according to designer-authored rules. The planner picks decomposition paths based on world state. High Moon Studios shipped HTN in Transformers: Fall of Cybertron and reported that it was faster than the GOAP system used in the previous game (War for Cybertron).

Guerrilla Games used a different hybrid for Horizon Zero Dawn: HTN planning combined with utility-based decisions for the machine AI, handling both the high-level behavior hierarchy and the fine-grained machine ecology where different machine classes (Acquisition, Transport, Combat, Reconnaissance) coordinate in groups.[9]

14Case studies

Halo 2 / Halo 3 (Bungie)

Isla's GDC 2005 talk[1] describes the Covenant AI as a behavior DAG (directed acyclic graph, not strictly a tree, because some behaviors are shared across branches). The key insight: define behaviors as self-contained modules, evaluated in priority order. The system let Bungie scale from a handful of AI types in Halo 1 to the diverse enemy roster in Halo 2 without the codebase becoming unmaintainable. Halo 3 refined the architecture further, adding better squad-level coordination.

Unreal Engine BT system

Unreal's BT[6] is event-driven rather than polling-based. Decorators observe blackboard keys and trigger re-evaluation only when observed values change. This reduces CPU cost on large NPC counts by avoiding the per-tick full-tree walk. The system includes a visual debugger that highlights the active branch in real time, making it straightforward to diagnose why an NPC chose a particular behavior. Unreal's "Simple Parallel" node replaces the academic Parallel composite: it ticks a primary task and a background task simultaneously, finishing when the primary finishes.

The Last of Us (Naughty Dog)

Naughty Dog's enemy AI[8] uses a priority-stacked skill/behavior system built on FSMs rather than behavior trees. Each NPC has a stack of "skills" (combat, investigate, patrol) prioritized by context. Low-level "behaviors" within each skill are FSM-driven. The architecture handles squad coordination (one suppresses while others advance) through a separate tactical layer. This is a useful contrast: TLOU shipped one of the most praised enemy AI systems in AAA without behavior trees, demonstrating that BTs are not the only viable approach.

Horizon Zero Dawn (Guerrilla Games)

Julian Berteling's GDC 2018 talk[9] covers Guerrilla's transition from Killzone's corridor-shooter AI to Horizon's open-world navigation and animation systems. The machine AI uses HTN planning for high-level decisions and utility scoring for target selection, not behavior trees. Different machine classes (Watchers for reconnaissance, Grazers for acquisition, Thunderjaws for combat) have distinct behavior profiles and coordinate in mixed-species groups. The system uses "information packets" attached to stimuli (arrows, rocks, player movement) that feed into machine sensors, allowing each class to react differently to the same event.

Hitman (IO Interactive)

IO Interactive's crowd system[12] handles over 1,000 NPCs per level. The core NPC AI uses behavior trees for individual decisions (react to player disguise, investigate suspicious behavior, alert guards). The crowd layer on top manages ambient behaviors (walking routes, conversations, scripted vignettes) at scale. The BT architecture built for Hitman: Absolution carried forward into the 2016 and 2018 Hitman releases with incremental refinement. A GDC 2017 talk by Anguelov, Vehkala, and Weber outlined the behavior tree cultivation practices IO uses to keep large trees maintainable.

15Pitfalls

16What's next

17Sources

  1. Damian Isla. "Handling Complexity in the Halo 2 AI." GDC 2005 / Gamasutra. gamedeveloper.com. The foundational talk on behavior-centric AI architecture that kicked off industry adoption of behavior trees. Describes the Halo 2 Covenant AI as a behavior DAG with priority-ordered evaluation.
  2. Alex J. Champandard. "Understanding Behavior Trees." AiGameDev.com, 2007. Formalizes the node taxonomy (composite, decorator, leaf) and names the Sequence/Selector pair. The article that standardized BT terminology for the game AI community.
  3. Alex J. Champandard and Philip Dunstan. "The Behavior Tree Starter Kit." Game AI Pro, Chapter 6. gameaipro.com. Reference implementation of a minimal BT runtime. The code most studios start from when building a custom BT system.
  4. Alejandro Marzinotto, Michele Colledanchise, Christian Smith, Petter Ogren. "Towards a Unified Behavior Trees Framework for Robot Control." IEEE ICRA 2014. ieeexplore.ieee.org. Formal semantics for BTs grounded in hybrid dynamical systems. Bridges the gap between game AI's informal descriptions and robotics' need for mathematical rigor.
  5. Michele Colledanchise and Petter Ogren. Behavior Trees in Robotics and AI: An Introduction. CRC Press, 2018. arxiv.org/abs/1709.00084 (preprint). The first comprehensive BT textbook. Covers formal properties, equivalence proofs, learning, and task planning.
  6. Epic Games. "Behavior Trees in Unreal Engine." Unreal Engine 5 documentation. dev.epicgames.com. Documents the visual BT editor, blackboard asset system, decorator-based abort triggers, and the event-driven evaluation model.
  7. Dave Mark. "Architecture Tricks: Managing Behaviors in Time, Space, and Depth." GDC AI Summit, 2013. gdcvault.com. Introduces the Infinite Axis Utility System. The canonical reference for utility-based AI scoring, later refined at GDC 2015 with Mike Lewis.
  8. Max Dyckhoff. "Ellie: Buddy AI in The Last of Us" and Travis McIntosh. "The Last of Us: Human Enemy AI." GDC 2014. gdcvault.com. Priority-stacked skill/behavior system built on FSMs, not behavior trees. Described in McIntosh's Game AI Pro 2 chapter (Ch. 34). A useful contrast showing AAA-quality AI without BTs.
  9. Julian Berteling. "Beyond 'Killzone': Creating New AI Systems for 'Horizon Zero Dawn'." GDC 2018. gdcvault.com. HTN planning plus utility scoring for machine ecology. Describes how different machine classes coordinate in mixed-species groups using information-packet-based stimulus systems.
  10. Jeff Orkin. "Three States and a Plan: The A.I. of F.E.A.R." GDC 2006. gamedevs.org. Goal-Oriented Action Planning. The FSM has three states; A* plans over the action space. Emergent flanking, suppression, and grenade-flushing behavior.
  11. Troy Humphreys. "Exploring HTN Planners Through Example." Game AI Pro, Chapter 12. gameaipro.com. Total-order forward decomposition planner used in Transformers: Fall of Cybertron. Faster than GOAP for the same game's previous title.
  12. Bobby Anguelov (WB Games Montreal), Mika Vehkala (Remedy), Ben Weber (Twitch). "AI Arborist: Proper Cultivation and Care for Your Behavior Trees." GDC 2017. gdcvault.com. General BT cultivation practices: keeping large trees maintainable, debugging, and testing. Anguelov's prior IO Interactive experience informed the talk but the content is studio-agnostic.
  13. Matteo Iovino, Edvards Scukins, Jonathan Styrud, Petter Ogren, Christian Smith. "A survey of Behavior Trees in robotics and AI." Robotics and Autonomous Systems, 2022. sciencedirect.com. Comprehensive survey of BT extensions, formal properties, and applications across game AI and robotics. Covers reactive, memory, and hybrid evaluation strategies.

See also