Behaviour tree guard (Unity 6 / C#)

A minimal, package-free behaviour tree and a guard NPC that uses it. Companion code for behaviour-trees. Plain C# in two files; nothing to install.

Files

ScriptResponsibility
BehaviourTree.csThe framework: NodeState, BTNode, SequenceNode, SelectorNode, InverterNode, ConditionNode, ActionNode (about 100 lines)
GuardAI.csBuilds the guard’s tree in Awake, ticks it in Update, and holds the conditions and actions it calls

The tree

Selector "Root"                      (first branch that does not fail wins)
├── Sequence "Flee when hurt"
│   ├── Condition  Health low?
│   └── Action     Flee              (Running; regenerates health)
├── Sequence "Attack when close"
│   ├── Condition  Player in reach?
│   ├── Inverter   ── Condition  Cooling down?
│   └── Action     Strike            (Success)
├── Sequence "Chase when seen"
│   ├── Condition  Can see player?
│   └── Action     Chase             (Running)
└── Action "Patrol"                  (Running)

The tree is ticked from the root every frame and keeps no memory of which branch ran last, so a higher-priority branch interrupts a lower one the moment its condition comes true.

Scene setup (about 10 minutes)

  1. New 3D scene. Add a Plane at the origin for the floor.
  2. Player: add a Capsule, name it Player, place it at (6, 1, 6). It has no script: you move it by dragging it in the Scene view during Play mode.
  3. Waypoints: create three or four empty GameObjects (WP1, WP2, …) spread around the plane at y = 1.
  4. Guard: add another Capsule, name it Guard, place it at (0, 1, 0) and add GuardAI. Drag Player into Player and the waypoints into Waypoints in order.
  5. Optional walls: add some Cubes, put them on a new layer called Walls, and set Obstacle Mask on the guard to Walls. The guard can no longer see you through them.
  6. Press Play. Arrange the Game and Scene views side by side, select the guard to see its view cone (white), view radius (yellow) and reach (red), and watch Current Action in the Inspector. The capsule changes colour with the action: green patrol, yellow chase, red strike, cyan flee.

What to try

  • Drag the player into the guard’s view cone, then behind it. Why does the guard stop chasing as soon as you are behind it?
  • Stand within reach. Strikes are logged once per Attack Cooldown; between strikes the Inverter fails the attack branch and the guard falls through to Chase, which simply holds its distance.
  • Right-click the GuardAI header and choose Take 25 damage three times (or drag the Health slider below 30). The guard flees and regenerates; it only returns to the fight above 60. Set Recover Above to 30 to see why two thresholds are used.
  • Move the “Chase when seen” branch above “Attack when close” in Awake. What breaks, and why does order matter in a selector?

Extension ideas

  1. Memory: store the player’s last seen position and add a “Search last known position” branch between Chase and Patrol.
  2. Blackboard: move shared values (target, last seen position, health) into a small Blackboard class that nodes read, instead of fields on GuardAI.
  3. Wait node: write a WaitNode decorator or leaf that returns Running for N seconds, and make the guard pause at each waypoint.
  4. Debug view: give each node a LastState field and draw the tree with OnGUI so you can watch which branch is running.
  5. NavMesh: replace MoveTowards with a NavMeshAgent (AI Navigation package) so the guard paths around walls.
  6. Production tool: rebuild the same guard with Unity’s Behavior package (com.unity.behavior), a graph-based behaviour tree editor with runtime debugging, and compare the two.

Source files

2 items under this folder.