Dot products, cross products, attack cones, facing checks, and the FMath utilities you reach for in gameplay code.
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()
// The direction the actor is currently facing
FVector Forward = Actor->GetActorForwardVector();
// Right and Up vectors
FVector Right = Actor->GetActorRightVector();
FVector Up = Actor->GetActorUpVector();
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.
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;
float FOVDegrees = 60.0f;
float FOVThreshold = FMath::Cos(FMath::DegreesToRadians(FOVDegrees / 2.0f));
bool bInFOV = Dot >= FOVThreshold;
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
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
// 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();
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;
// Get a direction 90° to the right of forward — useful for strafing, circling
FVector RightOfForward = FVector::CrossProduct(GetActorForwardVector(), FVector::UpVector);
A melee attack cone check combines dot product (is the target within the angle?) with a distance check (is the target within reach?).
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;
}
// 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
);
// 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);
}
Unreal's FMath namespace contains the gameplay math functions you reach for constantly. Know these.
| Function | What 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 |
| Function | What 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 |
// 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
);
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
| Angle | cos(angle) | Dot product result |
|---|---|---|
| 0° | 1.000 | Same direction |
| 30° | 0.866 | Narrow cone (60° total) |
| 45° | 0.707 | Medium cone (90° total) |
| 60° | 0.500 | Wide cone (120° total) |
| 90° | 0.000 | Perpendicular |
| 180° | -1.000 | Opposite direction |