Movement System
Documentation Unreal Engine AI Movement
Tactical AI positioning with direction sampling, distance maintenance, strafing, and pawn avoidance.
Intelligent positioning that makes AI feel alive. Approaching, strafing, retreating, and avoiding obstacles without looking robotic.
When to Use This
- Any AI that needs tactical positioning around a target
- Melee enemies that maintain attack distance
- Ranged enemies that keep their distance
- Group AI where pawns need to avoid each other
- Boss fights with phase-based positioning changes
This system can be used independently of the other plugin systems.
Core Concepts
Direction Sampling
Every tick, the system evaluates 16 directions around the AI and scores each one:
Interactive Direction Sampling
Move mouse to simulate Target AI Pawn
Target
Ideal Range
Green arrows = High Score (Preferred Direction)
Each direction gets a score based on:
- Distance to target: Does moving here get us closer to the ideal range?
- Angle to target: Are we facing the target?
- Avoidance: Is there a wall or another pawn in this direction?
- Positioning Rules: Custom modifiers you define (see below)
The highest-scoring direction wins. The result is movement that looks intelligent because it considers multiple factors at the same time.
Distance Maintenance
The AI tries to stay within a tolerance zone around a single desired distance:
Too Close Comfort Zone Too Far
|-----------|=============|-----------|
0 -tolerance distance +tolerance inf
(approach) (strafe) (retreat)
Configure this via
DesiredDistance and DistanceTolerance (percentage of desired distance). For example, DesiredDistance = 400 with DistanceTolerance = 0.2 means the AI is comfortable anywhere between 320 and 480 cm.Strafing
The AI naturally circles the target while maintaining distance:
- AutoStrafeSwap: Automatically changes direction when the opposite side scores significantly better
- Strafe Fatigue: After continuous strafing, the AI rests briefly (broadcasts
OnStrafeResting) - Threat-Triggered Swap: If configured, the AI swaps direction when the player stares at it. Set per
MovementBehaviorProfile(requires Threat Detection)
Movement Layers
The system operates in two layers, switching automatically based on distance:
| Layer | When | How |
|---|---|---|
| Tactical | Target is within HybridSwitchDistance | Direct movement input for responsive strafing and positioning |
| Strategic | Target is far away or path-blocked | NavMesh pathfinding to navigate around obstacles |
The switch happens at
HybridSwitchDistance (default: 800 units). This gives you the reliability of pathfinding at range and the snappy feel of direct control up close.Quick Setup
Option 1: Use a Preset
// In your AI Controller's BeginPlay:
MovementEvaluator->ApplyBehaviorConfig(FMovementBehaviorConfig::MakeAttacker());Available presets:
MakeDefault(): Balanced defaults (400 cm, moderate rest)MakeAttacker(): Close-range aggressive (300 cm, minimal rest)MakeWaiter(): Far-range passive (600 cm, frequent rest)MakeFlanker(): Medium distance repositioning (400 cm, quick pauses)MakeSupporter(): Medium-far range (500 cm, moderate rest)MakeElite(): Aggressive, relentless pressure (350 cm, very short rest)
Option 2: Use a Data Asset
- Create a
MovementBehaviorProfiledata asset (Right-click, Miscellaneous, Data Asset, MovementBehaviorProfile) - Configure desired distance, rest times, threat response, and positioning rules
- Apply it:
MovementEvaluator->ApplyBehaviorProfile(MyProfile);Option 3: Configure at Runtime
MovementEvaluator->SetDesiredDistance(350.f);
MovementEvaluator->SetStrafeSide(1); // 1 = right, -1 = leftOption 4: Role-Based (Recommended)
The most automated path. The movement profile is determined by the AI's current combat role.
- Configure your EnemyAIConfig data asset.
- Assign profiles to roles (e.g., Attacker gets Aggressive, Waiter gets Passive).
- The
SECCombatControllerComponentsyncs automatically when the role changes.
// Called by SECCombatControllerComponent automatically when role changes:
MovementEvaluator->SyncForCombatRole(RoleTag, MyAIConfig);Key Properties
| Property | Default | Purpose |
|---|---|---|
NumSamples | 16 | Directions to evaluate per tick |
DesiredDistance | 400 | Ideal distance to maintain (cm) |
DistanceTolerance | 0.2 | Comfort zone as percentage of desired distance |
bEnableAvoidance | true | Avoid other AI pawns |
AvoidanceRadius | 100 | How far to stay from other pawns (cm) |
bEnableHybridMovement | true | Switch between pathfinding and direct input |
HybridSwitchDistance | 800 | Distance to switch between Tactical and Strategic |
bAutoStrafeSwap | true | Automatically swap strafe direction |
StrafeSwapThreshold | 3.0 | Score ratio required to trigger auto-swap |
StrafeTimeLimit | 5.0 | Base seconds before forcing a strafe direction swap |
bEnableStrafeRest | true | Enable fatigue rest after continuous strafing |
StrafeRestTimeLimit | 10.0 | Seconds of strafing before forced rest |
StrafeRestDuration | 1.5 | Duration of forced rest (seconds) |
DirectionBlendCount | 3 | Top-scoring directions to blend (1 = pick best, 2-3 = smooth) |
bEnableStuckDetection | true | Detect when AI is stuck and cannot make progress |
See Configuration Reference for the complete list.
Key Functions
// Main tick function - call from StateTree or Blueprint
EMovementEvaluatorResult EvaluateAndMove();
// Same as above, but with an explicit distance override
EMovementEvaluatorResult EvaluateAndMoveToDistance(float Distance);
// Apply a preset configuration
void ApplyBehaviorConfig(const FMovementBehaviorConfig& Config);
// Apply a data asset profile
void ApplyBehaviorProfile(const UMovementBehaviorProfile* Profile);
// Sync with AI Config (role-based switch)
bool SyncForCombatRole(const FGameplayTag& RoleTag, UEnemyAIConfig* AIConfig);
// Runtime adjustments
void SetDesiredDistance(float NewDistance);
void SetStrafeSide(int32 NewSide); // 1 = right, -1 = left
void SwapStrafeSide();
void SetDistanceMultiplier(float Multiplier); // E.g., from threat detection
// Movement suspension
void SuspendMovement(float Duration);
bool IsMovementSuspended();
// Queries
bool IsAtDesiredDistance();
bool IsStrafing();
float GetCurrentDistanceToTarget();
EMovementLayer GetCurrentMovementLayer();Positioning Rules
For advanced control, create custom Positioning Rules. These are instanced UObjects that modify direction scores during evaluation.
UCLASS(Blueprintable)
class UMyFlankingRule : public UPositioningRule
{
GENERATED_BODY()
virtual float EvaluateDirection_Implementation(
const FDirectionEvaluationContext& Context) const override
{
// Return -1.0 to +1.0
// +1.0 = strongly encourage this direction
// 0.0 = no preference
// -1.0 = strongly discourage this direction
}
};Add rules to your
MovementBehaviorProfile's PositioningRules array. Each rule has a Weight (0-1) that controls how much influence it has on the final score. Multiple rules stack additively.Built-in rule:
UAnglePreferenceRule pushes the AI toward (or away from) a specific angle relative to the target's facing direction:PreferredAngle = 0- frontal assaultPreferredAngle = 90- flanking (side attacks)PreferredAngle = 180- backstab positioningbAvoidInstead = true- invert the rule to avoid the angle instead
Events
The component broadcasts delegates you can bind to in Blueprint or C++:
| Event | When |
|---|---|
OnStrafeStarted | AI begins strafing (includes strafe side) |
OnStrafeEnded | AI stops strafing |
OnStrafeSwapped | Direction changes (includes new side) |
OnStrafeResting | Strafe fatigue rest begins |
OnMovementSuspended | Movement suspended (includes duration) |
OnMovementResumed | Suspension ends |
OnArrivedAtDesiredDistance | AI enters the comfort zone |
OnDepartedDesiredDistance | AI leaves the comfort zone |
OnMovementLayerChanged | Layer switches (Tactical, Strategic) |
OnSwitchToPathfinding | Switched to Strategic layer |
OnSwitchToDirectInput | Switched to Tactical layer |
OnPathfindingFailed | Pathfinding blocked or unreachable |
Behavior Tree Tasks
The plugin includes BT tasks for movement scenarios that fall outside the main evaluation loop:
| Task | Purpose |
|---|---|
| Move Until Distance | Pathfind toward a blackboard target until within a specified distance. Supports timeout, max chase distance, and debug visualization. |
| Make Distance | Retreat from a target using direct movement input until reaching a target distance. Includes stuck detection and timeout. |
| Debug SEC Values | Logs all SEC_ blackboard values (target, distance, action ID) to output log and screen. Useful for debugging. |
Debug Tools
Console Commands:
SEC.Debug.Movement.DrawScoring 1 // Visualize direction scores
SEC.Debug.Movement.DrawAvoidance 1 // Show avoidance radii
SEC.Debug.Movement.LogMovement 1 // Log basic movement state
SEC.Debug.Movement.LogStrafeSwap 1 // Log strafe swap decisions
SEC.Debug.Movement.LogStrafeState 1 // Log strafe state changes
Per-Instance (Blueprint/C++):
MovementEvaluator->bDebugDrawScoring = true;
MovementEvaluator->bDebugDrawAvoidance = true;
MovementEvaluator->bDebugLogMovement = true;
MovementEvaluator->bDebugLogScoring = true;
MovementEvaluator->bDebugLogAvoidance = true;
MovementEvaluator->bDebugLogStrafeSwap = true;
MovementEvaluator->bDebugLogStrafeState = true;Integration Points
| System | How Movement Uses It |
|---|---|
| Threat Detection | SECCombatControllerComponent auto-wires delegates; threat response behavior (bSwapStrafeOnHighThreat, bAdjustDistanceByThreat) is configured per MovementBehaviorProfile and switches automatically with combat role |
| Combat Roles | Different roles apply different MovementBehaviorProfiles |
| Action System | Actions can use Attack Approach to close distance before executing |