I am implementing a screen indicator in the HUD. It always points into the direction of the Actor called F1 to give the player an idea what direction to go to. This is no groundbreaking stuff. In theory, this is how I do it:
In the tick method of the HUD:
Step 1: Get the screen location of F1
FVector2D ScreenLocation;
const bool bProjected = GetOwningPlayerController()->ProjectWorldLocationToScreen(VecF1, ScreenLocation, true);
Step 2A: Convert to coordinates that have the center of the screen as origin:
const float XFromCenter = ScreenLocation.X - .5;
const float YFromCenter = ScreenLocation.Y - .5;
Note: Alternatively, you can use ProjectWorldLocationToScreen
with false
(the default value of the last parameter) and then do the conversion to relative coordinates manually like this:
Step 2B: Manually convert to relative coordinates
const auto Vec2DSize = UWidgetLayoutLibrary::GetViewportSize(GetWorld());
const float XFromCenter = ScreenLocation.X / Vec2DSize.X - .5;
const float YFromCenter = ScreenLocation.Y / Vec2DSize.Y - .5;
… only that 2A and 2B have different results. I don’t understand what that last parameter means.
Step 3: Check if F1 is off screen:
if(abs(XFromCenter) > 0.5 || abs(YFromCenter) > 0.5)
{
// ...
}
Step 4: Check if F1 is above/below the viewport: the “vertical case”, in constrast to to the left of/to the right of the viewport (the “horizontal case”):
if(abs(YFromCenter / XFromCenter) > 1.)
{
// above/below viewport, "vertical case"
// ...
}
Step 5: Get the x screen location for the indicator overlay
OverlayX = XFromCenter * 0.5 / abs(YFromCenter);
Step 6: Get the y screen location for the indicator overlay:
OverlayY = XFromCenter < 0 ? -0.5 : 0.5;
Step 7: Do the same for the horizontal case
Step 8: Set the position of the indicator overlay:
const auto ViewportScale = UWidgetLayoutLibrary::GetViewportScale(GetWorld());
UWidgetLayoutLibrary::SlotAsCanvasSlot(CanvasF1)->SetPosition(FVector2D((OverlayX + .5) * Vec2DSize.X, (OverlayY + .5) * Vec2DSize.Y) / ViewportScale);
There are quite a number of potential mistakes.
- Conversion with
ViewportScale
- Conversion to relative coordinates
- Calculation of
OverlayX
andOverlayY
But I have high confidence in that part of the code. I am frustrated by the fact that ProjectWorldLocationToScreen
apparently isn’t well behaved. Sometimes it returns false
, indicating that the world location couldn’t be projected. But when is that the case? When the actor is behind the camera, maybe? I don’t get it.
Checking my code, ProjectWorldLocationToScreen
returns false
quite erratically. I have a tilted top-down view on my character and F1 is always on the plane that I am looking down at … given that, I might implement my own version of ProjectWorldLocationToScreen
, but how odd. Isn’t my use case very straightforward?
Then, the two versions of ProjectWorldLocationToScreen
, differentiated by the last parameter: Why do my alternatives have different outcomes? What does that parameter do?
EDIT: I fixed one bug in my code and removed the question that corresponded to it.