Skip to content

Instantly share code, notes, and snippets.

@rubenmoor
Last active November 10, 2022 14:16
Show Gist options
  • Save rubenmoor/d4afad7de45fd206ad07417845009570 to your computer and use it in GitHub Desktop.
Save rubenmoor/d4afad7de45fd206ad07417845009570 to your computer and use it in GitHub Desktop.
Unreal/C++, use `ProjectWorldLocationToScreen` to display an indicator that always points in the direction of some object in the world
void AMyHUD::Tick(float DeltaSeconds)
{
Super::Tick(DeltaSeconds);
const APlayerController* PC = GetOwningPlayerController();
const FVector2D Vec2DSize = UWidgetLayoutLibrary::GetViewportSize(GetWorld());
FVector2D ScreenLocation;
// `FVector VecF1` is the location of F1, the point in world space that I am tracking via screen indicator
// try to project to screen coordinates, ...
const bool bProjected = PC->ProjectWorldLocationToScreen(VecF1, ScreenLocation);
// alternative method, don't know the advantages, but supposedly takes into account UI scaling
//const bool bProjected = UWidgetLayoutLibrary::ProjectWorldLocationToWidgetPosition(GetOwningPlayerController(), VecF1, ScreenLocation, false);
// I am using my own coordinates, X/YFromCenter, that go from -0.5 to 0.5, with (0,0) being the center of the screen
// Y = 0 is the top, Y = 1 is the bottom
float XFromCenter;
float YFromCenter;
// ... which doesn't always work, i.e. F1 must be in front of the camera, I believe, which isn't always the case
if(!bProjected)
{
FVector CameraLocation;
FRotator CameraRotation;
PC->GetPlayerViewPoint(CameraLocation, CameraRotation);
// ... but in that case we construct a new VecF1 that is right in front of the camera, in the same plane as the player
// `VecR` is the world location of my player (third-person view)
const FVector VecF1InViewportPlane =
VecF1
+ CameraRotation.Vector()
* (VecR - VecF1).Length();
// ... and we try again
if(!GetOwningPlayerController()->ProjectWorldLocationToScreen(VecF1InViewportPlane, ScreenLocation))
{
// If the manual projection fails, too, we're out of options
UE_LOG(LogActor, Error, TEXT("AMyHUD::Tick: couldn't project Center of mass to screen"))
}
// only we have to make sure that this manually conceived screen location `VecF1InViewportPlane` doesn't accidentally
// end up on screen
// ... we convert to the "FromCenter" coordinate system
XFromCenter = ScreenLocation.X / Vec2DSize.X - .5;
YFromCenter = ScreenLocation.Y / Vec2DSize.Y - .5;
// ... and move the projected F1 out of the viewport, maintaining the direction of the indicator
YFromCenter += copysign(0.5 * YFromCenter / XFromCenter, YFromCenter);
XFromCenter += copysign(0.5, XFromCenter);
}
else
{
// convert to the "FromCenter" coordinate system and we are done
XFromCenter = ScreenLocation.X / Vec2DSize.X - .5;
YFromCenter = ScreenLocation.Y / Vec2DSize.Y - .5;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment