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

players POV (looking where the crosshairs are located)

Example: Player looking close enough

players POV (looking where the crosshairs are located)

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