None of these versions worked with UMG widgets. So I had to write one myself, based on the last published on by
#include "WidgetLayoutLibrary.h"
#include "SlateBlueprintLibrary.h"
void USLR_BlueprintLibrary::FindScreenEdgeLocationForWorldLocation(UObject* WorldContextObject,
const FVector& InLocation, const float EdgePercent, FVector2D& OutScreenPosition,
float& OutRotationAngleDegrees, bool &bIsOnScreen, bool &FacingBackward)
{
bIsOnScreen = false;
OutRotationAngleDegrees = 0.f;
FacingBackward = false;
FVector ContrivedParallelLocation = InLocation;
if (!GEngine) return;
UWorld* World = GEngine->GetWorldFromContextObjectChecked(WorldContextObject);
if (!World) return;
APlayerController* PlayerController = (WorldContextObject ? UGameplayStatics::GetPlayerController(WorldContextObject, 0) : NULL);
if (!PlayerController) return;
APlayerCameraManager* ViewPointActor = (WorldContextObject ? UGameplayStatics::GetPlayerCameraManager(WorldContextObject, 0) : NULL);
if (!ViewPointActor) return;
FVector Location = ViewPointActor->GetCameraLocation();
FVector Forward = ViewPointActor->GetActorForwardVector();
FVector Offset = (InLocation - Location).GetSafeNormal();
float DotProduct = FVector::DotProduct(Forward, Offset);
bool bLocationIsBehindCamera = (DotProduct < 0);
if (bLocationIsBehindCamera)
{
// For behind the camera situation, I project the objects location to the plane perpendicular to the viewport of the camera
// then put it a few centimeters forward of the camera so it doesn't appear on in the view but does return a reasonable angle
// for a circular pointer (like a compass pointing toward the enemy.
FacingBackward = true;
float DistanceToCamera = (InLocation - Location).Size(); //Get distance to camera
ContrivedParallelLocation = InLocation + (ViewPointActor->GetActorForwardVector() * (DistanceToCamera + 10000.f)); //Project forward
}
UWidgetLayoutLibrary::ProjectWorldLocationToWidgetPosition(
PlayerController, ContrivedParallelLocation, OutScreenPosition);
FVector2D ViewportSize = UWidgetLayoutLibrary::GetViewportSize(WorldContextObject);
USlateBlueprintLibrary::ScreenToViewport(PlayerController, ViewportSize, ViewportSize);
const FVector2D ViewportCenter = FVector2D(ViewportSize.X / 2, ViewportSize.Y / 2);
// Check to see if it's on screen. If it is, ProjectWorldLocationToScreen is all we need, return it.
if ((OutScreenPosition.X >= 0.f && OutScreenPosition.X <= ViewportSize.X
&& OutScreenPosition.Y >= 0.f && OutScreenPosition.Y <= ViewportSize.Y) && (!FacingBackward))
{
bIsOnScreen = true;
return;
}
OutScreenPosition -= ViewportCenter;
float AngleRadians = FMath::Atan2(OutScreenPosition.Y, OutScreenPosition.X);
AngleRadians -= FMath::DegreesToRadians(90.f);
OutRotationAngleDegrees = FMath::RadiansToDegrees(AngleRadians) + 180.f;
float Cos = cosf(AngleRadians);
float Sin = -sinf(AngleRadians);
OutScreenPosition = FVector2D(ViewportCenter.X + (Sin * 180.f), ViewportCenter.Y + Cos * 180.f);
float m = Cos / Sin;
FVector2D ScreenBounds = ViewportCenter * EdgePercent;
if (Cos > 0)
{
OutScreenPosition = FVector2D(ScreenBounds.Y / m, ScreenBounds.Y);
}
else
{
OutScreenPosition = FVector2D(-ScreenBounds.Y / m, -ScreenBounds.Y);
}
if (OutScreenPosition.X > ScreenBounds.X)
{
OutScreenPosition = FVector2D(ScreenBounds.X, ScreenBounds.X*m);
}
else if (OutScreenPosition.X < -ScreenBounds.X)
{
OutScreenPosition = FVector2D(-ScreenBounds.X, -ScreenBounds.X*m);
}
OutScreenPosition += ViewportCenter;
}
To use it you have to add “UMG” here:
PublicDependencyModuleNames.AddRange(new string] { "Core", "CoreUObject", "Engine", "InputCore", "UMG" });