Skip to content

Dot Product, Angle, and Facing Checks

At a glance

Lives in: Kismet Math Library; Blueprint API / Math / Vector - Returns: one real number; never an Actor, Boolean, direction, or None - Changes: nothing; Dot Product is a pure calculation - Fails by: returning a mathematically valid but misleading number when inputs are not normalized for an angle test, use different coordinate spaces, or include a zero direction - Official docs: Dot Product, Get Forward Vector, and Acos (Degrees)

The one-minute version

  • Dot Product compares two vectors and returns one scalar.
  • With two normalized directions, the result is from -1 to 1: 1 means same direction, 0 means perpendicular, and -1 means opposite.
  • For a front/behind check, dot the source's forward direction with the normalized direction from source to target. Positive is in the forward half-space; negative is behind.
  • For a field-of-view cone, compare the dot result with cos(HalfAngle). A 90-degree full cone uses a 45-degree half-angle and a threshold near 0.707.
  • Normalize before an angle or cone test. Without normalization, vector magnitude changes the result.
  • Guard a zero source-to-target vector. Normalizing it returns zero, and a dot of zero can masquerade as a 90-degree angle.
  • Use Acos (Degrees) when the graph needs a human-facing angle in degrees. Acos (Radians) returns radians. Clamp the dot to -1..1 first.
  • Dot product does not test range, walls, visibility, teams, or perception memory. Add distance/trace checks or use AI Perception for those jobs.

What it actually calculates

For vectors A and B:

Dot(A, B) = A.X*B.X + A.Y*B.Y + A.Z*B.Z

The geometric meaning is:

Dot(A, B) = Length(A) * Length(B) * cos(angle between A and B)

That second form explains why normalized inputs matter. If both vectors have length 1, their lengths drop out and the dot equals the cosine of the angle:

Normalized dot Approximate angle Meaning
1.0 0 degrees same direction
0.707 45 degrees diagonal
0.0 90 degrees perpendicular
-0.707 135 degrees mostly opposite
-1.0 180 degrees opposite direction

The node has no execution pins and no failure branch. It does not normalize its inputs, rotate either vector, or remember previous results.

Normalized and non-normalized inputs

Normalize inputs when the result is supposed to mean angle or alignment:

Forward  = Get Actor Forward Vector
ToTarget = Normalize(TargetLocation - ActorLocation)
FacingDot = Dot Product(Forward, ToTarget)

Get Actor Forward Vector is already unit length under an ordinary valid transform. The subtraction is an offset with distance still inside it, so that input must be normalized.

Non-normalized dot products are not wrong. They answer a different question. For example, dotting velocity with a unit forward vector gives signed forward speed:

ForwardSpeed = Dot Product(Get Velocity, Get Actor Forward Vector)
  • positive: moving partly forward;
  • zero: no forward/back component;
  • negative: moving partly backward.

Dotting the same velocity with the Actor's right vector gives signed lateral speed. This is useful for directional locomotion because magnitude is intentional there.

See Vector Length / Vector Length XY / Normalize for safe normalization and the near-zero fallback.

Front, behind, and a field-of-view cone

Front or behind

Offset = TargetLocation - SourceLocation

if Vector Length Squared(Offset) <= MinimumDistance * MinimumDistance:
    target is at the source; handle that case explicitly
else:
    ToTarget = Normalize(Offset)
    FacingDot = Dot Product(SourceForward, ToTarget)

    FacingDot > 0  -> target is in the forward half-space
    FacingDot = 0  -> target is approximately sideways
    FacingDot < 0  -> target is in the rear half-space

In gameplay, compare with a small threshold rather than exact equality with zero. Floating-point results near a boundary can vary by tiny amounts.

A narrower cone

A field of view is usually described as the full angle from one edge to the other. Dot product compares one direction with the cone's center line, so the threshold uses half that angle:

HalfAngle = FullFieldOfViewDegrees * 0.5
Threshold = Cos (Degrees)(HalfAngle)

InsideCone = FacingDot >= Threshold

Common thresholds:

Full cone Half-angle Dot threshold
60 degrees 30 degrees about 0.866
90 degrees 45 degrees about 0.707
120 degrees 60 degrees 0.5
180 degrees 90 degrees 0

A higher threshold means a narrower forward cone. Store either the designer's angle and calculate the threshold when it changes, or expose the threshold with a clear name. Recalculating a constant cosine on every Tick is harmless at small scale but unnecessary.

Getting the actual angle with Acos

If all you need is an inside/outside test, compare the dot directly. Acos is extra work and makes the graph harder to read.

When the actual angle is needed:

DirectionA = Normalize(A)
DirectionB = Normalize(B)
Dot        = Dot Product(DirectionA, DirectionB)
SafeDot    = Clamp(Dot, -1, 1)
Angle      = Acos (Degrees)(SafeDot)

Clamp first because floating-point calculations can produce a value just past 1 or -1, outside inverse cosine's valid domain.

Current Blueprint search exposes the unit in the node name:

  • Acos (Degrees) returns degrees;
  • Acos (Radians) returns radians.

Do not feed a radians result into a pin documented in degrees, or convert an already-degree result a second time. C++ FMath::Acos returns radians, so C++ usually wraps it in FMath::RadiansToDegrees for a degree result.

2D or 3D facing?

The standard vector dot uses X, Y, and Z. That is right for flight, turret aim, or a character that must look up and down at a target.

For a ground-facing check, remove vertical influence from both directions before normalizing:

FlatForward = (SourceForward.X, SourceForward.Y, 0)
FlatOffset  = (Target.X - Source.X, Target.Y - Source.Y, 0)

if both flat vectors are safely nonzero:
    FlatFacingDot = Dot Product(
        Normalize(FlatForward),
        Normalize(FlatOffset))

Flattening only one input mixes two different questions. Also guard the rare case where a direction points almost straight up: its flattened version has no reliable heading.

Use 3D for an aerial enemy's vision. Use 2D when height should not make a ground enemy think a nearby player is outside its horizontal facing cone.

Left or right is a different comparison

Forward dot tells front versus back. A result of zero only says "perpendicular"; it cannot distinguish left from right.

Use the source's right direction for a left/right test:

ToTarget = Normalize(TargetLocation - SourceLocation)
SideDot  = Dot Product(SourceRightVector, ToTarget)

SideDot > 0 -> target is to the source's right
SideDot < 0 -> target is to the source's left
SideDot = 0 -> target lies on the forward/back center line

In Unreal, local X+ is forward and local Y+ is right. Get right from the same Actor, component, or rotation that supplied forward. Mixing body forward with camera right creates a comparison against two different frames.

A cross product can also produce a signed side result, but its sign depends on axis order and the chosen up axis. Right-vector dot is usually clearer in a beginner Blueprint.

Keep both vectors in the same space

Dot product only multiplies numbers. It cannot detect that one direction is in world space while another is local to a component.

Valid world-space comparison:

WorldForward = SourceComponent.GetForwardVector
WorldToTarget = Normalize(TargetWorldLocation - SourceWorldLocation)
Dot Product(WorldForward, WorldToTarget)

Valid local-space comparison:

LocalToTarget = Inverse Transform Direction(SourceTransform, WorldToTarget)
compare LocalToTarget with local forward (1, 0, 0)

Do not dot world forward with local target direction. The result may look plausible at one rotation and fail as soon as the source turns. The Break/Make and transform-location page covers local/world conversions in detail.

A facing check is not sight

A dot test answers an angular question. A complete visibility decision can need several independent checks:

1. Is target within maximum distance?
2. Is target inside the facing cone?
3. Does a line trace reach the intended target without a wall blocking it?
4. Is the target eligible by team, state, or gameplay rules?

Use Line Trace By Channel for one immediate occlusion query. A trace by itself is not a cone: it tests one line. Use Sphere, Capsule, and Multi Trace basics to gather candidates, then apply range/facing rules if that matches the mechanic.

Pawn Sensing and AI Perception own the larger AI sensing path: sight radius/cone configuration, stimuli, hearing, memory, forgetting, affiliation, and debugger support. A custom dot test is useful for a focused attack arc or interaction prompt, but it does not replace that system automatically.

Debugging a facing check

Print or watch these values separately:

  1. source world location;
  2. target world location;
  3. source forward and right vectors;
  4. normalized direction to target;
  5. dot result and threshold;
  6. 2D or 3D mode;
  7. trace hit Actor, if occlusion matters.

Draw debug lines for forward and to-target directions. If the numbers are correct but the mechanic still fires through walls, the missing piece is the trace, not another dot operation. See Breakpoints and watch values for inspecting the live graph instance.

The C++ twin

For the curious, this original example performs a flat 90-degree full-cone test:

bool AGuardCharacter::IsTargetInFrontCone(const AActor* Target) const
{
    if (!IsValid(Target))
    {
        return false;
    }

    FVector FlatForward = GetActorForwardVector();
    FlatForward.Z = 0.0;

    FVector FlatOffset = Target->GetActorLocation() - GetActorLocation();
    FlatOffset.Z = 0.0;

    if (FlatForward.IsNearlyZero() || FlatOffset.IsNearlyZero())
    {
        return false;
    }

    const double FacingDot = FVector::DotProduct(
        FlatForward.GetSafeNormal(),
        FlatOffset.GetSafeNormal());

    constexpr double HalfAngleDegrees = 45.0;
    const double Threshold = FMath::Cos(
        FMath::DegreesToRadians(HalfAngleDegrees));

    return FacingDot >= Threshold;
}

Range and line-of-sight checks would remain separate conditions.

Lookalikes - which one do I want?

Tool Returns/does Use when Watch out
Dot Product Scalar alignment/component Front/behind, cones, or signed speed. Normalize for angle meaning.
Acos (Degrees) Angle from a cosine value You need the actual angle. Clamp to -1..1; costs more than a threshold.
Cross Product Vector perpendicular to two inputs You need an axis/normal or advanced signed-angle work. Input order changes direction.
Find Look at Rotation Rotator from one point toward another Something should face the target. It calculates rotation, not visibility.
Line Trace By Channel Blocking-hit query along one segment Walls or precise line of sight matter. One ray is not a field-of-view cone.
AI Perception Sensing events and remembered stimuli AI needs configurable sight/hearing and memory. Requires listener/sense/stimulus setup.

Going deeper