LCM Nav3D Free demo Get it on Fab

Tutorial 04: The Same Agent in StateTree

TL;DR: Identical flight behaviour to Tutorial 03, authored in StateTree instead of a Behavior Tree. Swap the AIController's RunBehaviorTree for a StateTreeAIComponent, and bind the Fly To task's TargetLocation instead of a blackboard key.

Time: ~15 minutes. Do Tutorial 03 first. This chapter only covers the differences.


Should you use StateTree or Behavior Tree?

Both are fully supported and produce the same flight. Pick on authoring preference, not capability.

Behavior Tree StateTree
Goal comes from A blackboard key Bound instance data on the task
Best at Reactive priority ordering Explicit states and transitions
Maturity Stable Epic's StateTree plugin is experimental on 5.2, stable from 5.3

The 5.2 caveat. The plugin is flagged beta on 5.2 only because Epic's StateTree plugin is experimental there. If you're shipping on 5.2 and want the most conservative option, use the Behavior Tree path. On 5.3+ this distinction disappears.


Step 1: The pawn

Exactly as Tutorial 03 Step 2: a Pawn with Floating Pawn Movement, a visible mesh, and Auto Possess AI = Placed in World or Spawned.

Nothing StateTree-specific here.


Step 2: Create the StateTree asset

  1. Content Browser → Add → Artificial Intelligence → StateTree.
  2. When it asks for a schema, choose StateTree AI Component Schema. This matters: the AI schema is what exposes the AI context (controller and pawn) the Fly To task needs.
  3. Name it ST_MyFlyer.

Step 3: Add the Fly To task

  1. Open ST_MyFlyer. You'll have a Root state.
  2. Add a child state and name it FlyToGoal.
  3. With that state selected, in the Tasks section click + and choose Fly To (listed under the LCM Nav3D category).
  4. Set the task's properties:
Field Value Notes
Target Location your goal See binding below
Acceptance Radius 100
Flight Speed 600 Match your Floating Pawn Movement Max Speed

Binding the goal

This is the structural difference from Behavior Trees. There's no blackboard; you bind the task's Target Location to a value.

The simplest approach is a StateTree parameter:

  1. Select the Root state → in Parameters, add a parameter: Name GoalLocation, Type Vector.
  2. Select the Fly To task → click the bind icon next to Target Location → choose GoalLocation.

Now anything that sets that parameter drives the agent.

Chasing instead. The task also has a Target Actor field. Set or bind that and the agent chases the actor, using the same prediction logic as the BT version. Note the ST chase path requires Target Actor to be non-null. If you want a fixed point, use Target Location.

Why Fly To has its own Target Actor field

This trips up everyone arriving from Behavior Trees, so it is worth stating plainly: that field is not an alternative to Find Actor By Tag - it is what Find Actor By Tag plugs into.

A Behavior Tree has a blackboard, so a BT task can name a key and read whatever wrote it. StateTree has no blackboard. Data moves by property binding from one node's output to another node's input, which means the consuming node must declare a field to receive the value. Delete Target Actor and there is nothing left to bind to.

This is the engine's own convention, not a plugin invention. Epic's Move To task carries exactly the same pair - a Destination vector plus an optional Target Actor, with the actor winning when both are set.

Fly To needs the live actor rather than a resolved position because it re-samples the target's location and velocity every tick to fly a lead intercept. A one-shot position would send the agent to where the target was when the state began.

Resolve the actor with the evaluator, not the task

Two nodes resolve a tag. Pick by whether you want a snapshot or a live reference:

Find Actor By Tag (task) Find Actor By Tag (evaluator)
Resolves Once, on state entry Continuously, on Refresh Interval
Found Actor Location Frozen at entry Refreshed every tick
Target destroyed / respawns Stays stale until re-entry Re-resolves immediately
Costs a state Yes - unless Stay Running After Resolve lets it share one No
Visible to Nodes ordered after it The whole subtree of the state it sits on
Can fail a state Yes (returns Failed) No - gate on its b Found Actor output

Prefer the evaluator for goal sources. Put it on the parent state, set Tag to Player, and bind Fly To's Target Actor to its Found Actor. One resolver then feeds Fly To, Look At, conditions and transitions at once, and it stays correct when the target dies or streams in late.

Only the search is throttled by Refresh Interval (0.5 s by default); the location output refreshes every tick off the cached pointer, so a moving target is never stale. Reach for the task form when a frozen-on-entry snapshot is the point - picking a patrol anchor and committing to it - or when you specifically want the state to fail if nothing carries the tag.

