I'm working on a project that involves a battleship with multiple turrets on it. The turrets have limited movement on both horizontal and vertical axis. When checking if they can target the player I need to check if the player is within both fields of view.

I'm using the acos of the dot product to get the horizontal angle. For the vertical angle, I'm taking the X and Z components and ignoring the Y, creating two new vectors and repeating the process, but it seems a little buggy and not correct. I tried searching online but there is very little information for getting both angles.

The project is in Unreal and Z is the up axis.

WeaponTransform is the FTransform of the AActor.

TForward is the initial forward direction WeaponTransform->GetRotation() * FVector::ForwardVector;

actorLocation is the location of the player using actor.GetActorLocation();

float hAngle = CalculateAngleBetween(tForward, actorLocation, WeaponTransform->GetLocation());
    inAngle = CheckAngle(hAngle, HMinAngle, HMaxAngle);

// This seems incorrect
FVector weaponForwardXZ = FVector(tForward.X, tForward.Z, 0.f);
    FVector actorLocationXZ = FVector(actorLocation.X, actorLocation.Z, 0.f);
    FVector weaponPositionXZ = FVector(WeaponTransform->GetLocation().X, WeaponTransform->GetLocation().Z, 0.f);
    float vAngle = CalculateAngleBetween(weaponForwardXZ, actorLocationXZ, weaponPositionXZ);
    inAngle = CheckAngle(vAngle, VMinAngle, VMaxAngle);

CalculateAngleBetween(FVector turretForward, FVector targetPosition, 
FVector turretPosition)
auto dir = targetPosition - turretPosition;
auto safeForward = turretForward.GetSafeNormal();
auto cross = FVector::CrossProduct(dir, turretForward);

float axisSign = FVector::DotProduct(cross, FVector::UpVector) >= 0.f ? 1.f : -1.f;

auto dot = FVector::DotProduct(dir, safeForward);
auto rad = FMath::Acos(dot) * axisSign;
return FMath::RadiansToDegrees(rad);
  • \$\begingroup\$ Dot product returns a float in [-1, 1], assuming vectors are normalised, which is not an angle in radians. So, what is "cos of the dot product"? Also, it's not clear which vectors are used when computing such a dot product, we can only make assumptions about that. \$\endgroup\$
    – liggiorgio
    Commented Feb 15, 2022 at 15:39
  • \$\begingroup\$ Can you show us what you're doing now, in code or in blueprints? The description you've given is a bit vague and doesn't 100% make sense the way it's been summarized (maybe you meant the arc-cosine of the dot product, not the cos? I presume you're normalizing a vector somewhere but that's not explicit...) \$\endgroup\$
    – DMGregory
    Commented Feb 15, 2022 at 18:30
  • \$\begingroup\$ I updated the question with the code I`m using and put a comment where I think the bug is. \$\endgroup\$
    – Dave
    Commented Feb 16, 2022 at 0:31
  • \$\begingroup\$ @DMGregory Any idea? \$\endgroup\$
    – Dave
    Commented Feb 17, 2022 at 9:28
  • \$\begingroup\$ Since these turrets are on a battleship, is it reasonable to assume they might be rolling with the waves, and so the turret's vertical axis might not exactly match the world vertical z axis? Or is that not a concern for your use case? \$\endgroup\$
    – DMGregory
    Commented Feb 17, 2022 at 16:58

1 Answer 1


Assuming WeaponTransform is an FTransform representing the transformation of the turret base/pivot (against which we should be measuring any angular deviation), then I'd propose that we use this to move the calculation into the local coordinate system of this base to make things simpler.

// Get the offset from our position to the target, relative to our base.
FVector local = WeaponTransform->InverseTransformPositionNoScale(actorLocation);

// Discard the distance so we keep only the direction (a point on the unit sphere)

// The Z coordinate of that direction is the sine of the elevation angle.
float elevatonAngle = FMath::RadiansToDegrees(FMath::Asin(local.z));

// The XY coordinates give us a horizontal bearing relative to our local X+.
float relativeBearing = FMath::RadiansToDegrees(FMath::Atan2(local.y, local.x));

This is a standard spherical coordinates conversion that you can find covered in many previous answers.

  • \$\begingroup\$ @DMGegory I tried using this but both numbers are less than one. Do I need to do another calculation after this? \$\endgroup\$
    – Dave
    Commented Feb 22, 2022 at 2:21
  • \$\begingroup\$ No. It could be that WeaponTransform refers to something other than what I suspected above, or it has been scaled non-uniformly. Or that actorLocation is not in the coordinate system that InverseTransformPosition expects. Can you add more details about how these inputs are defined? \$\endgroup\$
    – DMGregory
    Commented Feb 22, 2022 at 2:32
  • \$\begingroup\$ I edited the initial post with information on where these values come from. Let me know if anything is unclear. \$\endgroup\$
    – Dave
    Commented Feb 22, 2022 at 4:54
  • \$\begingroup\$ "the AActor" could refer to any actor, for a reader who doesn't know your project intimately. Try giving more than just the data type. A labelled screenshot can often help in cases like this. \$\endgroup\$
    – DMGregory
    Commented Feb 22, 2022 at 5:29
  • \$\begingroup\$ The AActor is player. I updated the post to include that information. My code is basically the same as what I have here, I just removed unrelated code, so a screenshot would be the same. \$\endgroup\$
    – Dave
    Commented Feb 24, 2022 at 5:01

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .