MiddleCodeCommonNot answered yet
How do you implement an enemy cone of vision using the dot product?
Return whether an enemy can see a target: the target must be inside the enemy's vision cone (half-angle VisionHalfAngle in degrees) and not occluded by geometry.
Constraints:
- Normalize both the forward vector and the direction to the target.
- Compare the dot product against
cos(HalfAngle), never against the raw angle. - After the angle passes, confirm line of sight with an
ECC_Visibilitytrace.
bool AEnemy::CanSeeTarget(const AActor* Target) const
{
// your code here
}
Write the implementation.
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.
- ✗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
- →How would you add a maximum vision distance to this check?
- →Why must the cone test be followed by a line-of-sight trace?
Cone of vision via the dot product
bool AEnemy::CanSeeTarget(const AActor* Target) const
{
if (!Target)
return false;
const FVector EyeLocation = GetActorLocation();
const FVector Forward = GetActorForwardVector(); // already normalized
const FVector ToTarget =
(Target->GetActorLocation() - EyeLocation).GetSafeNormal();
// The dot product of two unit vectors = the cosine of the angle
const float Dot = FVector::DotProduct(Forward, ToTarget);
// Cone half-angle in degrees → cosine for comparison
const float HalfAngleRad = FMath::DegreesToRadians(VisionHalfAngle);
const float CosHalfAngle = FMath::Cos(HalfAngleRad);
// The narrower the cone, the LARGER the required cosine
if (Dot < CosHalfAngle)
return false; // target is outside the field of view
// Inside the cone — now check the target is not behind a wall
FHitResult Hit;
FCollisionQueryParams Params;
Params.AddIgnoredActor(this);
Params.AddIgnoredActor(Target);
const bool bBlocked = GetWorld()->LineTraceSingleByChannel(
Hit, EyeLocation, Target->GetActorLocation(), ECC_Visibility, Params);
return !bBlocked; // visible if nothing blocked the ray
}
Key points:
- Both vectors are normalized, so
Dotis exactly the cosine of the angle. - Compare against
cos(HalfAngle), not the angle itself: for a 90° cone the threshold iscos(45°). - A narrow cone → cosine closer to 1 → a stricter test.
- The dot product ignores obstacles, so a line trace is still required.