Skip to content

Calculate Direction

At a glance

Lives on: the Anim Instance in Blueprint; the current C++ utility also lives in UKismetAnimationLibrary · Inputs: a Velocity Vector and a Base Rotation Rotator in the same world basis · Returns: float degrees in -180..180 · Meaning: 0 forward, positive toward the Base Rotation's right, negative toward its left, and ±180 backward · Failure result: no success Boolean; zero/nearly-zero velocity returns 0, while bad input choices still return a plausible number · Threading: the calculation is marked Blueprint-thread-safe, but its data sources must be safe too · Official pages: Calculate Direction and UKismetAnimationLibrary::CalculateDirection

Calculate Direction answers one narrow question: which horizontal direction is this velocity pointing, relative to this chosen rotation? It does not fetch the velocity, choose the rotation, smooth the result, or play a Blend Space.

The one-minute version

  • Supply the moving object's world Velocity and a world Base Rotation. The Base Rotation defines what “forward,” “right,” “back,” and “left” mean.
  • The signed output maps to a directional Blend Space like this:
     forward = 0
left = -90       right = +90
     backward = -180 / +180
  • -180 and +180 are the same backward direction. They are opposite ends of a number line but neighbors on a circle.
  • For body-relative strafing, use the Pawn/Character's Actor yaw as the base. For aim-relative strafing, use Control yaw when the design means “relative to where the controller aims.” Those choices produce different answers on purpose.
  • For ground locomotion, remove pitch/roll from Base Rotation and gate on Vector Length XY. Vertical velocity is not forward/left/right movement.
  • At idle, set Direction to a deliberate value (commonly 0) or preserve a chosen last-moving direction. Do not let tiny velocity jitter select random strafe samples.
  • The node is pure math and is marked thread-safe. Get Velocity, Controller lookups, and arbitrary Pawn functions do not become worker-thread-safe just because they feed it; use Property Access snapshots or gather data on the game thread.
  • Root motion and non-Character Pawns change the meaning and source of the Velocity you supply. Calculate Direction does not know how that motion was produced.

The signed-angle map

Assume the Base Rotation has yaw 0, so Unreal's positive X axis is forward and positive Y is right:

World velocity Relative motion Return value
(100, 0, 0) Forward 0
(0, 100, 0) Right about +90
(0, -100, 0) Left about -90
(-100, 0, 0) Backward +180 or the equivalent -180 boundary
(100, 100, 0) Forward-right about +45
(100, -100, 0) Forward-left about -45
(0, 0, 0) No direction 0

Rotate the Base Rotation by 90 degrees and the same world Velocity can produce a different angle, because “forward” has changed. The node is not classifying world north/east/south/west; it is expressing motion in the caller's chosen local frame.

What it actually does

The implementation compares the horizontal Velocity direction with the Base Rotation's forward and right axes:

pseudocode — not engine source

CalculateDirection(Velocity, BaseRotation):
    if Velocity is nearly zero:
        return 0

    horizontalDirection = safely normalize Velocity in the XY plane
    forward = BaseRotation's forward axis
    right = BaseRotation's right axis

    unsignedAngle = arccos(dot(forward, horizontalDirection)) in degrees

    if dot(right, horizontalDirection) is negative:
        return -unsignedAngle
    return unsignedAngle

The forward dot product supplies the size of the angle; the right dot product chooses its sign. The result is a snapshot. The function does not store a last angle, inspect animation assets, or know whether the caller is in a state machine.

Magnitude is not part of the returned direction after normalization. A slow and fast object moving along the same heading return the same angle. Calculate GroundSpeed separately with Vector Length XY.

Choosing Base Rotation

Base Rotation is the policy decision in this node.

Actor Rotation: direction relative to the body

Use Actor Rotation when Blend Space samples mean:

  • forward/back/strafe relative to the Character body;
  • a tank or creature's current facing frame; or
  • a mesh-aligned locomotion set whose forward matches the Actor's forward.
Velocity = OwnerCharacter.GetVelocity()
BaseRotation = Make Rotator(0, OwnerCharacter.ActorRotation.Yaw, 0)
Direction = Calculate Direction(Velocity, BaseRotation)

If Orient Rotation to Movement turns the Actor toward its acceleration, the result often moves toward 0 once the body finishes turning. That is correct: movement has become forward relative to the body. A full strafe Blend Space is most useful when body/aim facing can remain independent of travel.