Roaming between tagged goals. The task form also has Pick Random Match: tag every goal actor in the level (say, Goal), put the task ahead of Fly To in one state, and tick Stay Running After Resolve - this switch is what lets the two tasks share a state. Off, the resolve finishes on entry, and a task that finishes finishes its whole state: Fly To is entered but never ticked, and the agent stands still. Bind Fly To's Target Location to Found Actor Location, then add an On State Completed transition back into the same state - as a Go to State transition targeting the state itself, not Tree Succeeded, which would end the whole run after the first leg. Each re-entry draws a fresh random goal - Avoid Repeat Pick (on by default) guarantees it is never the goal the agent is already sitting on - and every agent runs its own tree instance, so a crowd sharing one StateTree asset spreads across the goal set by itself. This is the tagged-actor twin of the Pick Random Goal task, which does the same loop with random points instead of placed actors and carries the same Stay Running After Resolve switch for the same reason.

A StateTree with GoToA, FlyToA, GoToB and FlyToB states, evaluators in the left panel, and the LCM Fly To task's Details on the right

Figure 04.1 - A two-goal patrol, the StateTree way. This is the shipped STS_FlightBehavior. Each FlyTo* state runs one LCM Fly To task and transitions On State Completed to the next, so the patrol loop is expressed as states and transitions rather than as a Behavior Tree Loop decorator. The Find Actor By Tag states in between are what fill in each goal at runtime.

Note the Evaluators in the left panel - E State GoalA and E State GoalB. That is the StateTree replacement for a blackboard: an evaluator produces a value, and the task binds to it.

The LCM Fly To StateTree task Details panel, every row carrying a Bind control on the right

Figure 04.2 - The same task, in StateTree form. The properties are the ones you already met in Tutorial 03 - the difference is the Bind column down the right-hand edge. In StateTree every field can be driven by an evaluator, a parameter or another state's output, not just the goal.

In this shot Target Actor is bound to E State GoalA.Goal while everything else holds a literal, which is the normal arrangement: bind what changes, type what does not.

Frontier Stall Recovery reads Inherit (project default) here, and the task cannot tell you what that resolves to. A StateTree asset is not owned by a level, so it does not know which manager it will run under. The manager's 7. Runtime State ▸ Frontier Stall Recovery (auto) row is the one place that answer exists.


Step 4: The AI Controller

  1. Create an AIController Blueprint, BP_MyFlyerAI_ST.

  2. Add a StateTree AI Component.

  3. Select it and set State Tree to ST_MyFlyer.

  4. Set the goal parameter on BeginPlay. On the component, use Set Parameter (or set the value before the tree starts) with:

    • Name: GoalLocation
    • Value: e.g. GetActorOfClass(TargetPoint) → GetActorLocation
  5. On BP_MyFlyer → Class Defaults → AI Controller Class = BP_MyFlyerAI_ST.

Unlike the BT path there is no Run Behavior Tree call: the StateTreeAIComponent starts the tree itself.


Step 5: Fly

Place a TargetPoint and your pawn, press Play. Same result as Tutorial 03: the pawn routes through 3D space to the goal.


The rest of the StateTree surface

The plugin ships a full StateTree task set, all under the LCM Nav3D category:

Task Does
Fly To The flagship: 3D navigation to a point or actor
Patrol Cycle through a set of points
Orbit Target Circle a target at a radius
Land Descend and touch down
Look At / Turn In Place Orientation without translation
Find Cover Move to tactical cover
Evade CBS Avoidance-driven evasion
Follow Flow Field Follow a shared crowd steering field
Chunk Prewarm / Streaming Aware Infinite-world streaming helpers
Find Actor By Tag / Pick Random Goal / Wait Utility
Use Smart Object Smart Object interaction

Plus conditions (Path Exists, Is At Location, Chunk Loaded, Has Line Of Sight, Is In Cover, Health Below, Path Length Below, Threat Visible Via Perception) and evaluators (Path Status, Path Progress, Navigation Load, Threat Field, Squad, Memory, Perception Relay, Find Actor By Tag).

Reading a node's outputs. Fields a node produces are grouped under an Output section and drawn as read-only OUT pins - those are the ones you bind from. Everything else is a parameter you either type a value into or bind to.

Every one is listed with its full property set in the API Reference.


Behaviour differences worth knowing

The BT and ST Fly To tasks are built for parity, but a few things genuinely differ:

Behavior Tree StateTree
Goal source Blackboard key (Vector or Actor) Target Location or Target Actor instance data
Chase Works with a Vector or Actor key Requires a non-null Target Actor
Arrival reported Against the pre-move location that frame Against the post-move location

The arrival difference is a sub-frame detail: it only matters if you're comparing the two side-by-side with a very small Acceptance Radius.


Troubleshooting

Symptom Cause
Fly To isn't in the task list Asset wasn't created with the StateTree AI Component Schema
Task fails immediately No LcmNavigationManagerSVO in the level, or the goal is outside/inside geometry
Agent never starts StateTreeAIComponent has no State Tree assigned, or Auto Possess AI is off
Goal is always (0,0,0) The parameter isn't bound, or is set after the tree already ran

More: Troubleshooting.


Next: Tutorial 05: Dynamic Obstacles. Make the world move and watch agents reroute.