Post

Moving Game To The Other Display In UE

3 min read

Hi reader, this is the first post of my website, so I would like to hear about any suggestions or feedbacks in the comments section. Thanks in advance :)

Opening

Let’s say you wanna create an option to allow players to change the display where game is rendering, how do you do that in Unreal Engine?

Example Of The Display Setting Example Of The Display Setting

I was wondering the same thing a few days ago, so I decided to tinker around and implement this feature. I thought it will be easy, but… Let’s get to it.

First Try

If you ever created an options menu of some kind, you probably know first place to look at is UGameUserSettings. For those who doesn’t know about it, it’s a good high level interface to change some graphical settings without getting too deep into engine. Also it’s exposed to the Blueprints

Example Image Of The Setting Example Of The GameUserSettings

But unfortunately, the function I’m looking for isn’t exposed to the UGameUserSettings, so we need to go a bit deeper.


The Solution

I tinkered around for a while and tried a lot of stuff which didn’t work. Finally I have found a solution:

First of all we need a way to retrieve a list of displays that is connected to players PC.

1
2
FDisplayMetrics Metrics;
FDisplayMetrics::RebuildDisplayMetrics(Metrics);


FDisplayMetrics contains following members:

1
2
3
4
5
6
7
int32 PrimaryDisplayWidth;
int32 PrimaryDisplayHeight;
TArray<FMonitorInfo> MonitorInfo;
FPlatformRect PrimaryDisplayWorkAreaRect;
FPlatformRect VirtualDisplayRect;
FVector4 TitleSafePaddingSize;
FVector4 ActionSafePaddingSize;

we are only interested in MonitorInfo for the purposes of this article, but feel free to play around. It is a list of all the displays.


FMonitorInfo contains following members:

1
2
3
4
5
6
7
8
9
FString Name;
FString ID;
int32 NativeWidth = 0;
int32 NativeHeight = 0;
FIntPoint MaxResolution = FIntPoint(ForceInitToZero);
FPlatformRect DisplayRect;
FPlatformRect WorkArea;
bool bIsPrimary = false;
int32 DPI = 0;

If you wanna check the resolution of the displays use NativeWidth and NativeHeight. For some reason MaxResolution didn’t work for me. Keep in mind it might be a bug on my end or an issue with the engine


I’m not gonna dive into it any further, but for creating this kind of an option UI, you have everything in these structs. For the sake of simplicity I’ll assume you already know which display you wanna change to.

So let’s say you have an ID for the display you wanna use and it’s stored into a variable named TargetDisplayID

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
FDisplayMetrics Metrics;
FDisplayMetrics::RebuildDisplayMetrics(Metrics);

// Get the index of target display 
int TargetDisplayIndex = -1;
for(int i = 0; i < Metrics.MonitorInfo.Num(); i++)
{
    if(Metrics.MonitorInfo[i].ID == TargetDisplayID)
    {
        TargetDisplayIndex = i;
        break;
    }
}

if(TargetDisplayIndex == -1)
{
    // We couldn't find requested display
    // Throw an error, log failure, etc
}

// Get the position of requested display in the virtual space of all the displays combined
// For example if 2x 1080p displays is positioned side by side, top left corner of the right display would be (1920, 0) instead of (0, 0)
const FMonitorInfo& TargetDisplay = Metrics.MonitorInfo[TargetDisplayIndex];
const FVector2D WindowPosition = FVector2D(TargetDisplay.DisplayRect.Left, TargetDisplay.DisplayRect.Top);

if(GEngine && GEngine->GameViewport)
{
    // Here we are getting the slate window
    const TSharedPtr<SWindow> GWindow = GEngine->GameViewport->GetWindow();

    // Basically we are getting into windowed mode and moving window to the target display
    // Then we are going back into fullscreen or windowed fullscreen
    // Not clean, not the best way, but it's the only way works (which I can find)
    GWindow->SetWindowMode(EWindowMode::Windowed);
    // -> Here
    GWindow->MoveWindowTo(WindowPosition);
    GWindow->SetWindowMode(EWindowMode::WindowedFullscreen);

    // Optionally you can resize the window to a very small size before moving it to ensure correct behaviour
    // As far as I know, for a window to be counted in a display only more than half of it needs to be inside
    // So I did place following line to the marked line above (line 35):
    // GWindow->Resize(FVector2D(800, 600)); 
}


Ideally you could just handle it from UGameViewportClient and it would be exposed to the Blueprints too, but here we are.

Hope this article helps, see you in the comments section!


This post is licensed under CC BY 4.0 by the author.