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
-180and+180are 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:
-180and+180represent 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
+179to-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/+180seam. - 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¶
- Blend Spaces in Animation Blueprints — the asset that usually consumes Direction and GroundSpeed.
- Event Blueprint Update Animation — the game-thread beginner pattern for refreshing those variables.
- Animation threading and Property Access — safe data snapshots for worker-thread animation functions.
- Get Velocity — verify what motion Vector the Actor or Pawn actually reports.
- Get Control Rotation / Get Base Aim Rotation / Get Player View Point — choosing an aim/view basis without confusing it with body rotation.
- Why won't my character face where it's moving? — why movement-oriented facing can legitimately drive Direction toward zero.
- Root Motion vs Character Movement — when animation rather than ordinary movement owns travel.
- Official docs: Blend Spaces and UAnimInstance.
- Engine source (requires engine access — we do not reproduce it here):
UKismetAnimationLibrary::CalculateDirectioninEngine/Source/Runtime/AnimGraphRuntime/Private/KismetAnimationLibrary.cppwith its declaration in the matchingPublic/KismetAnimationLibrary.h; the Blueprint-thread-safe Anim Instance entry is declared inEngine/Source/Runtime/Engine/Classes/Animation/AnimInstance.hand implemented with the Anim Instance utilities underEngine/Source/Runtime/Engine/Private/Animation/AnimInstance.cpp.