Last active
November 10, 2022 14:16
-
-
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
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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