StateTree Integration

Documentation Unreal Engine AI StateTree

Graph-authored AI structure on top of the same combat pipeline as the native brain.


USECBrainComponent reads UEnemyAIConfig::DefaultStateTree. A path runs a runtime UBotStateTreeAIComponent; an empty path runs the native loop (build context, move, poll, execute) with no graph.

When to Use This

  • Patrol, investigate, search, or boss phases you want to author in a StateTree.
  • Different high-level states that share the same combat loop subtree.
  • Awareness-driven branches without wiring perception delegates by hand.
Leave Default State Tree empty when pure combat is enough. Scoring, movement, and execution stay the same in both modes.

How It Works

On possession, SECBrainComponent resolves the config through SECCombatControllerComponent::ResolveAIConfig:
  1. DefaultStateTree set: SECBrainComponent spawns UBotStateTreeAIComponent, takes the brain slot, and the component loads the asset on possession.
  2. DefaultStateTree empty: tick the native loop on SECBrainComponent (EvaluationCooldown default 0.1s).
  3. StateTree fails to start: log a warning and fall back to the native loop.
Call RefreshBrain() after you change the config at runtime without re-possessing.
Both paths call the same components: USECDecisionLibrary, MovementEvaluatorComponent, ActionEvaluationComponent. StateTree tasks are thin wrappers around those APIs.

Setup

Assign Default State Tree on UEnemyAIConfig. Copy Reference on the asset in the Content Browser and paste the soft path.
Details
Enemy AI Config (EnemyAIConfig)
Behavior
Default State Tree
Content/SoulslikeEnemyCombat/.../StateTree_SEC_Core
Decision Context Params
Aggression Level
1
Window Id Interval Seconds
1.0
LOS Trace Channel
Visibility
LOS Trace Complex
Empty Default State Tree runs the native combat loop. Decision Context Params feed Build Decision Context and the native brain.
Shipped showcase path (plugin content): Content/SoulslikeEnemyCombat/Showcase/AI/StateTree/StateTree_SEC_Core. StateTree_RoleBehavior is the combat-loop subtree shown in the screenshots below.
Requirements on the controller: SECCombatControllerComponent, ActionEvaluationComponent, MovementEvaluatorComponent, and SECBrainComponent. AEnemyControllerBase creates all four. Custom controllers need the same set.

Shipped Graph Layout

StateTree_SEC_Core picks Idle vs Combat. Combat runs a role subtree when the AI has a focus target and a non-None combat role.
StateTree_SEC_Core: Combat runs Attacker_Subtree while the AI has a target and combat role; Idle otherwise
StateTree_RoleBehavior is the combat loop subtree: poll focus and role, build context, poll action, branch to AI Movement or Do Action, then a short Recovery delay before polling again.
StateTree_RoleBehavior: Role state polls and branches to Move or Action, with Recovery before returning to Role
The graph Recovery state is a timed pause in the tree. Action Recovery Time on EnemyAIConfig only suspends offensive action selection while movement and reactions continue. Use Recovery for a brief settle; use ActionRecoveryTime to tune attack spacing.

Combat Loop Wiring

Bind these three tasks for the core fight loop:
Build Decision Context → Poll Action → Do Action

   AI Movement (parallel tick while in Move state)
Editor nameC++ structRole
Build Decision ContextFSTTask_BuildDecisionContextFills OutDecisionContext each tick via USECDecisionLibrary
Poll ActionFSTTask_PollActionScores on EvaluationCooldown; skips while an action runs
Do ActionFSTTask_DoActionCalls ExecuteAction; finishes on OnActionExecutionCompleted
AI MovementFSTTask_AIMovementCalls MovementEvaluatorComponent::EvaluateAndMove
Poll Action outputs OutChosenAction and bHasValidAction. Wire InChosenAction on Do Action from that output.
Do Action does not poll SEC.Action.End. It binds ActionEvaluationComponent::OnActionExecutionCompleted and finishes the StateTree task when execution ends.

Task Reference

Combat tasks use USECDecisionLibrary and eval/movement components. Role polling uses SECCombatControllerComponent::ResolveCurrentCombatRole. Focus and awareness tasks read the controller directly (GetFocusActor, USECAwarenessComponent).

