Skip to content

Simple Patrol, Chase, Attack, and AI Debugging

A first Behavior Tree enemy should be boring on purpose: patrol when nothing is known, chase when there is a target, attack in range, search briefly when the target is lost, and debug each layer before guessing.

At a glance

Use this for: the first complete enemy loop after sensing and basic Move To already work - Main pieces: AIController, Blackboard keys, Behavior Tree branches, Move To tasks, perception, timers/search memory, attack tasks, and AI debugging tools - Beginner rule: prove pawn, controller, NavMesh, perception, Blackboard, tree branch, and task result in that order - Official docs: Behavior Tree Quick Start Guide, AI Debugging, and Using the Gameplay Debugger

The one-minute version

  • Build the enemy as a small state loop: patrol, chase, search, attack.
  • The AIController starts the Behavior Tree and updates Blackboard keys from perception.
  • The Blackboard stores facts: TargetActor, LastKnownLocation, PatrolLocation, HasLineOfSight, InAttackRange, and maybe SearchExpiresAt.
  • The Behavior Tree chooses branches. It should not hide sensing, movement setup, and combat implementation in one giant custom task.
  • Patrol chooses a valid destination and moves there. Chase moves toward the target. Search moves to the last known location. Attack calls a pawn/combat function when in range.
  • Debug AI in layers: pawn possession, NavMesh, perception event, Blackboard values, active Behavior Tree branch, Move To result, then attack function.
  • The AI Debugger opens with apostrophe ('); default categories show NavMesh, general AI, Behavior Tree, EQS, and Perception information.

The target shape

A first useful Behavior Tree can be this simple:

Root
-> Selector AI Root
   -> Sequence Attack
      Decorator: TargetActor is set
      Decorator: InAttackRange == true
      Observer Aborts = Both on the range/target condition as needed
      Task: AttackTarget

   -> Sequence Chase
      Decorator: TargetActor is set
      Observer Aborts = Both
      Service: UpdateCombatFacts
      Move To TargetActor

   -> Sequence Search
      Decorator: LastKnownLocation is set
      Decorator: TargetActor is not set
      Move To LastKnownLocation
      Wait 1.0
      Task: ClearSearchLocation

   -> Sequence Patrol
      Task or Service: ChoosePatrolLocation
      Move To PatrolLocation
      Wait 1.5 with random deviation

That is enough to teach the real loop. Add strafing, cover, team rules, Environmental Query System (EQS), complex cooldowns, and multiple attack types after the branch structure is visible and debuggable.

Blackboard keys

Use clear key names. A first enemy might have:

Key Type Written by Read by
TargetActor Object, base Actor or Pawn AI Perception / Pawn Sensing / damage wake-up Attack and chase decorators, Move To
LastKnownLocation Vector Perception update, chase service Search Move To
PatrolLocation Vector patrol task/service Patrol Move To
HasLineOfSight Bool perception or line-of-sight service chase/search decorators
InAttackRange Bool service or attack-range check attack decorator
AttackRange Float controller setup or enemy defaults service/task checks
HomeLocation Vector controller On Possess return-home or search fallback

Do not make the Behavior Tree guess facts from scratch in every task. Put changing facts into the Blackboard and watch them while debugging.

AIController setup

The controller starts the tree and owns the main sensing-to-Blackboard bridge:

BP_EnemyAIController

On Possess(NewPawn)
-> Run Behavior Tree BT_Enemy
-> Branch Return Value
   true:
       Get Blackboard Component
       Set HomeLocation = NewPawn location
       Set AttackRange from enemy defaults
   false:
       Print "BT_Enemy failed to start"

For AI Perception:

On Target Perception Updated(Actor, Stimulus)
-> filter actor is player/valid target
-> Break AI Stimulus
-> if Successfully Sensed:
       Blackboard.TargetActor = Actor
       Blackboard.HasLineOfSight = true
       Blackboard.LastKnownLocation = Actor location
       clear forget/search timer
   else:
       Blackboard.HasLineOfSight = false
       Blackboard.LastKnownLocation = Stimulus Location
       start short forget timer

When the forget timer fires:

ForgetTarget
-> if HasLineOfSight is false:
       Clear TargetActor

That leaves LastKnownLocation available for search while target memory is being cleared deliberately.

Patrol

Patrol needs a valid destination before the Move To task runs.

Random patrol:

BTT_ChoosePatrolLocation
-> Event Receive Execute AI
-> Get Controlled Pawn
-> Get Actor Location
-> Get Random Reachable Point in Radius
-> Branch Return Value
   true:
       Set Blackboard PatrolLocation = RandomLocation
       Finish Execute true
   false:
       Set Blackboard PatrolLocation = CurrentLocation
       Finish Execute true

Patrol path:

Enemy pawn has PatrolPoints array or PatrolPath actor reference
-> BTT_ChooseNextPatrolPoint chooses next index
-> Set Blackboard PatrolLocation = point location
-> Finish Execute true

Then the tree:

Sequence Patrol
-> BTT_ChoosePatrolLocation
-> Move To PatrolLocation
-> Wait

If patrol never moves, debug PatrolLocation, then debug NavMesh. A valid vector in the Blackboard does not guarantee a reachable path.

Chase

Chase should not be "call Move To from perception every time sight updates." Let perception set the target key and let the tree run the chase branch.

Sequence Chase
    Decorator: TargetActor Is Set
    Observer Aborts = Both
    Service: UpdateCombatFacts
    Move To TargetActor

The service can update:

DistanceToTarget
InAttackRange
LastKnownLocation while target is visible/valid

The Move To task should use TargetActor and a reasonable acceptable radius. If the goal is a moving player, enable goal tracking/blackboard observation when your Move To settings support it.

If chase keeps restarting or twitching, check whether multiple systems are issuing movement requests: AI Move To from perception, Behavior Tree Move To, and pawn code can fight each other. Pick one owner for movement while the tree is active.

Search is what happens after the AI loses target certainty but still knows a place to investigate.

Simple search branch:

Sequence Search
    Decorator: LastKnownLocation Is Set
    Decorator: TargetActor Is Not Set
    Move To LastKnownLocation
    Wait 1.0
    Task: Clear LastKnownLocation

That creates a readable behavior:

see player -> chase
lose sight -> remember last known location
forget target after short timer
move to last known location
wait/search briefly
clear search location
fall back to patrol

Do not clear every key at the first lost-sight event unless you want the AI to forget instantly. The usual beginner mistake is one of two extremes: never clear the target, or clear all memory immediately and never search.

Attack

Attack should be a task that asks the pawn/combat component to act.

Tree branch:

Sequence Attack
    Decorator: TargetActor Is Set
    Decorator: InAttackRange == true
    Observer Aborts = Both or Self depending on layout
    Task: AttackTarget

Task shape:

BTT_AttackTarget
-> Event Receive Execute AI
-> Get Controlled Pawn
-> Get Blackboard TargetActor
-> Is Valid both
   false: Finish Execute false
-> call pawn function TryMeleeAttack(TargetActor)
-> Finish Execute with returned success

The pawn or combat component owns details:

  • cooldown checks;
  • montage playback;
  • damage window;
  • weapon trace;
  • hit effects;
  • health/damage application.

The task should not directly edit every combat sub-object unless this is a throwaway prototype. Calling one named function gives you a clean debug point: "did the Behavior Tree request an attack, and did the pawn accept it?"

Avoid flicker between chase and attack

If the AI switches rapidly between chase and attack, the range rule may be too tight:

Attack starts at <= 150
Chase resumes at > 150

Capsules, movement, and path following can push the distance around the exact threshold. Use a small buffer:

Enter attack at <= 150
Leave attack at >= 190

Or use cooldown/decorator rules so the AI does not evaluate the attack branch every tiny distance change.

This is not a Behavior Tree failure. It is a state-threshold design problem.

Debug in layers

When the enemy is broken, do not start by rewriting the tree. Prove each layer.

  1. Pawn exists and is possessed. Print or debug Get Controller on the enemy pawn. It should be your AIController, not None and not a PlayerController.
  2. NavMesh is usable. Press P in the editor viewport or use AI Debugger NavMesh view. Confirm green navigation under the enemy and the goal.
  3. Perception fires. Print from On See Pawn or On Target Perception Updated, including actor name and Successfully Sensed.
  4. Blackboard keys change. Watch TargetActor, LastKnownLocation, HasLineOfSight, and InAttackRange in the Behavior Tree debugger.
  5. Behavior Tree is running. Use AI Debugger general AI/Behavior Tree categories to confirm the tree class and active task/branch.
  6. Branch decorators pass or fail for a reason. Check the exact key names and types each decorator reads.
  7. Move To result makes sense. If movement fails, return to NavMesh, valid target, acceptable radius, pawn movement component, and competing movement requests.
  8. Attack function accepts the request. Put a breakpoint or print inside TryMeleeAttack, not only in the task.

This order matters because each layer depends on the previous one.

AI Debugger

During Play In Editor, press apostrophe (') to open the AI Debugger. The default categories are useful for this exact workflow:

Key What to inspect
Numpad 0 NavMesh data.
Numpad 1 General AI information: controller, pawn, path following, active behavior/task.
Numpad 2 Behavior Tree branch plus Blackboard key values.
Numpad 3 Environmental Query System data, if you use EQS.
Numpad 4 AI Perception information such as sight/hearing stimuli.

The Behavior Tree view is especially useful because it shows two truths at once: which branch is active and which Blackboard values the tree sees. If the key value is wrong there, the tree is reacting to bad data. If the key value is right but the branch is wrong, inspect decorators and priority order.

Behavior Tree breakpoints

Behavior Tree assets can use breakpoints much like normal Blueprints. Use them when the debugger display tells you "this branch/task is suspicious" and you need to stop at the exact node.

Useful breakpoints:

  • custom task Event Receive Execute AI;
  • custom task abort event;
  • service activation/tick;
  • the pawn function called by an attack task;
  • perception event in the AIController.

Pick the live AI instance you are debugging. Watching the wrong enemy is just as misleading in AI as it is in any other Blueprint.

Common failure patterns

Symptom Likely first place to check
Enemy never moves Possession, AIController class, NavMesh, Move To key, movement component.
Enemy never sees player Perception component, sight config, stimuli source/affiliation, line of sight, sensing interval.
Blackboard target is always empty Perception event did not set the key, set wrong key name/type, or used wrong controller instance.
Chase branch never activates Branch priority, TargetActor decorator, observer aborts, Blackboard value.
Enemy sees player but waits before chasing Missing Lower Priority/Both abort on chase condition, or current lower-priority task must finish.
Enemy never returns to patrol Target key or line-of-sight key never clears, search branch never clears last known location.
Enemy attacks from too far away Attack range key/decorator is wrong, acceptable radius is too large, or distance is measured from origins.
Enemy does not attack in range InAttackRange service/key is stale, attack branch is lower priority than chase, or attack task fails.
Tree is stuck on custom task Task never calls Finish Execute, or latent work never reports completion.
Works for one enemy only Hard cast, missing component, wrong Blackboard/key defaults, or per-instance settings differ.

Every row has a concrete next probe. Use prints, breakpoints, and the AI Debugger to find the first false assumption.

What this pattern does not do

  • It does not make perception, NavMesh, or possession work automatically.
  • It does not require EQS, StateTree, team attitudes, or custom senses.
  • It does not make attack montage timing or damage authoritative by itself.
  • It does not solve multiplayer replication. AI decisions and damage should usually be server-owned in multiplayer.
  • It does not replace pawn/combat functions with Behavior Tree tasks.
  • It does not guarantee good game feel. Ranges, speeds, cooldowns, and aborts still need tuning.

Lookalikes - which one do I want?

Tool Use when Watch out
Patrol branch AI should move through random or authored points when idle. Needs valid reachable locations.
Chase branch AI has a target actor and should close distance. Do not also spam AI Move To from perception.
Search branch AI lost target but has a last known location. Clear search memory deliberately when done.
Attack branch Target is valid and in range. Task should call pawn/combat behavior, not own every attack detail.
AI Debugger You need live NavMesh, AI, Behavior Tree, Blackboard, and perception evidence. It observes; it does not fix setup.
Blueprint breakpoints You need to pause a task/service/controller event. Must target the live instance that is running.
Print String You need quick proof a path ran and what value it saw. Remove or key noisy prints.
Visual Logger / EQS debugger You are debugging environment query choices or recorded AI decisions. More advanced than the first patrol/chase loop.

Rule of thumb: build the smallest readable enemy loop first, then debug the layer that owns the fact you distrust.

Going deeper