Chapter 07

Gameplay Math in Unreal

Dot products, cross products, attack cones, facing checks, and the FMath utilities you reach for in gameplay code.

In this chapter

  1. Vectors in Unreal
  2. Dot Product — Facing & FOV
  3. Cross Product — Normals & Lateral Checks
  4. Attack Cones
  5. FMath Utilities
1

Vectors in Unreal

Unreal uses a left-handed coordinate system: X is forward, Y is right, Z is up. All vectors are FVector.

FVector A = ActorA->GetActorLocation();
FVector B = ActorB->GetActorLocation();

// Direction from A to B (not normalized)
FVector Dir = B - A;

// Normalized direction (unit vector, length = 1)
FVector DirNorm = Dir.GetSafeNormal();

// Distance between A and B
float Dist = FVector::Dist(A, B);
// Or: Dir.Size()
Always normalize before using a vector in a dot or cross product that is supposed to return angle information. An un-normalized vector will give wrong results because its length contaminates the result.

Actor forward vector

// The direction the actor is currently facing
FVector Forward = Actor->GetActorForwardVector();

// Right and Up vectors
FVector Right = Actor->GetActorRightVector();
FVector Up    = Actor->GetActorUpVector();

2

Dot Product — Facing & FOV

The dot product of two normalized vectors equals the cosine of the angle between them:

A · B = cos(θ)

Result:  1.0  → vectors point the same direction (0°)
         0.0  → vectors are perpendicular (90°)
        -1.0  → vectors point opposite directions (180°)

This makes dot product ideal for facing checks and field-of-view tests.

Facing check: is target in front of me?

FVector MyForward   = GetActorForwardVector();
FVector ToTarget    = (Target->GetActorLocation() - GetActorLocation()).GetSafeNormal();
float   Dot         = FVector::DotProduct(MyForward, ToTarget);

// Dot > 0 means target is somewhere in front (within 90° either side)
bool bIsInFront = Dot > 0.0f;

FOV check: is target within a cone angle?

float FOVDegrees   = 60.0f;
float FOVThreshold = FMath::Cos(FMath::DegreesToRadians(FOVDegrees / 2.0f));

bool bInFOV = Dot >= FOVThreshold;
To check a 60° FOV cone, the half-angle is 30°. cos(30°) ≈ 0.866. If the dot product is ≥ 0.866, the target is within the cone. You compare against the cosine of the half-angle because dot product returns a cosine value, not an angle.

Aim assist: how closely are we aimed at the target?

FVector AimDir   = GetControlRotation().Vector();
FVector ToTarget = (Target->GetActorLocation() - CameraLocation).GetSafeNormal();
float   AimDot   = FVector::DotProduct(AimDir, ToTarget);

// AimDot near 1.0 = nearly perfectly aimed
// Scale aim assist strength by (1.0 - AimDot) or similar

3

Cross Product — Normals & Lateral Checks

The cross product of two vectors produces a third vector perpendicular to both. Its direction follows the left-hand rule in Unreal's coordinate system.

A × B = vector perpendicular to the plane formed by A and B

Surface normal

// Given two edges of a triangle, find the face normal
FVector Edge1  = V1 - V0;
FVector Edge2  = V2 - V0;
FVector Normal = FVector::CrossProduct(Edge1, Edge2).GetSafeNormal();

Left/right check: is the target to my left or right?

FVector MyForward = GetActorForwardVector();
FVector ToTarget  = (Target->GetActorLocation() - GetActorLocation()).GetSafeNormal();

FVector Cross = FVector::CrossProduct(MyForward, ToTarget);

// In Unreal (left-handed, Z-up):
// Cross.Z > 0 → target is to the right
// Cross.Z < 0 → target is to the left
bool bTargetIsRight = Cross.Z > 0.0f;
Use the Z component of the cross product for left/right checks in a flat ground plane. If you are working in 3D space (e.g., checking above/below), use the Y component instead.

Perpendicular direction (strafe offset, orbit)

