Virtual Joytick (Touch interface) do not use DPI Scaling like UMG


I just found that virtual joystick do not use the same scaling as UMG widget.


static FORCEINLINE float GetScaleFactor(const FGeometry& Geometry)

it is hardcoded a **const float DesiredWidth = 1024.0f; **to calculate DPI.

It is hard to place UMG widget properly with Joytick as they don’t follow the same DPI rules.

I’m on a mobile game so the resolution change a lot between devices and I need to be able to control this.

I submit a “bug” request but until then can someone help me on how to fix this?

I already try to do in the GetScaleFactor:

const FVector2D viewportSize = FVector2D(GEngine->GameViewport->Viewport->GetSizeXY());
const float viewportScale = GetDefault<UUserInterfaceSettings>(UUserInterfaceSettings::StaticClass())->GetDPIScaleBasedOnSize(FIntPoint(viewportSize.X, viewportSize.Y));

Sadly, the result are not right. I must miss something in the paint/geometry allocation process in slate but I can get where.


@Nick_Darnell I know you build a lot of the UMG Widgets, could you please give me some hint on how to solve this?

Little bump in case anyone knows how to fix this.


Ran into this as well due to having UI objects in both HUD (not DPI scaled) and HUDWidget (DPI scaled). Use these to translate when needed:

#include "Runtime/UMG/Public/Blueprint/WidgetLayoutLibrary.h"
#include "Runtime/Engine/Classes/Engine/UserInterfaceSettings.h"
#include "Runtime/Engine/Public/SceneView.h"

* Get DPI scale of UMG HUD.
* Used to translate actual pixels to DPI scaled pixels.
float UPrimaryHUDWidget::Get_UMG_DPI_Scale()
 // need a variable here to pass to the GetViewportSize function
 FIntPoint viewportSize = GetWorld()->GetGameViewport()->Viewport->GetSizeXY();

 // we need to floor the float values of the viewport size so we can pass those into the GetDPIScaleBasedOnSize function
 int32 X = FGenericPlatformMath::FloorToInt(viewportSize.X);
 int32 Y = FGenericPlatformMath::FloorToInt(viewportSize.Y);

 // the GetDPIScaleBasedOnSize function takes an FIntPoint, so we construct one out of the floored floats of the viewport
 // the fuction returns a float, so we can return the value out of our function here
 return GetDefault<UUserInterfaceSettings>(UUserInterfaceSettings::StaticClass())->GetDPIScaleBasedOnSize(FIntPoint(X, Y));

* Get inverse DPI scale of UMG HUD.
* Use to translate DPI scaled pixels to actual pixels.
float UPrimaryHUDWidget::Get_Inverse_UMG_DPI_Scale()
 float dpiScale = Get_UMG_DPI_Scale();
 if (dpiScale <= 0.0)
  return 1.0f;
  return 1.0f / dpiScale;

An easy test to ensure your DPI scaling works is draw a crosshair in the center of the DPI-scaled HUD based on the actual ViewPort pixel width and height:

    // to get center of screen here we need to invert UMG DPI scaling
    FVector2D iconDrawPosition;
    FIntPoint size = GetWorld()->GetGameViewport()->Viewport->GetSizeXY();
    iconDrawPosition.X = (float)(size.X) * 0.5f *  Get_Inverse_UMG_DPI_Scale();
    iconDrawPosition.Y = (float)(size.Y) * 0.5f *  Get_Inverse_UMG_DPI_Scale();


thanks for the answer. In fact the joystick is a bit particular.
If you want to apply the DPI as any other, you need to return 1.f in GetScaleFactor(). Otherwise, you apply twice the DPI scale.
I opened a issue case in Unreal and hope they will fix that soon. Otherwise, you need to setup your own class and override a lot of stuff to get this little change working.