Combat and decision

Editor nameUse
Build Decision ContextEvery tick in active combat states
Poll ActionPick the next action from the active ActionSet
Do ActionRun the chosen action
AI MovementTactical positioning while in a Move state

Polling helpers

Editor nameUse
Poll Combat RoleExposes CurrentRole and OnRoleChanged
Poll Focus ActorExposes FocusActor and bHasTarget from GetFocusActor()

Role sync (optional)

Editor nameUse
Sync Action SetPoll pawn SECActionSetComponent into ActionEvaluationComponent
Sync Reaction SetPoll pawn SECReactionSetComponent into ReactionEvaluationComponent
Sync Movement ProfilePoll EnemyAIConfig movement profile into MovementEvaluatorComponent
SECCombatControllerComponent calls SyncStateForCombatRole when combat roles change (action set, reaction set, movement profile). You only need the Sync tasks for custom controllers or when the graph must own sync timing.

Manual override

Editor nameUse
UpdateActionSetSets a fixed ActionSet on ActionEvaluationComponent (boss phase swap, debug)

Conditions

Editor nameC++ structUse
ASC Has TagFSTCondition_ASCHasTagGate on pawn ASC tags (HasAny / HasAll)
Awareness State At LeastFSTCondition_AwarenessStateBranch on USECAwarenessComponent state
ASC Has Tag binds Pawn, not the controller. The pawn must implement IAbilitySystemInterface.
Awareness State At Least compares GetAwarenessState(Target) to MinState. Empty Target uses the most-aware target. Ordering: Unknown < Forgotten < Suspicious < Lost < Detected.

Integration

SystemStateTree touchpoint
Action SystemPoll Action / Do Action; FDecisionContext::bHasLOS gates LOS actions
Movement SystemAI Movement; profiles sync on role change or Sync Movement Profile
Awareness SystemAwareness State At Least; Poll Last Known Location
Combat RolesPoll Combat Role; Core tree checks role before Combat
Multiplayer SupportSame server-side pipeline; graph runs on authority

AdvancedBuild Decision Context fields
Resolved once on EnterState from UEnemyAIConfig::DecisionContextParams via ResolveDecisionParams.
Each tick fills FDecisionContext:
  • Spatial: distance, angle, bHasLOS (eyes-to-eyes trace on LineOfSightTraceChannel).
  • Self: HealthPercentage from BasicHealthComponent if present, else 1.0; Stamina fixed at 100; Speed from pawn velocity; AggressionLevel from params.
  • Target: ThreatLevel from focus distance and facing (not ThreatDetectionComponent).
  • Tags: ASC tags on self and target; WorldTags from USECWorldTagSubsystem on the server.
GAS-backed health belongs in an Attribute Scorer on the action, not in the context struct.
AdvancedBlueprint decision readers
NodeReturns
Get Decision Context (AI Controller)FDecisionContext
Get Decision Context (Pawn)Same; resolves the pawn's controller first
Resolve Decision ParamsFSECDecisionContextParams from config
All three are server-only. Off authority they return defaults.
A getter uses a fresh window and seed (DeltaTime = 0), so WindowId / Seed will not match the running brain cadence. Read spatial, health, threat, and tag fields live; do not expect identical score jitter.
AdvancedAwareness tasks

Poll Last Known Location

Reads USECAwarenessComponent::GetLastKnownLocation. Outputs LastKnownLocation, AgeSeconds, bValid, ResolvedTarget. Bind LastKnownLocation to AI Move To for search/investigate states.
Empty Target uses GetMostAwareTarget.
Typical loop: patrol below Suspicious; investigate at Suspicious; combat at Detected; search at Lost; drop to Idle on Forgotten. See Awareness System.
AdvancedBotStateTreeAIComponent
Runtime component created by SECBrainComponent when a StateTree is configured.
Details
Bot State Tree AI Component (BotStateTreeAIComponent)
AI|StateTree
Auto Initialize From Config
Fallback State Tree
None
SECBrainComponent creates this at runtime when Default State Tree is set. Custom controllers can add it manually.
Init order on possession: controller cached config → pawn IEnemyAIConfigProviderFallbackStateTree.
AddLinkedStateTreeOverride swaps subtrees by gameplay tag for variants.