// Get a direction 90° to the right of forward — useful for strafing, circling
FVector RightOfForward = FVector::CrossProduct(GetActorForwardVector(), FVector::UpVector);

4

Attack Cones

A melee attack cone check combines dot product (is the target within the angle?) with a distance check (is the target within reach?).

Full cone check

bool AMyCharacter::IsTargetInAttackCone(AActor* Target) const
{
    float AttackRange    = 200.0f;
    float AttackAngleDeg = 90.0f;  // total cone width

    FVector Origin    = GetActorLocation();
    FVector ToTarget  = Target->GetActorLocation() - Origin;
    float   Distance  = ToTarget.Size();

    // 1. Distance check
    if (Distance > AttackRange)
        return false;

    // 2. Angle check using dot product
    float HalfAngleCos = FMath::Cos(FMath::DegreesToRadians(AttackAngleDeg / 2.0f));
    float Dot          = FVector::DotProduct(GetActorForwardVector(), ToTarget.GetSafeNormal());

    return Dot >= HalfAngleCos;
}

Visualizing for debugging

// Draw the attack cone in the world (editor + PIE)
DrawDebugCone(
    GetWorld(),
    GetActorLocation(),
    GetActorForwardVector(),
    AttackRange,
    FMath::DegreesToRadians(AttackAngleDeg / 2.0f),  // horizontal half-angle
    FMath::DegreesToRadians(AttackAngleDeg / 2.0f),  // vertical half-angle
    12,              // segments
    FColor::Red,
    false,
    0.1f             // duration
);
Always wire up a debug visualization for cone checks during development. It is the fastest way to spot a misconfigured angle or a wrong forward vector — and it shows exactly what a designer would need to tune.

Multi-target cone sweep

// Check all actors in an overlap sphere, then filter by cone
TArray<AActor*> OverlapActors;
UKismetSystemLibrary::SphereOverlapActors(
    GetWorld(), GetActorLocation(), AttackRange,
    ObjectTypes, nullptr, {this}, OverlapActors);

for (AActor* Actor : OverlapActors)
{
    if (IsTargetInAttackCone(Actor))
        ApplyDamage(Actor);
}

5

FMath Utilities

Unreal's FMath namespace contains the gameplay math functions you reach for constantly. Know these.

Clamping and interpolation

FunctionWhat it does
FMath::Clamp(V, Min, Max)Clamps value to [Min, Max]
FMath::Lerp(A, B, Alpha)Linear interpolation between A and B
FMath::FInterpTo(Current, Target, DeltaTime, Speed)Frame-rate independent smooth approach
FMath::VInterpTo(...)Same but for FVector
FMath::RInterpTo(...)Same but for FRotator

Angle conversions

FunctionWhat it does
FMath::DegreesToRadians(D)Degrees → radians (for trig functions)
FMath::RadiansToDegrees(R)Radians → degrees (for display)
FMath::Cos(Radians)Cosine — used with dot product thresholds
FMath::Acos(Value)Inverse cosine — gives angle from dot product result

Mapping ranges

// Map a value from one range to another
// e.g. map health [0, 100] to opacity [0.0, 1.0]
float Mapped = FMath::GetMappedRangeValueClamped(
    FVector2D(0.f, 100.f),   // input range
    FVector2D(0.f, 1.f),    // output range
    CurrentHealth
);

Random

float    RandF  = FMath::RandRange(0.f, 1.f);
int32    RandI  = FMath::RandRange(0, 10);
FVector  RandDir = FMath::VRandCone(ForwardVector, FMath::DegreesToRadians(15.f));
// VRandCone returns a random direction within a cone — useful for shotgun spread

Quick reference: dot product cheat sheet

Anglecos(angle)Dot product result
0°1.000Same direction
30°0.866Narrow cone (60° total)
45°0.707Medium cone (90° total)
60°0.500Wide cone (120° total)
90°0.000Perpendicular
180°-1.000Opposite direction
← Chapter 6 ↑ Index