Easy Offscreen Indicator Blueprint Node

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" });

1 Like