Skip to content
Development
Skill

/ai-behavior-trees-utility-ai

Engine-neutral behavior tree and utility AI code for NPCs in any language: Blackboard, reactive composites, decorators and response-curve scoring. Use when an enemy keeps chasing after losing sight, restarts its action every frame or flip-flops between choices. Not for FSMs,

From plugin
awesome-gamedev-agent-skills
8.6k79 skills

Claude Code

$ npx -y skills add gamedev-skills/awesome-gamedev-agent-skills --skill ai-behavior-trees-utility-ai --agent claude-code

SKILL.md

ai-behavior-trees-utility-ai.SKILL.md
name: ai-behavior-trees-utility-ai
description: >
  Engine-neutral behavior tree and utility AI code for NPCs in any language: Blackboard, reactive
  composites, decorators and response-curve scoring. Use when an enemy keeps chasing after losing
  sight, restarts its action every frame or flip-flops between choices. Not for FSMs, steering or
  pathfinding (`game-ai`) or Unreal BT, EQS and StateTree (`unreal-behavior-trees`).

Behavior trees and Utility AI

A behavior tree (BT) gives an agent ordered, interruptible priorities; Utility AI scores every option and picks the most wanted one. The code is plain C# so it ports to any engine; the non-obvious part is the lifecycle contract (who resets what, and when), which is where almost every BT bug lives.

When to use

  • Build or fix a reusable BT runtime: Blackboard, leaves, composites, decorators.
  • Build a utility decider: curves, considerations, a selector with hysteresis.
  • Combine them: a BT branch that hands a graded choice (which attack, which target) to utility.

**Not this skill:** choosing FSM vs BT, steering, A*, flow fields and perception belong to `game-ai`. Unreal's `BehaviorTree`/`Blackboard` assets, `BTTask`/`BTService`, EQS and StateTree belong to `unreal-behavior-trees`. The agent that physically moves (`NavMeshAgent`, `NavigationAgent3D`) belongs to the engine navigation skill.

Workflow

1. **Read what exists.** Find the current AI code, its tick site and how it reads the world. If the project already ships a BT plugin (Godot LimboAI or Beehave, Unity Behavior, NodeCanvas), extend it instead of adding a second runtime. 2. **Pick the default shape: BT outside, utility inside.** The BT decides engage, flee or patrol; a utility node inside the engage branch picks how. Use utility at the top only when every top-level behavior is a graded trade-off (needs-driven villagers), because it is harder to debug and prone to thrashing. 3. **Design the Blackboard first.** Perception writes facts (`targetPos`, `health01`, `distToPlayer`); leaves read them. Nodes never hold references to each other. 4. **Choose memory or reactive per composite.** Priority selectors at the top are reactive so a higher branch can preempt; a `Sequence` whose first child is a guard condition must be a `ReactiveSequence`, or the guard stops being checked once the action runs. 5. **Normalize every utility input to 0..1** through a curve before combining; multiply with compensation so a 0 still vetoes. 6. **Tick on a decision cadence** (5-15 Hz for most NPCs) from an accumulator, staggered across agents. Movement and animation still run every frame. 7. **Prove it with a headless tick test** (below): script the Blackboard, tick N times and assert the status sequence and reset counts. Then draw the running leaf and per-action scores on screen while tuning.

The lifecycle contract

Every node returns `Success`, `Failure` or `Running`. Parents reset a child as soon as it returns a terminal status and when they abandon it while `Running`; the tree resets a terminal root. `Reset()` must be idempotent and must clear only execution-local state (timers, reservations), never persistent state such as a cooldown deadline.

// Selector = fallback/OR: return the first child that is not Failure.
// `Current` (from Composite) remembers a Running child so the next tick resumes there.
public sealed class Selector : Composite
{
    public override Status Tick(Blackboard bb, float dt)
    {
        for (; Current < Children.Count; Current++)
        {
            var s = Children[Current].Tick(bb, dt);
            if (s == Status.Running) return Status.Running;            // resume here next tick
            Children[Current].Reset();                                // terminal child can re-enter cleanly
            if (s == Status.Success) { Current = 0; return Status.Success; }
        }
        Current = 0;
        return Status.Failure;                                         // every child failed
    }
}

