Gameplay Math
Vector and rotation math for gameplay in Unreal Engine — dot and cross products, transforms, world and local space, interpolation, line traces, and quaternions.
15 questions
JuniorTheoryVery commonWhat is the dot product used for in gameplay programming?
What is the dot product used for in gameplay programming?
The dot product of two normalized vectors gives the cosine of the angle between them. Gameplay uses it to test if a target is in front (Dot > 0), inside a vision cone, or to project one vector onto another.
Common mistakes
- ✗Forgetting to normalize inputs — then the result is not a cosine and the sign test is the only thing still valid
- ✗Confusing the dot product (scalar) with the cross product (vector)
- ✗Treating the dot product result as an angle in degrees instead of a cosine
Follow-up questions
- →How do you convert a dot product result into an angle in degrees?
- →Why does the sign of the dot product still work even with non-normalized vectors?
JuniorCodeVery commonHow do you perform a line trace in C++ in Unreal Engine?
How do you perform a line trace in C++ in Unreal Engine?
Call GetWorld()->LineTraceSingleByChannel(Hit, Start, End, Channel, Params). It writes the first blocking hit into an FHitResult and returns true if something was hit. Start and End are world-space points.
Common mistakes
- ✗Passing a direction vector instead of an absolute world-space
Endpoint - ✗Forgetting to add the tracing actor to
FCollisionQueryParamsand hitting itself - ✗Reading
FHitResultfields without first checking the boolean return value
Follow-up questions
- →How do you trace against multiple objects instead of stopping at the first blocker?
- →What is the difference between tracing by channel and tracing by object type?
JuniorTheoryVery commonWhat is the difference between world space and local space in Unreal?
What is the difference between world space and local space in Unreal?
World space is the global coordinate system shared by the whole level. Local space is relative to a parent's transform. Unreal converts between them with TransformPosition / InverseTransformPosition on an FTransform.
Common mistakes
- ✗Comparing a local-space offset against world-space positions without converting first
- ✗Assuming a component's local location equals its world location when it has no parent — true only at the root
- ✗Thinking the two spaces differ only by units rather than by origin and orientation
Follow-up questions
- →How do you convert a direction (not a position) between local and world space?
- →What does an actor's relative transform represent versus its world transform?
JuniorTheoryCommonWhat is the cross product used for in gameplay programming?
What is the cross product used for in gameplay programming?
The cross product of two vectors returns a third vector perpendicular to both. Gameplay uses it to build surface normals, derive a right vector from forward and up, and to tell whether a target is to the left or right.
Common mistakes
- ✗Confusing the cross product (returns a vector) with the dot product (returns a scalar)
- ✗Assuming the cross product is commutative — swapping operands flips the result direction
- ✗Expecting a perpendicular result when the two input vectors are parallel — it gives a zero vector
Follow-up questions
- →Why does the cross product return a zero vector when the inputs are parallel?
- →How do you use the sign of a cross-product component to decide left versus right?
JuniorTheoryCommonHow do you compute the direction from one actor to another in Unreal?
How do you compute the direction from one actor to another in Unreal?
Subtract the source location from the target location, then normalize: (Target->GetActorLocation() - Source->GetActorLocation()).GetSafeNormal(). The result is a unit FVector pointing from source to target.
Common mistakes
- ✗Subtracting in the wrong order —
Source - Targetpoints away from the target - ✗Forgetting to normalize, so the vector length leaks distance into later math
- ✗Using
Normalize()instead ofGetSafeNormal()and crashing on a near-zero vector
Follow-up questions
- →Why is
GetSafeNormal()preferred overNormalize()for a direction vector? - →How would you get the distance and the direction in a single pass?
JuniorCodeCommonHow do you move an object at a constant speed from point A to point B?
How do you move an object at a constant speed from point A to point B?
Compute the normalized direction (B - A).GetSafeNormal(), then each frame add Direction * Speed * DeltaTime to the location. Multiplying by DeltaTime keeps the speed framerate-independent; stop when the remaining distance is reached.
Common mistakes
- ✗Forgetting
* DeltaTime, so movement speed scales with the frame rate - ✗Using a non-normalized direction, making speed depend on the distance to B
- ✗Overshooting B because the last frame's step is not clamped to the remaining distance
Follow-up questions
- →How do you detect and snap to B exactly on the frame that would overshoot it?
- →How would constant-velocity movement differ from
VInterpTotoward B?
JuniorTheoryCommonWhat is a Transform in Unreal Engine and what does it contain?
What is a Transform in Unreal Engine and what does it contain?
An FTransform bundles three components: location (FVector), rotation (FQuat), and scale (FVector). It describes where an object sits in space and can map points between local and world space.
Common mistakes
- ✗Forgetting that a transform includes scale, not just position and rotation
- ✗Thinking
FTransformstores rotation as Euler angles — it uses anFQuatinternally - ✗Composing transforms in the wrong order — transform multiplication is not commutative
Follow-up questions
- →How do you combine a child's relative transform with its parent's world transform?
- →Why does
FTransformstore rotation as a quaternion rather than anFRotator?
MiddleCodeCommonHow do you implement an enemy cone of vision using the dot product?
How do you implement an enemy cone of vision using the dot product?
Normalize the enemy forward vector and the direction to the target, take their dot product, and compare it to cos(HalfAngle). If Dot >= cos(HalfAngle) the target is inside the cone; then add a line trace to confirm line of sight.
Common mistakes
- ✗Comparing the dot product to the angle in degrees instead of to its cosine
- ✗Forgetting to normalize both vectors, so the dot product is not a clean cosine
- ✗Skipping the line trace, so the enemy 'sees' a target through a wall
Follow-up questions
- →How would you add a maximum vision distance to this check?
- →Why must the cone test be followed by a line-of-sight trace?
MiddleTheoryCommonHow do Lerp, InterpTo, and physics-based movement differ for moving an object?
How do Lerp, InterpTo, and physics-based movement differ for moving an object?
Lerp blends by a 0–1 alpha you control. FMath::VInterpTo eases toward a target at a speed each frame, framerate-independent. Physics-based movement applies forces or velocity and lets the simulation resolve collisions.
Common mistakes
- ✗Calling
Lerpwith a fixed alpha every frame, expecting framerate-independent easing - ✗Forgetting to pass
DeltaTimetoVInterpTo, making its speed depend on frame rate - ✗Moving a physics-simulated body with
SetActorLocationand fighting the simulation
Follow-up questions
- →How do you make a
Lerp-based movement framerate-independent? - →When would you prefer physics movement over
VInterpTofor a character?
MiddleCodeCommonHow do you find the object under the mouse cursor or at screen center?
How do you find the object under the mouse cursor or at screen center?
Convert the screen point to a world ray with APlayerController::DeprojectScreenPositionToWorld, then line trace along it. For the cursor, GetHitResultUnderCursor does both steps in one call and fills an FHitResult.
Common mistakes
- ✗Treating screen pixel coordinates as world-space points without deprojecting first
- ✗Forgetting that
bShowMouseCursormust be enabled forGetHitResultUnderCursorto work - ✗Confusing deproject (screen → world ray) with project (world → screen)
Follow-up questions
- →How would you trace from the exact center of the screen instead of the cursor?
- →Why does a deprojected ray need both an origin and a direction, not just a point?
MiddleTheoryCommonHow do quaternions help with rotations compared to Euler angles?
How do quaternions help with rotations compared to Euler angles?
An FQuat represents rotation without gimbal lock and composes cleanly by multiplication. It supports smooth shortest-path interpolation via Slerp. Unreal stores rotations as FQuat internally and exposes FRotator for readability.
Common mistakes
- ✗Believing quaternions suffer gimbal lock — that is an Euler-angle problem
- ✗Assuming quaternion multiplication is commutative — rotation order still matters
- ✗Interpolating
FRotatorcomponents linearly and expecting a smooth shortest-path turn
Follow-up questions
- →What is the difference between
SlerpandLerpon quaternions? - →Why does Unreal still expose
FRotatorif it stores rotations asFQuat?
MiddleCodeCommonHow do you smoothly rotate an actor to face a moving target each frame?
How do you smoothly rotate an actor to face a moving target each frame?
Build the desired rotation with FRotationMatrix::MakeFromX(Direction).Rotator() or (Target - Self).Rotation(), then ease toward it with FMath::RInterpTo(Current, Desired, DeltaTime, Speed) and apply it via SetActorRotation.
Common mistakes
- ✗Snapping with
SetActorRotationto the target rotation, skipping interpolation entirely - ✗Forgetting to pass
DeltaTimetoRInterpTo, making turn speed framerate-dependent - ✗Linearly lerping
FRotatorcomponents and getting a long way around past 180 degrees
Follow-up questions
- →How would you clamp the turn so the actor only yaws and never pitches?
- →Why is
RInterpTopreferable to linearly interpolatingFRotatorcomponents?
SeniorTheoryOccasionalWhy does naive RInterpTo overshoot at low FPS, and how do you make smoothing frame-rate independent?
Why does naive RInterpTo overshoot at low FPS, and how do you make smoothing frame-rate independent?
RInterpTo's linear step Speed * DeltaTime is only stable while it stays below 1; at low FPS a large DeltaTime makes the step exceed the remaining distance, so it overshoots and oscillates. The fix is exponential smoothing — blend with 1 - exp(-Speed * DeltaTime), which stays in 0..1 for any step.
Common mistakes
- ✗Believing passing
DeltaTimealone makes the result frame-rate independent — linear stepping still diverges at large steps - ✗Treating the overshoot as a rotation-wrapping bug rather than an instability of the explicit Euler step
- ✗Tuning
Speedto look right at 60 FPS, then seeing oscillation appear only on a slow machine
Follow-up questions
- →Why does the exponential form
1 - exp(-Speed * DeltaTime)stay bounded for anyDeltaTime? - →How would fixed-step sub-stepping inside one frame fix the same instability?
SeniorDesignOccasionalIn a very large open world, line traces and object placement become visibly unstable — hit points and positions jitter and snap — once the action moves far enough from the world origin, while everything is rock-solid near it. Explain why distance from the origin degrades positional accuracy this way, and describe the strategies used to keep precision usable across a world far larger than a single coordinate space comfortably allows.
In a very large open world, line traces and object placement become visibly unstable — hit points and positions jitter and snap — once the action moves far enough from the world origin, while everything is rock-solid near it. Explain why distance from the origin degrades positional accuracy this way, and describe the strategies used to keep precision usable across a world far larger than a single coordinate space comfortably allows.
A 32-bit float has ~7 significant digits, so far from the origin the value spacing grows to centimetres and trace endpoints snap to that coarse grid, producing jitter. Fixes: rebase the world origin near the player, tile the world, or use 64-bit coordinates.
Common mistakes
- ✗Blaming the jitter on a physics or collision bug rather than the float spacing growing with distance
- ✗Assuming a 64-bit world is free — Large World Coordinates costs memory bandwidth and needs precision-aware code
- ✗Rebasing the origin but forgetting to shift cached world-space vectors, networked positions, or particle data
Follow-up questions
- →What breaks in gameplay code when the world origin is rebased mid-session?
- →How does tile streaming with per-tile local coordinates avoid the precision problem entirely?
SeniorTheoryRareWhy does nlerp drift from constant angular velocity, and when does it still beat Slerp?
Why does nlerp drift from constant angular velocity, and when does it still beat Slerp?
Nlerp lerps the four components then normalizes — the path stays on the unit sphere but rotation speed is non-uniform, fastest near the midpoint. Slerp keeps constant angular velocity but costs a sin/acos. Nlerp still wins for small angles and many-bone skinning where the speed error is invisible.
Common mistakes
- ✗Thinking nlerp leaves the result off the unit sphere — the normalize step fixes magnitude; the error is in speed, not length
- ✗Assuming nlerp and
Slerptrace different arcs — they follow the same shortest path, only the timing along it differs - ✗Using
Slerpeverywhere for correctness without realizing nlerp's speed error is imperceptible for small per-frame deltas
Follow-up questions
- →How does the speed error of nlerp scale as the angle between the two quaternions grows?
- →Why must you check the sign of the dot product before nlerp or Slerp?