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:
LayerWhenHow
TacticalTarget is within HybridSwitchDistanceDirect movement input for responsive strafing and positioning
StrategicTarget is far away or path-blockedNavMesh 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

  1. Create a MovementBehaviorProfile data asset (Right-click, Miscellaneous, Data Asset, MovementBehaviorProfile)
  2. Configure desired distance, rest times, threat response, and positioning rules
  3. Apply it:
MovementEvaluator->ApplyBehaviorProfile(MyProfile);

Option 3: Configure at Runtime

MovementEvaluator->SetDesiredDistance(350.f);
MovementEvaluator->SetStrafeSide(1);  // 1 = right, -1 = left
The most automated path. The movement profile is determined by the AI's current combat role.
  1. Configure your EnemyAIConfig data asset.
  2. Assign profiles to roles (e.g., Attacker gets Aggressive, Waiter gets Passive).
  3. The SECCombatControllerComponent syncs automatically when the role changes.
// Called by SECCombatControllerComponent automatically when role changes:
MovementEvaluator->SyncForCombatRole(RoleTag, MyAIConfig);

Key Properties

PropertyDefaultPurpose
NumSamples16Directions to evaluate per tick
DesiredDistance400Ideal distance to maintain (cm)
DistanceTolerance0.2Comfort zone as percentage of desired distance
bEnableAvoidancetrueAvoid other AI pawns
AvoidanceRadius100How far to stay from other pawns (cm)
bEnableHybridMovementtrueSwitch between pathfinding and direct input
HybridSwitchDistance800Distance to switch between Tactical and Strategic
bAutoStrafeSwaptrueAutomatically swap strafe direction
StrafeSwapThreshold3.0Score ratio required to trigger auto-swap
StrafeTimeLimit5.0Base seconds before forcing a strafe direction swap
bEnableStrafeResttrueEnable fatigue rest after continuous strafing
StrafeRestTimeLimit10.0Seconds of strafing before forced rest
StrafeRestDuration1.5Duration of forced rest (seconds)
DirectionBlendCount3Top-scoring directions to blend (1 = pick best, 2-3 = smooth)
bEnableStuckDetectiontrueDetect 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 assault
  • PreferredAngle = 90 - flanking (side attacks)
  • PreferredAngle = 180 - backstab positioning
  • bAvoidInstead = true - invert the rule to avoid the angle instead

Events

The component broadcasts delegates you can bind to in Blueprint or C++:
EventWhen
OnStrafeStartedAI begins strafing (includes strafe side)
OnStrafeEndedAI stops strafing
OnStrafeSwappedDirection changes (includes new side)
OnStrafeRestingStrafe fatigue rest begins
OnMovementSuspendedMovement suspended (includes duration)
OnMovementResumedSuspension ends
OnArrivedAtDesiredDistanceAI enters the comfort zone
OnDepartedDesiredDistanceAI leaves the comfort zone
OnMovementLayerChangedLayer switches (Tactical, Strategic)
OnSwitchToPathfindingSwitched to Strategic layer
OnSwitchToDirectInputSwitched to Tactical layer
OnPathfindingFailedPathfinding blocked or unreachable

Behavior Tree Tasks

The plugin includes BT tasks for movement scenarios that fall outside the main evaluation loop:
TaskPurpose
Move Until DistancePathfind toward a blackboard target until within a specified distance. Supports timeout, max chase distance, and debug visualization.
Make DistanceRetreat from a target using direct movement input until reaching a target distance. Includes stuck detection and timeout.
Debug SEC ValuesLogs 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

SystemHow Movement Uses It
Threat DetectionSECCombatControllerComponent auto-wires delegates; threat response behavior (bSwapStrafeOnHighThreat, bAdjustDistanceByThreat) is configured per MovementBehaviorProfile and switches automatically with combat role
Combat RolesDifferent roles apply different MovementBehaviorProfiles
Action SystemActions can use Attack Approach to close distance before executing