Temporarily Overriding Movement Intent with StateTree
The base values of Movement Intent come from formation slots: FormationComponent provides slot world transforms, SquadComponent builds FGAIS_MovementIntentResolveState for each member, and the result is reduced into each Unit's FGAIS_SquadUnitMovementIntent. A StateTree Modifier is appropriate when movement guidance should change only while a temporary state is active, such as charging, suppression, slow formation movement, a debuff, or suspended Driver application.
Related documentation: see Connecting Movement Intent to the Movement System for basic integration, Squad, Role, and Unit Parameters for fields, Formation and Movement Intent Extension Points for extension boundaries, and Formation Editor and Slot Diagnostics for troubleshooting.
Execution Order
Whenever Movement Intent is refreshed, the Definition's DefaultMovementIntentResponseSettings and DefaultMovementIntentTags initialize the draft first. Formation slots provide TargetLocation, DesiredOrientation, and SlotTags context, while squad average velocity is written into PredictionVelocity.
Modifiers then run in two phases:
- Squad phase: Modifiers registered with
PushSquadMovementIntentModifierrun first and are suitable for tactical states shared by the whole squad. - Unit phase: Modifiers registered with
PushUnitMovementIntentModifierrun afterward and are suitable for an individual member's exceptional state or role override.
Within the same scope, Modifiers execute from low to high PriorityWithinScope. Higher values run later and can overwrite MovePolicy, DriverPolicy, speed, prediction, and MovementIntentTags written by earlier Modifiers.
1. Create a Modifier Set
Create a UGAIS_MovementIntentModifierSet asset and add one or more UGAIS_MovementIntentModifier instances. ModifyMovementIntent should modify only the supplied FGAIS_MovementIntentResolveState. Do not change squad members, formation, commands, Abilities, or MovementComponent from the callback, and do not push/pop another Modifier from inside it.
Typical charge-state changes:
- Set
MovePolicytoForceMoveso automatic hysteresis does not stop the charge. - Increase
SpeedMultiplier, or setbOverrideDesiredSpeedandDesiredSpeedOverride. - Append a project semantic tag such as
GAIS.MovementIntent.Charge. - Keep
DriverPolicy = Applyso the configured Driver continues consuming the final Intent.
If the state should let StateTree or the project movement layer read Intent while temporarily preventing the Squad Driver from applying it, set DriverPolicy to Suspended. Intent remains mirrored to UnitComponent, but the Driver releases or does not activate control.
2. Register the Temporary State in StateTree
In the squad StateTree's charge state, add Task: Run Squad Movement Intent Modifier Set:
- Bind
SquadActorto the squad host. - Set
ModifierSetto a shared asset; useInline Modifier Setonly when the configuration belongs exclusively to this state. - Set
PriorityWithinScopeabove ordinary policies, for example 100.
On state entry, the Task calls PushSquadMovementIntentModifier, stores an FGAIS_MovementIntentModifierHandle, and remains Running. On state exit, it calls PopMovementIntentModifier; after the Handle is removed, Intent is requested to resolve again.
To modify only one member, use Task: Run Unit Movement Intent Modifier Set in the Unit StateTree and bind UnitActor. It finds the owning squad from UnitComponent and calls PushUnitMovementIntentModifier.
3. Verify Driver Activation and Release
UGAIS_UnitMovementIntentDriver has three stages:
Stage | Description |
|---|---|
| The Driver attempts to take control of the Unit. If it returns false, the active-driver state is not entered for that update. |
| The Driver continuously writes final Intent into the project movement implementation. The CharacterMovement Driver writes speed and applies orientation when configured. |
| Releases control when Intent becomes invalid, |
The built-in UGAIS_UnitMovementIntentDriver_NoOp represents an explicit no-op policy. It accepts an authoritative Unit but does not move it. UGAIS_UnitMovementIntentDriver_CharacterMovement targets ACharacter + UCharacterMovementComponent, can write MaxWalkSpeed, and can use DesiredOrientation to control AIController Gameplay Focus or character rotation.
Complete Scenario: Squad Charge
- Configure normal
DefaultMovementIntentResponseSettingsand the CharacterMovement Driver in SquadDefinition. - Create
MI_Modifier_ChargewithMovePolicy = ForceMove,SpeedMultiplier = 1.5, a charge tag, andDriverPolicy = Apply. - Add
Task: Run Squad Movement Intent Modifier Setto the squad StateTree'sChargestate, reference the asset, and setPriorityWithinScope = 100. - After entering
Charge, every member's final Intent should show higherDesiredSpeed,bShouldMove = true, and the same formation-slot-derived target location. - After leaving
Charge, the Task automatically pops the Handle. The next resolution returns to Definition defaults; the Driver releases obsolete overrides and restores the original movement control state it owned.
Troubleshooting Checklist
Symptom | Check |
|---|---|
Task immediately enters Failed | Is the Actor valid and authoritative, is the Squad Active, and does ModifierSet contain a valid Modifier? |
Squad Modifier does not affect final Unit result | Was it overwritten by the later Unit phase or a higher |
Intent is correct but the unit does not move | Is |
Charge speed remains after leaving the state | Did StateTree truly leave the state, was the Handle popped, and does the Owner still have another Modifier? |
Effect stacks after repeated entry | Was a manually pushed Handle not saved, or does Owner cleanup miss an exceptional exit path? |
Modifier intermittently fails | Does |
Debug in this order: Modifier order → final Intent → Driver activation → Owner/Handle cleanup → StateTree exit. If final Intent is correct but the Driver does not activate, the issue is movement integration. If Modifier order is wrong, adjust PriorityWithinScope and choose the correct Squad or Unit phase first.
