Raycast-less Look Based Interactables
Determine if the player is looking at 'something' based off a strictness threshold without needing to raycast.
Overview
For this scenario we will say a player is looking wherever their crosshairs are pointing (as is the case with every FPS and TPS). Therefor to determine if a player is looking at an item, we want to know if their crosshairs are pointing at the item.
This knowledge could be used to; Show/hide popup prompts, play effects or particles, de/activate some element...etc
Steps to achieve this
Lets say the crosshair world location is the Players eyes
Getting the crosshair screen position, translating to world space, and creating a vector forward from the Players eyes
Getting the direction from eyes to item
Check how parallel these two vectors are to determine if the player is looking at the item or not
Note: do NOT use the player's position when creating the vector from 'eyes to item' since the players world position is not going to be the same as the eye position and we want a vector from where the player is looking vs standing.
Get Crosshair Look direction in world space
Translate the crosshair position on the screen (let's assume middle of screen) to world space and create a vector representing where our Player is looking
// Get viewport size
FVector2D ViewportSize;
GEngine->GameViewport->GetViewportSize(ViewportSize);
// Get screen space position of crosshairs (modify as needed for crosshair position)
FVector2D CrosshairScreenPosition(ViewportSize.X / 2.f, ViewportSize.Y / 2.f);
// Get world location and direction of crosshairs
FVector CrosshairWorldPos; // Crosshair location in world space
FVector CrosshairFwdDir; // Crosshair forward direction in world space (imagine pointing from screen 'into' computer monitor)
UGameplayStatics::DeprojectScreenToWorld(UGameplayStatics::GetPlayerController(this, 0), CrosshairScreenPosition, CrosshairWorldPos, CrosshairFwdDir);
const FVector PlayerLookDir = CrosshairWorldPos + CrosshairFwdDir * VECTOR_MULTIPLIER;//VECTOR_MULTIPLIER is an arbitrarily large scalar just to create a large enough vector to compare with
Get direction to Item
This assumes you already have the Item in question you wish to check if the Player is looking at. Refer to the example project for how to get this (and any) Item in range.
const FVector DirectionToItem = Item->GetActorLocation() - CrosshairWorldPos;
Compare 'looking at' and 'item direction' vectors
The dot product of two vectors gives a scalar value from [-1,1].
1: Vectors are pointing in the same direction
0: Vectors are perpendicular
-1: Vectors are opposite
Therefor we can compare the dot product of our two look directions, originating from the same point (crosshair location), with some scalar threshold to determine how parallel the two vectors are.
Let's define the scalar threshold as something very close to 1 which means 'very close to parallel' to claim a player is looking at it
float LookThreshold = 0.99f;
Modify LookThreshold to change how close the crosshairs and item need to be to confirm a player looking at it.
To help choose a value for LookThreshold, plug it into the following equation to get the degree angle desired
angle = acos(LookThreshold) * (180/pi)
Or in code
float Angle = FMath::RadiansToDegrees(FMath::Acos(LookThreshold));
LookThreshold values of:
1 = 0 degree difference meaning exactly parallel. (don't recommend)
0.50 = 60 degree difference
0.75 = 40 degree difference
0.99 = 8 degree difference (what we will use)
0.999 = 2.5 degree difference...etc
// Normalize the vectors so we only account for DIRECTION and not length
float LookAtItemAmount = FVector::DotProduct(PlayerLookDir.GetSafeNormal(), DirectionToItem.GetSafeNormal());
bool bIsLookingAtItem = LookAtItemAmount >= LookThreshold;
if (bIsLookingAtItem )
{
// <Insert Custom Logic for whatever it means for the player to be looking at this AActor type here>
}
Example: Player not looking close enough

Example: Player looking close enough

What if I don't have crosshairs?
I chose to use the crosshair to determine where the player was looking, but this could easily be applied to any situation.
Just perform the calculations from wherever you want your player to indicate their look direction; where the character mesh is facing, where the crosshairs are, where the screen is pointing, anything.
Just be sure to start the two vectors (Look Direction and Direction to Item) from the same starting point.
Conclusion
Find the world space location of the player's look (crosshair location).
Get a vector for the player's look in the forward direction representing their look direction
Get a vector for the direction from the player's look to the item.
Compare the Dot Product of these two vectors to some threshold and use that to determine it the player is looking at the item
_cp
Last updated