Temporarily Overriding Movement Intent with StateTree

GAIS_Cover
YuewuDev

Author

YuewuDev

Created: Oct 12, 2026Updated: Oct 12, 2026

Table of contents

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:

  1. Squad phase: Modifiers registered with PushSquadMovementIntentModifier run first and are suitable for tactical states shared by the whole squad.
  2. Unit phase: Modifiers registered with PushUnitMovementIntentModifier run 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 MovePolicy to ForceMove so automatic hysteresis does not stop the charge.
  • Increase SpeedMultiplier, or set bOverrideDesiredSpeed and DesiredSpeedOverride.
  • Append a project semantic tag such as GAIS.MovementIntent.Charge.
  • Keep DriverPolicy = Apply so 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:

  1. Bind SquadActor to the squad host.
  2. Set ModifierSet to a shared asset; use Inline Modifier Set only when the configuration belongs exclusively to this state.
  3. Set PriorityWithinScope above 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

ActivateMovementIntent

The Driver attempts to take control of the Unit. If it returns false, the active-driver state is not entered for that update.

ApplyMovementIntent

The Driver continuously writes final Intent into the project movement implementation. The CharacterMovement Driver writes speed and applies orientation when configured.

ReleaseMovementIntent

Releases control when Intent becomes invalid, DriverPolicy becomes Suspended, the Unit leaves the squad, or the squad becomes invalid. The CharacterMovement Driver restores original speed and rotation state when it still owns them.

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

  1. Configure normal DefaultMovementIntentResponseSettings and the CharacterMovement Driver in SquadDefinition.
  2. Create MI_Modifier_Charge with MovePolicy = ForceMove, SpeedMultiplier = 1.5, a charge tag, and DriverPolicy = Apply.
  3. Add Task: Run Squad Movement Intent Modifier Set to the squad StateTree's Charge state, reference the asset, and set PriorityWithinScope = 100.
  4. After entering Charge, every member's final Intent should show higher DesiredSpeed, bShouldMove = true, and the same formation-slot-derived target location.
  5. 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 PriorityWithinScope?

Intent is correct but the unit does not move

Is DriverPolicy = Apply, did the Driver activate, and does the Unit type satisfy Driver requirements?

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 ModifyMovementIntent alter members, formation, or binding sources and trigger stability protection?

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.