Control Rotation: direction relative to aim/view intent

Use Control yaw when the samples mean “moving relative to where the controller aims”:

  • 0: toward the reticle/aim yaw;
  • +90: strafe right of aim;
  • -90: strafe left of aim; and
  • ±180: backpedal while still aiming forward.

This is common for a strafe shooter, but it needs a stable source on every machine that animates the Pawn. Other clients normally do not have the remote player's PlayerController. For remote presentation, use the Pawn's replicated aim representation or an explicit replicated aim yaw rather than looking up Player Controller 0.

Mesh rotation: account for authored forward deliberately

The Actor may face X-forward while an imported mesh has a relative rotation to look correct. Do not hide that mismatch by randomly adding 90 degrees until one animation appears right. Decide whether the Blend Space directions are defined in Actor space or mesh-authored space, then apply one documented offset at that boundary.

Remove pitch and vertical motion for ground locomotion

A directional ground Blend Space describes motion in the XY plane. Give it a yaw-only basis:

GroundVelocity = Make Vector(Velocity.X, Velocity.Y, 0)
GroundBase = Make Rotator(Pitch = 0, Yaw = BaseRotation.Yaw, Roll = 0)
GroundSpeed = Vector Length XY(GroundVelocity)

Pitching the Base Rotation tilts its forward axis away from the horizontal Velocity and skews the dot product. A purely vertical Velocity has no useful forward/strafe meaning even though it is a nonzero 3D vector. Gate the call on ground speed instead of asking Calculate Direction to turn falling speed into a locomotion sample.

Flying, swimming, and six-degrees-of-freedom movement need a different design. This node still returns one signed angle, not a complete 3D heading with both yaw and pitch.

Feeding a -180..180 Blend Space

A conventional 2D locomotion Blend Space uses:

Direction axis: -180 .. 180
Speed axis:       0 .. MaxGroundSpeed

Direction 0       -> forward samples
Direction +90     -> right samples
Direction -90     -> left samples
Direction -180/+180 -> matching backward samples

The backward seam needs deliberate asset setup:

  • -180 and +180 represent the same direction, so samples at both ends should be compatible (often the same backward animation).
  • Enable Wrap Input only when the axis is intentionally circular and the endpoint animations match; otherwise crossing the seam can pop.
  • Do not apply an ordinary float interpolation from +179 to -179. That treats the shortest two-degree change as a 358-degree trip through forward. Use normalized angular-delta math or the Blend Space's circular/smoothing facilities.
  • Speed and direction smoothing solve different problems. Prove the raw values before hiding them behind smoothing.

Idle and low-speed jitter

The function's zero-velocity return is 0, but movement systems rarely settle on mathematically perfect zero every update. Network correction, a moving base, physics, or deceleration can leave tiny XY velocities whose direction changes wildly.

Use an idle threshold:

GroundSpeed = Vector Length XY(Velocity)

if GroundSpeed > 3:          // project threshold, not an engine constant
    Direction = Calculate Direction(GroundVelocity, GroundBase)
else:
    Direction = 0            // or preserve LastMovingDirection by design

The idle state should normally be selected by speed, not by Direction 0. 0 means both “forward” and “no direction,” so it is not proof that the Pawn is moving forward.

Root motion and non-Character Pawns

Calculate Direction never calls Get Velocity; it trusts the Vector supplied. First prove what that Vector means for this object:

  • A Character commonly reports Character Movement velocity.
  • A custom Pawn may report velocity from its Pawn Movement Component, root component, physics body, or an override.
  • A component moving inside a stationary Actor does not automatically make Actor Velocity represent that component's motion.
  • During a root-motion action, authored animation contributes movement and the normal locomotion Blend Space may not be the active pose owner at all.
  • Teleports and direct transform assignments are not a stable velocity source unless the responsible movement system computes one.

For normal in-place Character locomotion, Character Movement Velocity is the right source. During a root-motion montage, let the montage/action own the pose unless the design explicitly needs a derived travel direction.

Animation Blueprint threading

UAnimInstance::CalculateDirection is marked BlueprintThreadSafe, and the Kismet Animation Library form is pure math. It may run in a thread-safe Animation Blueprint function when its inputs are already safe values.

Unsafe pattern:

Blueprint Thread Safe Update Animation
-> chase an arbitrary live Pawn reference
-> call several gameplay functions
-> Calculate Direction

