Offscreen Indicator

Hello,
this is probably not the most elegant solution, but it works and I thought someone might find it useful.
The function returns true when given WorldLocation is within viewport, false when it is offscreen, with nearest screen position.

Declaration:




UFUNCTION(BlueprintPure, meta = (Keywords = "world location to screen position"))
static bool ProjectWorldToScreen(const APlayerController* PlayerController, FVector WorldLocation, FVector2D& OutScreenPosition);



Implementation: [4.16]



bool YourClassName::ProjectWorldToScreen(const APlayerController* PlayerController, FVector WorldLocation, FVector2D& OutScreenPosition)
{
	if (!PlayerController)
	{
		return false;
	}

	FVector2D ScreenPosTemp(-1.f, -1.f);

	PlayerController->ProjectWorldLocationToScreen(WorldLocation, ScreenPosTemp);

	const FVector2D ViewportSize = GEngine->GameViewport->Viewport->GetSizeXY();
	bool bLocationIsInViewport = ScreenPosTemp.X >= 0.0f && ScreenPosTemp.X <= ViewportSize.X && ScreenPosTemp.Y >= 0.0f && ScreenPosTemp.Y <= ViewportSize.Y;

	if (bLocationIsInViewport) /*It is on the screen*/
	{
		OutScreenPosition = ScreenPosTemp;
		return true;
	}
	else /*It is not on the screen*/
	{
		const FVector CameraLocation = PlayerController->PlayerCameraManager->GetCameraLocation();
		const FRotator CameraRotation = PlayerController->PlayerCameraManager->GetCameraRotation();

		const FVector CameraForwardVector = UKismetMathLibrary::GetForwardVector(CameraRotation);
		const FVector CameraRightVector = UKismetMathLibrary::GetRightVector(CameraRotation);
		const FVector CameraUpVector = UKismetMathLibrary::GetUpVector(CameraRotation);

		const FVector ProjectedVector = UKismetMathLibrary::ProjectVectorOnToPlane(WorldLocation - CameraLocation, CameraForwardVector).GetSafeNormal();

		// Some random calculation to determine wheter the angle is in upper or lower half
		const float DotProduct = FVector::DotProduct(ProjectedVector, CameraUpVector);

		float Angle = 0.0f;

		// Set angle to -180 .. 180 range
		if (DotProduct > 0.0f)
		{
			const float Dot = FVector::DotProduct(ProjectedVector, CameraRightVector);
			Angle = -UKismetMathLibrary::DegAcos(Dot); // negative angle
		}
		else
		{
			const float Dot = FVector::DotProduct(ProjectedVector, CameraRightVector);
			Angle = UKismetMathLibrary::DegAcos(Dot); // positive angle
		}

		// Coordinates of 4 borders in viewport screen
		const FVector UpperLeftCorner(0.f, 0.f, 0.f);
		const FVector LowerLeftCorner(0.f, ViewportSize.Y, 0.f);
		const FVector UpperRightCorner(ViewportSize.X, 0.f, 0.f);
		const FVector LowerRightCorner(ViewportSize.X, ViewportSize.Y, 0.f);		
		
		const FVector ViewportCentre(ViewportSize.X / 2.0f, ViewportSize.Y / 2.0f, 0.f);		

		// Vector that points from centre of the screen to the WorldPosition
		FVector DirectionVector(UKismetMathLibrary::DegCos(Angle), UKismetMathLibrary::DegSin(Angle), 0.f);
		DirectionVector *= 10'000.f;	// multiplying by some value to make sure end of this vector will always lie outside of the screen
		DirectionVector += ViewportCentre;

		FVector IntersectionPoint = FVector::ZeroVector;

		if (Angle <= 90.f)
		{
			if (Angle <= 0.f)
			{
				if (Angle <= -90.f)
				{
					// 2. Quadrant						
					FMath::SegmentIntersection2D(UpperLeftCorner, UpperRightCorner, ViewportCentre, DirectionVector, IntersectionPoint);
					FMath::SegmentIntersection2D(UpperLeftCorner, LowerLeftCorner, ViewportCentre, DirectionVector, IntersectionPoint);
				}
				else
				{
					// 1. Quadrant						
					FMath::SegmentIntersection2D(UpperLeftCorner, UpperRightCorner, ViewportCentre, DirectionVector, IntersectionPoint);
					FMath::SegmentIntersection2D(UpperRightCorner, LowerRightCorner, ViewportCentre, DirectionVector, IntersectionPoint);
				}
			}
			else
			{
				// 4. Quadrant				
				FMath::SegmentIntersection2D(LowerLeftCorner, LowerRightCorner, ViewportCentre, DirectionVector, IntersectionPoint);
				FMath::SegmentIntersection2D(UpperRightCorner, LowerRightCorner, ViewportCentre, DirectionVector, IntersectionPoint);
			}
		}
		else
		{
			// 3. Quadrant			
			FMath::SegmentIntersection2D(UpperLeftCorner, LowerLeftCorner, ViewportCentre, DirectionVector, IntersectionPoint);
			FMath::SegmentIntersection2D(LowerLeftCorner, LowerRightCorner, ViewportCentre, DirectionVector, IntersectionPoint);

		}

		OutScreenPosition.X = IntersectionPoint.X;
		OutScreenPosition.Y = IntersectionPoint.Y;

		return false;
	}	
}



2 Likes

Thanks @anonymous_user_13a0be99 , real good implementation code. It’s easy to understand and with less code.

@anonymous_user_13a0be99 this seems to work only at 1080p resolution, do you know why that’s the issue?