This is the memory variant: it cannot be preempted. `Sequence`, `ReactiveSelector`, `ReactiveSequence`, `Parallel`, the decorators and `BehaviorTree` follow the same contract in `references/behavior-tree-core.md`.

Utility scoring in one snippet

// A consideration maps one raw fact to 0..1 through a response curve.
float Score(Blackboard bb)
{
    float distance01 = Curves.InverseLerp01(bb.Get<float>("distToPlayer"), 20f, 2f); // near = 1
    float health01   = Curves.Sigmoid(bb.Get<float>("health01"), k: 8f, mid: 0.4f);  // hurt = low
    // A 0 in any factor still vetoes the action; compensation stops each extra
    // consideration from dragging every score toward 0. No allocation per call.
    return Curves.Compensate(distance01 * health01, count: 2);
}

Selection: argmax with an inertia bonus of about 0.05 for the running action is the default; softmax (temperature about 0.2) when designers want variety without silly picks.

Headless tick test

Run this shape in the project's test runner (Unity Test Framework, GdUnit4, xUnit) before tuning by eye; `Assert` stands for the framework's assertion. A probe leaf counts ticks and resets, so lifecycle bugs show up as numbers.

sealed class Probe : Leaf
{
    public int Ticks, Resets; readonly int _after; readonly Status _end;
    public Probe(int after, Status end = Status.Success) { _after = after; _end = end; }
    public override Status Tick(Blackboard bb, float dt) => ++Ticks >= _after ? _end : Status.Running;
    public override void Reset() { Ticks = 0; Resets++; }
}
// Guard lost mid-action: the running action must be aborted, not resumed.
var bb = new Blackboard(); var chase = new Probe(after: 3);
var seq = new ReactiveSequence().Add(new Condition(b => b.Get<bool>("sees"))).Add(chase);
bb.Set("sees", true);  Assert(seq.Tick(bb, 0.1f) == Status.Running);
bb.Set("sees", false); Assert(seq.Tick(bb, 0.1f) == Status.Failure && ch
Read more
@gamedev-skills
Ships withawesome-gamedev-agent-skills

<img src="docs/assets/awesome-gamedev-agent-skills-banner.png" width="820" alt="awesome-gamedev-agent-skills — game-dev skills for AI coding agents.

Stars on GitHub

Momentum

stars in total
8.6k
Star history of gamedev-skills/awesome-gamedev-agent-skills on GitHub, a chart by star-history.com

More skills from other plugins.

brainstorming
Auto-invoked

brainstorming

You MUST use this before any creative work - creating features, building components, adding functionality, modifying behavior, or…

@obra@obraInstall Skill
ask-matt

ask-matt

Ask which skill or flow fits your situation. A router over the skills in this repo.

skills13d309k
@mattpocock@mattpocockInstall Skill
accessibility
Auto-invoked

accessibility

Design, implement, and audit accessible UI to WCAG 2.2 Level AA across Web, iOS, and Android — semantic ARIA roles and labels,…

ecc2w293k
@affaan-m@affaan-mInstall Skill
compiler-commit

compiler-commit

Use when you want to verify compiler changes and commit with the correct convention. Runs tests, lint, and format, then commits…

react4d252k
@facebook@facebookInstall Skill

Questions about ai-behavior-trees-utility-ai.

Which coding agents can use ai-behavior-trees-utility-ai?
ai-behavior-trees-utility-ai installs in Claude Code, Codex, Cursor, Gemini CLI, GitHub Copilot, OpenCode and Windsurf with the skills CLI, using each agent's flag shown on this page. Flowy's auto-invocation, which fires a skill when your prompt matches, runs in Claude Code only.