{"id":3237,"date":"2021-01-02T19:26:43","date_gmt":"2021-01-02T11:26:43","guid":{"rendered":"http:\/\/blog.coolcoding.cn\/?p=3237"},"modified":"2021-01-02T19:40:29","modified_gmt":"2021-01-02T11:40:29","slug":"creating-screen-size-aware-scaling-for-the-ui","status":"publish","type":"post","link":"https:\/\/blog.coolcoding.cn\/?p=3237","title":{"rendered":"Creating screen size-aware scaling for the UI"},"content":{"rendered":"\n<p>If you have followed the previous recipe, you will notice that when you use&nbsp;<strong>Play In Editor<\/strong>, the button that loads is unusually small.<\/p>\n\n\n\n<p>The reason for this is UI Scaling, a system that allows you to scale the user interface based on the screen size. User interface elements are represented in terms of pixels, usually in absolute terms (the button should be 10 pixels tall).<\/p>\n\n\n\n<p>The problem with this is that if you use a higher-resolution panel, 10 pixels might be much smaller, because each pixel is smaller in size.<\/p>\n\n\n\n<h1 class=\"wp-block-heading\">Getting ready<\/h1>\n\n\n\n<p>The UI scaling system in Unreal allows you to control a global scale modifier, which will scale all the controls on the screen based on the screen resolution. Given the previous example, you might wish to adjust the size of the button so that its apparent size is unchanged when you view your UI on a smaller screen. This recipe shows two different methods for altering the scaling rates.<\/p>\n\n\n\n<h1 class=\"wp-block-heading\">How to do it&#8230;<\/h1>\n\n\n\n<ol><li>Create a custom&nbsp;PlayerController&nbsp;subclass. Call it&nbsp;ScalingUIPlayerController.<\/li><li>Inside the class, override&nbsp;BeginPlay:<\/li><\/ol>\n\n\n\n<pre class=\"wp-block-preformatted\">#pragma once<br><br>#include \"CoreMinimal.h\"<br>#include \"GameFramework\/PlayerController.h\"<br>#include \"ScalingUIPlayerController.generated.h\"<br><br>\/**<br> * <br> *\/<br>UCLASS()<br>class CHAPTER_14_API AScalingUIPlayerController : public APlayerController<br>{<br>    GENERATED_BODY()<br>    <br><strong>public:<\/strong><br><strong>    virtual void BeginPlay() override;<\/strong><br>};<br> <\/pre>\n\n\n\n<ol><li>Add the following code in the implementation of that function inside of&nbsp;ScalingUIPlayerController.cpp:<\/li><\/ol>\n\n\n\n<pre class=\"wp-block-preformatted\">#include \"ScalingUIPlayerController.h\"<br><strong>#include \"SlateBasics.h\"<\/strong><br><br><strong>void AScalingUIPlayerController::BeginPlay()<\/strong><br><strong>{<\/strong><br><strong>    Super::BeginPlay();<\/strong><br><strong>    TSharedRef&lt;SVerticalBox&gt; widget = SNew(SVerticalBox)<\/strong><br><strong>        + SVerticalBox::Slot()<\/strong><br><br><strong>        .HAlign(HAlign_Center)<\/strong><br><strong>        .VAlign(VAlign_Center)<\/strong><br><strong>        [<\/strong><br><strong>            SNew(SButton)<\/strong><br><strong>            .Content()<\/strong><br><strong>        [<\/strong><br><strong>            SNew(STextBlock)<\/strong><br><strong>            .Text(FText::FromString(TEXT(\"Test button\")))<\/strong><br><strong>        ]<\/strong><br><strong>        ];<\/strong><br><strong>    GEngine-&gt;GameViewport-&gt;AddViewportWidgetForPlayer(GetLocalPlayer(), widget, 1);<\/strong><br><strong>}<\/strong><\/pre>\n\n\n\n<ol><li>Create a new&nbsp;GameModeBase&nbsp;subclass called&nbsp;ScalingUIGameMode&nbsp;and give it a default constructor:<\/li><\/ol>\n\n\n\n<pre class=\"wp-block-preformatted\">#pragma once<br><br>#include \"CoreMinimal.h\"<br>#include \"GameFramework\/GameModeBase.h\"<br>#include \"ScalingUIGameMode.generated.h\"<br><br>\/**<br> * <br> *\/<br>UCLASS()<br>class CHAPTER_14_API AScalingUIGameMode : public AGameModeBase<br>{<br>    GENERATED_BODY()<br>    <br><strong>    AScalingUIGameMode();<\/strong><br>};<\/pre>\n\n\n\n<ol><li>Within the default constructor, set the default player controller class to&nbsp;ScalingUIPlayerController:<\/li><\/ol>\n\n\n\n<pre class=\"wp-block-preformatted\">#include \"ScalingUIGameMode.h\"<br>#include \"CustomHUDPlayerController.h\"<br><br>AScalingUIGameMode::AScalingUIGameMode() : AGameModeBase()<br>{<br>    PlayerControllerClass = ACustomHUDPlayerController::StaticClass();<br>}<\/pre>\n\n\n\n<ol><li>Save and compile your new classes.<\/li><li>Within the Editor, open the&nbsp;World Settings&nbsp;menu from the toolbar by going to&nbsp;Settings | World Settings.<\/li><li>Inside&nbsp;World Settings, override the level&#8217;s Game Mode to be our&nbsp;ScalingUIGameMode.<\/li><\/ol>\n\n\n\n<figure class=\"wp-block-image\"><img decoding=\"async\" src=\"http:\/\/blog.coolcoding.cn\/wp-content\/uploads\/2021\/01\/02f9189c-7fad-4349-8bb7-195579247691.png\" alt=\"\"\/><\/figure>\n\n\n\n<p>This should give you a user interface like the one from the previous recipe. Note that the UI is very tiny if you use&nbsp;Play In Editor:<\/p>\n\n\n\n<figure class=\"wp-block-image\"><img decoding=\"async\" src=\"http:\/\/blog.coolcoding.cn\/wp-content\/uploads\/2021\/01\/d136a6b5-4043-4f09-8443-541766ef6a1c.png\" alt=\"\"\/><\/figure>\n\n\n\n<p>Tiny button on the game screen&nbsp;<\/p>\n\n\n\n<p>To alter the rate at which the UI scales down or up, we need to change the scaling curve. We can do that through two different methods.<\/p>\n\n\n\n<h1 class=\"wp-block-heading\">Using the In-Editor method<\/h1>\n\n\n\n<ol><li>Launch Unreal, then open the&nbsp;Project Settings&nbsp;dialog through the&nbsp;Edit&nbsp;menu:<\/li><\/ol>\n\n\n\n<figure class=\"wp-block-image\"><img decoding=\"async\" src=\"http:\/\/blog.coolcoding.cn\/wp-content\/uploads\/2021\/01\/4bf372f4-6819-4ee5-96f2-662d40adc52c.jpg\" alt=\"\"\/><\/figure>\n\n\n\n<ol><li>Under the&nbsp;Engine &#8211; User Interface&nbsp;section, there is a curve called the&nbsp;DPI Curve, which can be used to alter the UI scaling factor based on the short dimension of your screen:<\/li><\/ol>\n\n\n\n<figure class=\"wp-block-image\"><img decoding=\"async\" src=\"http:\/\/blog.coolcoding.cn\/wp-content\/uploads\/2021\/01\/b3a198bb-bb69-47ea-a3d9-61f52351f475.png\" alt=\"\"\/><\/figure>\n\n\n\n<ol><li>Click on the second dot, or keypoint, on the graph.<\/li><li>Change its&nbsp;Scale&nbsp;value to&nbsp;1. Then, do the same for the first dot and set its&nbsp;Scale&nbsp;value to&nbsp;1&nbsp;as well:<\/li><\/ol>\n\n\n\n<figure class=\"wp-block-image\"><img decoding=\"async\" src=\"http:\/\/blog.coolcoding.cn\/wp-content\/uploads\/2021\/01\/71b43d27-052b-49b2-a2f5-28c1e89ae87c.png\" alt=\"\"\/><\/figure>\n\n\n\n<ol><li>Return to the main editor and run the game again. You should notice that the button is larger than it was before:<\/li><\/ol>\n\n\n\n<figure class=\"wp-block-image\"><img decoding=\"async\" src=\"http:\/\/blog.coolcoding.cn\/wp-content\/uploads\/2021\/01\/91c6e745-0bc1-4555-9288-fe5dca318431.png\" alt=\"\"\/><\/figure>\n\n\n\n<p>Easier to see button on the game screen\n\n<\/p>\n\n\n\n<h1 class=\"wp-block-heading\">Using the Config file method<\/h1>\n\n\n\n<ol><li>Browse to your project directory and look inside the&nbsp;Config&nbsp;folder:<\/li><\/ol>\n\n\n\n<figure class=\"wp-block-image\"><img decoding=\"async\" src=\"http:\/\/blog.coolcoding.cn\/wp-content\/uploads\/2021\/01\/e4843569-d39c-408e-a063-9829f0467739.png\" alt=\"\"\/><\/figure>\n\n\n\n<ol><li>Open&nbsp;DefaultEngine.ini, which is located in the&nbsp;Config&nbsp;folder of your project, inside your text editor of choice.<\/li><li>Find the&nbsp;[\/Script\/Engine.UserInterfaceSettings]&nbsp;section:<\/li><\/ol>\n\n\n\n<pre class=\"wp-block-preformatted\">[\/Script\/Engine.UserInterfaceSettings]<br>UIScaleCurve=(EditorCurveData=(PreInfinityExtrap=RCCE_Constant,PostInfinityExtrap=RCCE_Constant,DefaultValue=340282346638528859811704183484516925440.000000,Keys=((Time=480.000000,Value=1.000000),(Time=720.000000,Value=1.000000),(Time=1080.000000,Value=1.000000),(Time=8640.000000,Value=8.000000))),ExternalCurve=None)<\/pre>\n\n\n\n<ol><li>Look for a key called&nbsp;UIScaleCurve&nbsp;in that section.<\/li><li>In the value for that key, you&#8217;ll notice a number of&nbsp;(Time=x,Value=y)&nbsp;pairs. Edit the second pair so that its&nbsp;Time&nbsp;value is&nbsp;720.000000&nbsp;and the&nbsp;Value&nbsp;is&nbsp;1.000000&nbsp;if it isn&#8217;t already.<\/li><li>Restart the editor if you have it open.<\/li><li>Start the&nbsp;Play In Editor&nbsp;preview to confirm that your UI now remains readable at the&nbsp;<strong>PIE<\/strong>&nbsp;screen&#8217;s resolution (assuming you are using a 1080p monitor so that the PIE window is running at 720p or thereabouts):<\/li><\/ol>\n\n\n\n<figure class=\"wp-block-image\"><img decoding=\"async\" src=\"http:\/\/blog.coolcoding.cn\/wp-content\/uploads\/2021\/01\/288286d3-a546-4e7b-b440-5fc65abe4c8a.jpg\" alt=\"\"\/><\/figure>\n\n\n\n<ol><li>You can also see how the scaling works if you use a&nbsp;New Editor Window&nbsp;to preview your game.<\/li><li>To do so, click on the arrow to the right of&nbsp;Play&nbsp;on the toolbar.<\/li><li>Select&nbsp;New Editor Window.<\/li><li>Inside this window, you can use the console command&nbsp;r.SetRes widthxheight&nbsp;to change the resolution (for example,&nbsp;r.SetRes 200&#215;200), and observe the changes that result from doing so.<\/li><\/ol>\n\n\n\n<h1 class=\"wp-block-heading\">How it works&#8230;<\/h1>\n\n\n\n<p>As usual, when we want to use a custom&nbsp;PlayerController, we need a custom&nbsp;GameMode&nbsp;to specify which&nbsp;PlayerController&nbsp;to use.<\/p>\n\n\n\n<p>We create both a custom&nbsp;PlayerController&nbsp;and&nbsp;GameMode, and then place some&nbsp;Slate&nbsp;code in the&nbsp;BeginPlay&nbsp;method of&nbsp;PlayerController&nbsp;so that some UI elements are drawn.<\/p>\n\n\n\n<p>Because the main game viewport is usually quite small within the Unreal editor, the UI initially shows in a scaled-down fashion.&nbsp;This is intended to allow for the game UI to take up less room on smaller resolution displays, but this can have the side effect of making the text very difficult to read if the window isn&#8217;t being stretched to fit the full screen.<\/p>\n\n\n\n<p>Unreal stores the configuration data that should persist between sessions but not necessarily be hard coded into the executable inside config files.&nbsp;Config files use an extended version of the&nbsp;.ini&nbsp;file format, which has been commonly used with Windows software.<\/p>\n\n\n\n<p>Config files store data using the following syntax:<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">[Section Name] \nKey=Value <\/pre>\n\n\n\n<p>Unreal has a&nbsp;UserInterfaceSettings&nbsp;class, with a property called&nbsp;UIScaleCurve&nbsp;on it.&nbsp;That&nbsp;UPROPERTY&nbsp;is marked as config, so Unreal serializes the value to the&nbsp;.ini&nbsp;file.<\/p>\n\n\n\n<p>As a result, it stores the&nbsp;UIScale&nbsp;data in the&nbsp;DefaultEngine.ini&nbsp;file, in the&nbsp;Engine.UserInterfaceSettings&nbsp;section.<\/p>\n\n\n\n<p>The data is stored using a text format, which contains a list of key points. Editing the&nbsp;Time,&nbsp;Value&nbsp;pairs alters or adds new key points to the curve.<\/p>\n\n\n\n<p>The&nbsp;Project Settings&nbsp;dialog is a simple frontend for directly editing the&nbsp;.ini&nbsp;files yourself, and for designers, it is an intuitive way to edit the curve. However, having the data stored textually allows for programmers to potentially develop build tools that modify properties such as&nbsp;UIScale&nbsp;without having to recompile their game.<\/p>\n\n\n\n<p>Time&nbsp;refers to the input value. In this case, the input value is the narrower dimension of the screen (usually, the height).<\/p>\n\n\n\n<p>Value&nbsp;is the universal scaling factor that&#8217;s applied to the UI when the screen&#8217;s narrow dimension is approximately the height of the value in the&nbsp;Time&nbsp;field.<\/p>\n\n\n\n<p>So, to set the UI to remain normal-sized at a 1280 x 720 resolution, set the time\/input factor to 720 and the scale factor to 1.<\/p>\n\n\n\n<h1 class=\"wp-block-heading\">See also<\/h1>\n\n\n\n<ul><li>You can refer to the UE4 documentation for more information regarding config files:&nbsp;<a href=\"https:\/\/docs.unrealengine.com\/en-US\/Programming\/Basics\/ConfigurationFiles\">https:\/\/docs.unrealengine.com\/en-US\/Programming\/Basics\/ConfigurationFiles<\/a>.<\/li><\/ul>\n\n\n\n<p><\/p>\n\n\n\n<p><\/p>\n\n\n\n<p><\/p>\n","protected":false},"excerpt":{"rendered":"<p>If you have followed the previous recipe, you will noti [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[27,1],"tags":[26],"_links":{"self":[{"href":"https:\/\/blog.coolcoding.cn\/index.php?rest_route=\/wp\/v2\/posts\/3237"}],"collection":[{"href":"https:\/\/blog.coolcoding.cn\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/blog.coolcoding.cn\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/blog.coolcoding.cn\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/blog.coolcoding.cn\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=3237"}],"version-history":[{"count":1,"href":"https:\/\/blog.coolcoding.cn\/index.php?rest_route=\/wp\/v2\/posts\/3237\/revisions"}],"predecessor-version":[{"id":3246,"href":"https:\/\/blog.coolcoding.cn\/index.php?rest_route=\/wp\/v2\/posts\/3237\/revisions\/3246"}],"wp:attachment":[{"href":"https:\/\/blog.coolcoding.cn\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=3237"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/blog.coolcoding.cn\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=3237"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/blog.coolcoding.cn\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=3237"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}