{"id":3226,"date":"2021-01-02T19:22:35","date_gmt":"2021-01-02T11:22:35","guid":{"rendered":"http:\/\/blog.coolcoding.cn\/?p=3226"},"modified":"2021-01-02T19:40:29","modified_gmt":"2021-01-02T11:40:29","slug":"user-interfaces-ui-and-umg","status":"publish","type":"post","link":"https:\/\/blog.coolcoding.cn\/?p=3226","title":{"rendered":"User Interfaces &#8211; UI and UMG"},"content":{"rendered":"\n<p>In this chapter, we will cover the following recipes:<\/p>\n\n\n\n<ul><li>Drawing using Canvas<\/li><li>Adding Slate Widgets to the screen<\/li><li>Creating screen size-aware scaling for the UI<\/li><li>Displaying and hiding a sheet of UMG elements in-game<\/li><li>Attaching function calls to Slate events<\/li><li>Using Data Binding with Unreal Motion Graphics<\/li><li>Controlling widget appearance with Styles<\/li><li>Creating a custom SWidget\/UWidget<\/li><\/ul>\n\n\n\n<h1 class=\"wp-block-heading\">Introduction<\/h1>\n\n\n\n<p>Displaying feedback to the player is one of the most important elements within game design, and this will usually involve some sort of HUD, or at least menus, within your game.<\/p>\n\n\n\n<p>In previous versions of Unreal, there was simple HUD support, which allowed you to draw simple shapes and text to the screen. However, it was somewhat limited in terms of aesthetics, and so solutions such as&nbsp;<strong>Scaleform<\/strong>&nbsp;became common to work around these limitations. Scaleform leveraged Adobe&#8217;s Flash file format to store vector images and UI scripts. It was not without its own cons for developers, though, not least the cost&nbsp;\u2013&nbsp;it was a third-party product requiring a (sometimes, expensive) license.<\/p>\n\n\n\n<p>As a result, Epic developed Slate for the Unreal 4 editor and the in-game UI framework. Slate is a collection of widgets (UI elements) and a framework that allows for a cross-platform interface for the Editor. It is also usable in-game to draw widgets, such as sliders and buttons, for menus and HUDs.<\/p>\n\n\n\n<p>Slate uses declarative syntax to allow for an XML-style representation of user interface elements in their hierarchy in native C++. It accomplishes this by making heavy use of macros and operator overloading.<\/p>\n\n\n\n<p>That said, not everybody wants to ask their programmers to design the game&#8217;s HUD. One of the significant advantages of using Scaleform within Unreal 3 was the ability to develop the visual appearance of game UIs using the Flash visual editor so that visual designers didn&#8217;t need to learn a programming language. Programmers could then insert the logic and data separately. This is the same paradigm that&#8217;s espoused by the&nbsp;<strong>Windows Presentation Framework<\/strong>&nbsp;(<strong>WPF<\/strong>), for example.<\/p>\n\n\n\n<p>In a similar fashion, Unreal provides&nbsp;<strong>Unreal Motion Graphics<\/strong>&nbsp;(<strong>UMG<\/strong>). UMG is a visual editor for Slate widgets that allows you to visually style, lay out, and animate user interfaces. UI widgets (or controls, if you&#8217;ve come from a Win32 background) can have their properties controlled by either Blueprint code (written in the Graph view of the UMG window) or from C++. This chapter primarily deals with displaying UI elements, creating widget hierarchies, and creating base&nbsp;SWidget&nbsp;classes that can be styled and used within UMG.<\/p>\n\n\n\n<h1 class=\"wp-block-heading\">Technical requirements<\/h1>\n\n\n\n<p>This chapter requires the use of Unreal Engine 4 and uses Visual Studio 2017 as the IDE. Instructions on how to install both pieces of software and the requirements for them can be found in&nbsp;<a href=\"https:\/\/learning.oreilly.com\/library\/view\/unreal-engine-4x\/9781789809503\/4adb307e-d86f-4d70-b6de-a0893102d1cf.xhtml\">Chapter 1<\/a>,&nbsp;<em>UE4 Development Tools<\/em>.<\/p>\n\n\n\n<h1 class=\"wp-block-heading\">Drawing using Canvas<\/h1>\n\n\n\n<p><strong>Canvas<\/strong>&nbsp;is a continuation of the simple HUD that&#8217;s implemented within Unreal 3. While it isn&#8217;t so commonly used within shipping games, mostly being replaced by Slate\/UMG, it&#8217;s simple to use, especially when you want to draw text or shapes to the screen. Canvas drawing is still used extensively by console commands that are used for debugging and performance analysis, such as&nbsp;stat game&nbsp;and the other&nbsp;stat&nbsp;commands<\/p>\n\n\n\n<h1 class=\"wp-block-heading\">Getting ready&#8230;<\/h1>\n\n\n\n<p>Refer to&nbsp;<a href=\"https:\/\/learning.oreilly.com\/library\/view\/unreal-engine-4x\/9781789809503\/eade601c-4b55-484a-ba49-0a989d81d7f8.xhtml\">Chapter 4<\/a>,&nbsp;<em>Actors and Components,<\/em>&nbsp;if you need a refresher on using the C++ Code Wizard.<\/p>\n\n\n\n<h1 class=\"wp-block-heading\">How to do it&#8230;<\/h1>\n\n\n\n<ol><li>From your Visual Studio project (File | Open Visual Studio), open the&nbsp;Source\\&lt;Module&gt;&nbsp;folder and, from there, open the&nbsp;&lt;Module&gt;.build.cs&nbsp;file (in my case, it would be&nbsp;Source\\Chapter_14\\Chapter_14.build.cs) . Uncomment\/add the following line of code:<\/li><\/ol>\n\n\n\n<pre class=\"wp-block-preformatted\">using UnrealBuildTool;<br><br>public class Chapter_14 : ModuleRules<br>{<br>  public Chapter_14(ReadOnlyTargetRules Target) : base(Target)<br>  {<br>    PCHUsage = PCHUsageMode.UseExplicitOrSharedPCHs;<br>  <br>    PublicDependencyModuleNames.AddRange(new string[] { \"Core\", <br>    \"CoreUObject\", \"Engine\", \"InputCore\" });<br><br>    PrivateDependencyModuleNames.AddRange(new string[] { });<br><br>    \/\/ Uncomment if you are using Slate UI<br>    <strong>PrivateDependencyModuleNames.AddRange(new string[] { \"Slate\", <br>    \"SlateCore\" });<\/strong><br>    <br>    \/\/ Uncomment if you are using online features<br>    \/\/ PrivateDependencyModuleNames.Add(\"OnlineSubsystem\");<br><br>    \/\/ To include OnlineSubsystemSteam, add it to the plugins section in your uproject file with the Enabled attribute set to true<br>  }<br>}<\/pre>\n\n\n\n<ol><li>Create a new&nbsp;GameModeBase&nbsp;called&nbsp;CustomHUDGameMode&nbsp;using the editor class wizard.&nbsp;<\/li><li>Add a constructor to the class:<\/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 \"CustomHUDGameMode.generated.h\"<br><br>\/**<br> * <br> *\/<br>UCLASS()<br>class CHAPTER_14_API ACustomHUDGameMode : public AGameModeBase<br>{<br>    GENERATED_BODY()<br><br>    <strong>ACustomHUDGameMode();<\/strong><br>    <br>};<\/pre>\n\n\n\n<ol><li>Add the following to the constructor implementation:<\/li><\/ol>\n\n\n\n<pre class=\"wp-block-preformatted\">#include \"CustomHUDGameMode.h\"<br>#include \"CustomHUD.h\"<br><br>ACustomHUDGameMode::ACustomHUDGameMode() : AGameModeBase()<br>{<br>    HUDClass = ACustomHUD::StaticClass();<br>}<\/pre>\n\n\n\n<p>At this point, you will get compile errors, because the&nbsp;CustomHUD&nbsp;class does not exist. That is what we will be creating next.<\/p>\n\n\n\n<ol><li>Create a new&nbsp;HUD&nbsp;subclass using the&nbsp;Add C++ Class&nbsp;wizard:<\/li><\/ol>\n\n\n\n<figure class=\"wp-block-image\"><img decoding=\"async\" src=\"http:\/\/blog.coolcoding.cn\/wp-content\/uploads\/2021\/01\/7830c68f-9f06-44ce-aedd-20e29710145d.png\" alt=\"\"\/><\/figure>\n\n\n\n<ol><li>When asked for the name, put in&nbsp;CustomHUD, and click on the&nbsp;Create Class&nbsp;button:<\/li><\/ol>\n\n\n\n<figure class=\"wp-block-image\"><img decoding=\"async\" src=\"http:\/\/blog.coolcoding.cn\/wp-content\/uploads\/2021\/01\/279fff54-150b-4019-b6c1-fc9d9f07b0a0.png\" alt=\"\"\/><\/figure>\n\n\n\n<ol><li>Inside&nbsp;CustomHUD.h, add the following function with the&nbsp;override&nbsp;keyword to the class:<\/li><\/ol>\n\n\n\n<pre class=\"wp-block-preformatted\">#pragma once<br><br>#include \"CoreMinimal.h\"<br>#include \"GameFramework\/HUD.h\"<br>#include \"CustomHUD.generated.h\"<br><br>\/**<br> * <br> *\/<br>UCLASS()<br>class CHAPTER_14_API ACustomHUD : public AHUD<br>{<br>    GENERATED_BODY()<br>    <br><strong>public:<\/strong><br><strong>    virtual void DrawHUD() override;<\/strong><br><br>};<\/pre>\n\n\n\n<ol><li>Now, implement the function:<\/li><\/ol>\n\n\n\n<pre class=\"wp-block-preformatted\">#include \"CustomHUD.h\"<br><strong>#include \"Engine\/Canvas.h\"<\/strong><br><br><strong>void ACustomHUD::DrawHUD()<\/strong><br><strong>{<\/strong><br><strong>    Super::DrawHUD();<\/strong><br><strong>    Canvas-&gt;DrawText(GEngine-&gt;GetSmallFont(), TEXT(\"Test string to be printed to screen\"), 10, 10); <\/strong><br><strong>    FCanvasBoxItem ProgressBar(FVector2D(5, 25), FVector2D(100, 5));<\/strong><br><strong>    Canvas-&gt;DrawItem(ProgressBar);<\/strong><br><strong>    DrawRect(FLinearColor::Blue, 5, 25, 100, 5);<\/strong><br><strong>}<\/strong><\/pre>\n\n\n\n<ol><li>Compile your code and launch the editor.<\/li><li>Within the editor, open the&nbsp;World Settings&nbsp;panel from the&nbsp;Settings&nbsp;drop-down 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\/d725eff7-9368-40b6-a5da-84695df4ad25.jpg\" alt=\"\"\/><\/figure>\n\n\n\n<ol><li>In the&nbsp;World Settings&nbsp;dialog, select&nbsp;CustomHUDGameMode&nbsp;from the list under&nbsp;GameMode Override:<\/li><\/ol>\n\n\n\n<figure class=\"wp-block-image\"><img decoding=\"async\" src=\"http:\/\/blog.coolcoding.cn\/wp-content\/uploads\/2021\/01\/5ff91259-600d-482f-b002-6497a6fae2ee.jpg\" alt=\"\"\/><\/figure>\n\n\n\n<ol><li>Play and verify that your custom HUD is drawing to the 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\/a15e2c53-27d0-40fe-bd70-09c9464d1827.png\" alt=\"\"\/><\/figure>\n\n\n\n<h1 class=\"wp-block-heading\">How it works&#8230;<\/h1>\n\n\n\n<p>All the UI recipes here will be using Slate for drawing, so we need to add a dependency between our module and the Slate framework so that we can access the classes that have been declared in that module.&nbsp;The best place to put custom Canvas draw calls for a game HUD is inside a subclass of&nbsp;AHUD.<\/p>\n\n\n\n<p>To tell the engine to use our custom subclass, though, we need to create a new&nbsp;GameMode&nbsp;and specify the type of our custom class.<\/p>\n\n\n\n<p>Within the constructor of our custom Game Mode, we assign the&nbsp;UClass&nbsp;for our new HUD type to the&nbsp;HUDClass&nbsp;variable. This&nbsp;UClass&nbsp;is passed on to each&nbsp;player&#8217;s&nbsp;controller as they spawn in, and the controller is then responsible for the&nbsp;AHUD&nbsp;instance that it creates.<\/p>\n\n\n\n<p>With our custom&nbsp;GameMode&nbsp;loading our custom HUD, we need to actually create said custom HUD class.&nbsp;AHUD&nbsp;defines a virtual function called&nbsp;DrawHUD(), which is invoked in every frame to allow us to draw elements to the screen.&nbsp;As a result, we override that function and perform our drawing inside the implementation.<\/p>\n\n\n\n<p>The first method that&#8217;s used is as follows:<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">float DrawText(constUFont* InFont, constFString&amp;InText, <br> float X, float Y, float XScale = 1.f, float YScale = 1.f, <br> constFFontRenderInfo&amp;RenderInfo = FFontRenderInfo());<\/pre>\n\n\n\n<p>DrawText&nbsp;requires a font to draw with. The default font used by&nbsp;stat&nbsp;and other HUD drawing commands in the engine code is actually stored in the&nbsp;GEngine&nbsp;class, and can be accessed by using the&nbsp;GetSmallFont&nbsp;function, which returns an instance of the&nbsp;UFont&nbsp;as a pointer.<\/p>\n\n\n\n<p>The remaining arguments that we are using are the actual text that should be rendered, and the offset, in pixels, at which the text should be drawn.<\/p>\n\n\n\n<p>DrawText&nbsp;is a function that allows you to directly pass in the data that is to be displayed.&nbsp;The general&nbsp;DrawItem&nbsp;function is a Visitor implementation that allows you to create an object that encapsulates the information about the object to be drawn and reuse that object on multiple draw calls.<\/p>\n\n\n\n<p>In this recipe, we create an element that can be used to represent a progress bar. We encapsulate the required information regarding the width and height of our box into an&nbsp;FCanvasBoxItem, which we then pass to the&nbsp;DrawItem&nbsp;function on our Canvas.<\/p>\n\n\n\n<p>The third item that we draw is a filled rectangle. This function uses convenience methods that are defined in the HUD class rather than on the Canvas itself. The filled rectangle is placed at the same location as our&nbsp;FCanvasBox&nbsp;so that it can represent the current value inside the progress bar.<\/p>\n\n\n\n<h1 class=\"wp-block-heading\">See also&#8230;<\/h1>\n\n\n\n<ul><li>Refer to&nbsp;<a href=\"https:\/\/learning.oreilly.com\/library\/view\/unreal-engine-4x\/9781789809503\/c73bb4ba-4b7d-4aef-a33e-732f184d261f.xhtml\">Chapter 10<\/a>,&nbsp;<em>Integrating C++ and the Unreal Editor&nbsp;\u2013 Part II,<\/em>&nbsp;and the&nbsp;<em>Creating new console commands<\/em>&nbsp;recipe within, to learn how to create your own console commands<\/li><\/ul>\n\n\n\n<h1 class=\"wp-block-heading\">Adding Slate Widgets to the screen<\/h1>\n\n\n\n<p>The previous recipe used the&nbsp;FCanvas&nbsp;API to draw to the screen. However,&nbsp;FCanvas&nbsp;suffers from a number of limitations, for example, animations are difficult to implement, and drawing graphics on the screen involves creating textures or materials.&nbsp;FCanvas&nbsp;also doesn&#8217;t implement anything in the way of widgets or window controls, making data entry or other forms of user input more complex than necessary. This recipe will show you how to begin creating HUD elements on-screen using Slate, which provides a number of built-in controls<\/p>\n\n\n\n<h1 class=\"wp-block-heading\">Getting ready<\/h1>\n\n\n\n<p>Add&nbsp;Slate&nbsp;and&nbsp;SlateCore&nbsp;to your module&#8217;s dependencies if you haven&#8217;t done so already (see the&nbsp;<em>Drawing using Canvas<\/em>&nbsp;recipe to learn how to do this).<\/p>\n\n\n\n<h1 class=\"wp-block-heading\">How to do it&#8230;<\/h1>\n\n\n\n<ol><li>Create a new&nbsp;PlayerController&nbsp;subclass using the&nbsp;Add C++ Class&nbsp;wizard:<\/li><\/ol>\n\n\n\n<figure class=\"wp-block-image\"><img decoding=\"async\" src=\"http:\/\/blog.coolcoding.cn\/wp-content\/uploads\/2021\/01\/7c52bf2e-a859-4474-8c1d-9cd54069694e.png\" alt=\"\"\/><\/figure>\n\n\n\n<ol><li>When asked for the name of the class, type in&nbsp;CustomHUDPlayerController&nbsp;and press the&nbsp;Create Class&nbsp;button:<\/li><\/ol>\n\n\n\n<figure class=\"wp-block-image\"><img decoding=\"async\" src=\"http:\/\/blog.coolcoding.cn\/wp-content\/uploads\/2021\/01\/1ad35366-16d0-413a-bbb0-a5635ead359a.png\" alt=\"\"\/><\/figure>\n\n\n\n<ol><li>Override the&nbsp;BeginPlay&nbsp;virtual method within your new subclass:<\/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 \"CustomHUDPlayerController.generated.h\"<br><br>\/**<br> * <br> *\/<br>UCLASS()<br>class CHAPTER_14_API ACustomHUDPlayerController : 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 for your overridden&nbsp;BeginPlay()&nbsp;virtual method inside the subclass&#8217; implementation:<\/li><\/ol>\n\n\n\n<pre class=\"wp-block-preformatted\">#include \"CustomHUDPlayerController.h\"<br><strong>#include \"SlateBasics.h\"<\/strong><br><br><br><strong>void ACustomHUDPlayerController::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><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 class based on&nbsp;GameModeBase&nbsp;called&nbsp;SlateHUDGameMode.<\/li><li>Add a constructor inside the Game Mode:<\/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 \"SlateHUDGameMode.generated.h\"<br><br>\/**<br> * <br> *\/<br>UCLASS()<br>class CHAPTER_14_API ASlateHUDGameMode : public AGameModeBase<br>{<br>    GENERATED_BODY()<br>    <br><strong>    ASlateHUDGameMode();<\/strong><br>};<\/pre>\n\n\n\n<ol><li>Implement the constructor with the following code:<\/li><\/ol>\n\n\n\n<pre class=\"wp-block-preformatted\">#include \"SlateHUDGameMode.h\"<br><strong>#include \"CustomHUDPlayerController.h\"<\/strong><br><br><strong>ASlateHUDGameMode::ASlateHUDGameMode() : Super()<\/strong><br><strong>{<\/strong><br><strong> PlayerControllerClass = ACustomHUDPlayerController::StaticClass();<\/strong><br><strong>}<\/strong><\/pre>\n\n\n\n<ol><li>Within the Editor, open the&nbsp;World Settings&nbsp;menu from the toolbar by going to&nbsp;Settings | World Settings:<\/li><\/ol>\n\n\n\n<figure class=\"wp-block-image\"><img decoding=\"async\" src=\"http:\/\/blog.coolcoding.cn\/wp-content\/uploads\/2021\/01\/826cba58-111f-40cf-8ff5-4d6c56bf5a45.jpg\" alt=\"\"\/><\/figure>\n\n\n\n<ol><li>Inside&nbsp;World Settings, override the level&#8217;s&nbsp;Game Mode&nbsp;to be our&nbsp;SlateHUDGameMode:<\/li><\/ol>\n\n\n\n<figure class=\"wp-block-image\"><img decoding=\"async\" src=\"http:\/\/blog.coolcoding.cn\/wp-content\/uploads\/2021\/01\/9f73bfe4-1610-4d0c-b44b-b14e786dbbc7.jpg\" alt=\"\"\/><\/figure>\n\n\n\n<ol><li>Play the level. You will see your new UI displayed on the 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\/2347b898-a48c-495d-84ce-d06b195e3021.png\" alt=\"\"\/><\/figure>\n\n\n\n<p>Button located on the game screen<\/p>\n\n\n\n<ul><li><a href=\"https:\/\/learning.oreilly.com\/library\/view\/unreal-engine-4x\/9781789809503\/9b5848b2-8faf-4cb8-99ec-6b4f424c537b.xhtml#\">Copy<\/a><\/li><li><a href=\"https:\/\/learning.oreilly.com\/library\/view\/unreal-engine-4x\/9781789809503\/9b5848b2-8faf-4cb8-99ec-6b4f424c537b.xhtml#\">Add Highlight<\/a><\/li><li><a href=\"https:\/\/learning.oreilly.com\/library\/view\/unreal-engine-4x\/9781789809503\/9b5848b2-8faf-4cb8-99ec-6b4f424c537b.xhtml#\">Add Note<\/a><\/li><\/ul>\n\n\n\n<h1 class=\"wp-block-heading\">How it works&#8230;<\/h1>\n\n\n\n<p>For us to reference Slate classes or functions in our code, our module must link with the&nbsp;Slate&nbsp;and&nbsp;SlateCore&nbsp;modules, so we add those to the module dependencies.<\/p>\n\n\n\n<p>We need to instantiate our UI in one of the classes that loads when the game runs, so for this recipe, we use our custom&nbsp;PlayerController&nbsp;in the&nbsp;BeginPlay&nbsp;function as the place to create our UI.<\/p>\n\n\n\n<p>Inside the&nbsp;BeginPlay&nbsp;implementation, we create a new&nbsp;SVerticalBox&nbsp;using the&nbsp;SNew&nbsp;function. We add a slot for a widget to our box, and set that slot to both horizontal and vertical centering.<\/p>\n\n\n\n<p>Inside the slot, which we access using square brackets, we create a button that has&nbsp;Textblock&nbsp;inside it. In&nbsp;Textblock, we set the&nbsp;Text&nbsp;property to a string literal value.<\/p>\n\n\n\n<p>With the UI now created, we call&nbsp;AddViewportWidgetForPlayer&nbsp;to display this widget on the local player&#8217;s screen.<\/p>\n\n\n\n<p>With our custom&nbsp;PlayerController&nbsp;ready, we now need to create a custom&nbsp;GameMode&nbsp;to specify that it should use our new&nbsp;PlayerController. With the custom&nbsp;PlayerController&nbsp;being loaded at the start of the game, when&nbsp;BeginPlay&nbsp;is called, our UI will be shown.<\/p>\n\n\n\n<p>The UI is very small at this screen size. Refer to the following recipe for information on how to scale it appropriately for the resolution of the game window.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>In this chapter, we will cover the following recipes: D [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"closed","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[27,1],"tags":[],"_links":{"self":[{"href":"https:\/\/blog.coolcoding.cn\/index.php?rest_route=\/wp\/v2\/posts\/3226"}],"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=3226"}],"version-history":[{"count":2,"href":"https:\/\/blog.coolcoding.cn\/index.php?rest_route=\/wp\/v2\/posts\/3226\/revisions"}],"predecessor-version":[{"id":3295,"href":"https:\/\/blog.coolcoding.cn\/index.php?rest_route=\/wp\/v2\/posts\/3226\/revisions\/3295"}],"wp:attachment":[{"href":"https:\/\/blog.coolcoding.cn\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=3226"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/blog.coolcoding.cn\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=3226"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/blog.coolcoding.cn\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=3226"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}