Safe shape:

Property Access snapshots Velocity and the chosen yaw
-> thread-safe function builds GroundVelocity and GroundBase
-> GroundSpeed threshold
-> Calculate Direction
-> set animation Direction variable

Alternatively, gather object data during Event Blueprint Update Animation on the game thread and calculate there. A thread-safe last node does not certify the whole upstream chain.

When it fails (and what failure looks like)

Calculate Direction always returns a float; most failures look like a wrong but believable angle:

Symptom Likely cause
Returns 0 while idle Expected zero/nearly-zero behavior; use Speed to distinguish idle from forward.
Right animation plays while moving left Axis samples/sign convention were swapped, or an authored mesh offset was hidden in the math.
Direction is near 0 in every direction Actor is orienting to movement, so its forward turns to match Velocity.
Camera orbit changes strafe angle unexpectedly Control Rotation was used where body-relative Actor Rotation was intended.
Looking up/down changes ground direction Pitch/roll were left in Base Rotation.
Jump/fall chooses a side sample Ground-speed gating/vertical removal is missing.
Backward motion flips between -180 and +180 Normal circular seam; endpoint samples or smoothing do not handle it.
Idle value flickers Tiny XY velocity is above the effective zero tolerance; add a gameplay/animation threshold.
Works in Event Graph, cannot compile thread-safe One of the input-producing calls is not thread-safe; snapshot it with Property Access.
Returns a default after calling through None A missing Anim Instance target caused an invalid-reference Blueprint call; guard Get Anim Instance first.

The function logs no “wrong base rotation” warning and cannot know that a number was in local space while the other was in world space. The caller owns that contract.

The pattern everyone actually uses

Animation update data:
    Velocity = OwnerCharacter movement velocity
    ActorYaw = OwnerCharacter actor yaw

GroundVelocity = (Velocity.X, Velocity.Y, 0)
GroundSpeed = Vector Length XY(GroundVelocity)
GroundBase = (Pitch 0, Yaw ActorYaw, Roll 0)

if GroundSpeed > IdleThreshold:
    Direction = Calculate Direction(GroundVelocity, GroundBase)
else:
    Direction = 0

AnimGraph:
    Direction + GroundSpeed -> 2D directional Blend Space

For aim-relative strafing, replace ActorYaw with the project's safe, network-appropriate aim yaw source. Keep the rest of the contract unchanged.

The C++ twin, for the curious (our own example code):

const FVector GroundVelocity(Velocity.X, Velocity.Y, 0.0);
const FRotator GroundBase(0.0, BaseRotation.Yaw, 0.0);

Direction = GroundVelocity.Size2D() > IdleSpeed
    ? UKismetAnimationLibrary::CalculateDirection(GroundVelocity, GroundBase)
    : 0.0f;

What Calculate Direction does not do

  • It does not fetch or validate an Actor, Pawn, Movement Component, or Anim Instance reference.
  • It does not move or rotate anything.
  • It does not return speed, acceleration, input direction, or facing intent.
  • It does not choose Actor Rotation versus Control Rotation.
  • It does not smooth idle jitter or the -180/+180 seam.
  • It does not play a Blend Space or require a particular animation asset.
  • It does not replicate Direction or make local-only aim data available on remote clients.
  • It does not make upstream object access thread-safe.

It is cheap, side-effect-free angle math. Calling it each animation update is normal; supplying coherent inputs is the real work.

Lookalikes — which one do I want?

Node / value Use when Difference
Calculate Direction World motion must become a signed local strafe angle. Returns -180..180; needs Velocity plus chosen Base Rotation.
Vector Length XY You need ground speed without vertical velocity. Magnitude only; no left/right sign.
Normalized Delta Rotator You have two rotations and need their shortest signed difference, often for aim yaw. Compares rotations, not Velocity.
Inverse Transform Direction You need the complete local-space movement Vector. Preserves vector components instead of collapsing them to one angle.
Dot Product You only need a forward/back or facing test. Simpler scalar classification, not a full signed angle.
Find Look at Rotation You have start and target positions. Produces a world Rotator toward a point, not movement-relative direction.
Get Last Movement Input Vector You need requested input direction. Intent can differ from actual Velocity due to collision, braking, or physics.

Rule of thumb: Calculate Direction is for a directional locomotion axis; Normalized Delta Rotator is for rotation-to-rotation differences such as aim.

Going deeper