使用UMG的用户界面

使用UMG创建简易的菜单系统

Windows
MacOS
Linux

在本教程中,可学习使用虚幻示意图形(UMG)构建具有多屏幕和按钮的基本菜单系统。

1.设置UMG的模块依赖性

如首次使用 虚幻引擎4,建议先阅读编程快速入门 tutorial 。本教程将假设您已熟悉项目的创建、向其中添加C++代码,以及编译代码。同时也会向 蓝图 公开函数和属性。如欲了解更多信息,可从变量、定时器和事件 tutorial 入手。

  1. 将从新建项目开始:

    • 点击新项目中的 游戏 分类,然后点击 下一步

    • 选择一个空白模板并点击 下一步

    • 确认启用了 C++台式机/主机使用初学者内容包(With Starter Content) 设置。

    • 将项目命名为"HowTo_UMG"。

    由于要编写使用 虚幻运动图形(UMG) 的代码,因此需进入 Visual Studio,获取部分默认不可用的模块。

  2. 使用主编辑器界面上 文件 下拉菜单中的 打开Visual Studio 命令来获取项目代码。

    OpenVisualStudio.png

  3. UMG依赖于模块,因此需将此类模块添加至HowTo_UMG.Build.cs中。

    BuildCS.png

    在HowTo_UMG.Build.cs中,需将UMG添加到包含的公共模块列表中,并将Slate和SlateCore添加到包含的私有模块列表中。需对HowTo_UMG的构造函数首行进行如下修改:

    PublicDependencyModuleNames.AddRange(new string[] { "Core", "CoreUObject", "Engine", "InputCore", "UMG" });

    然后将以下行取消注释:

    PrivateDependencyModuleNames.AddRange(new string[] { "Slate", "SlateCore" });

    完整源代码提供如下,以便仔细核对代码。


设置UMG后,可在项目的自定义 游戏模式 中添加代码,以创建和显示游戏菜单。

半成品代码

HowTo_UMG.Build.cs

// Copyright 1998-2018 Epic Games, Inc. All Rights Reserved.

using UnrealBuildTool;

public class HowTo_UMG :ModuleRules
{
    public HowTo_UMG(TargetInfo Target)
    {
        PublicDependencyModuleNames.AddRange(new string[] { "Core", "CoreUObject", "Engine", "InputCore", "UMG" });

        //PrivateDependencyModuleNames.AddRange(new string[] {  });

        // 如使用Slate UI,则取消注释
        PrivateDependencyModuleNames.AddRange(new string[] { "Slate", "SlateCore" });

        // 如使用在线功能,则取消注释
        // PrivateDependencyModuleNames.Add("OnlineSubsystem");
        // if ((Target.Platform == UnrealTargetPlatform.Win32) || (Target.Platform == UnrealTargetPlatform.Win64))
        // {
        //      if (UEBuildConfiguration.bCompileSteamOSS == true)
        //      {
        //          DynamicallyLoadedModuleNames.Add("OnlineSubsystemSteam");
        //      }
        // }
    }
}

2.扩展游戏模式

  1. 创建的菜单将由 用户控件 组成。需编写函数以新建并显示用户控件,然后在游戏开始时调用该函数。同时还需追踪创建内容,以便之后进行删除。各项目已有自定义 游戏模式 类,可直接打开,其在HowToUMGGameMode.h中被定义。需将以下函数和属性添加到类的底部:

    public:
        /** 移除当前菜单控件,并在指定类(如有)中新建控件。*/
        UFUNCTION(BlueprintCallable, Category = "UMG Game")
        void ChangeMenuWidget(TSubclassOf<UUserWidget> NewWidgetClass);
    
    protected:
        /** 游戏开始时调用。*/
        virtual void BeginPlay() override;
    
        /** 游戏开始时,用作菜单的控件类。*/
        UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "UMG Game")
        TSubclassOf<UUserWidget> StartingWidgetClass;
    
        /** 用作菜单的控件实例。*/
        UPROPERTY()
        UUserWidget* CurrentWidget;
  2. 为在代码中使用用户控件,需在#include部分的顶部添加以下行:

    #include "Blueprint/UserWidget.h"
  3. 现在开始处理HowTo_UMGGameMode.cpp,需填充声明的两个函数的主体。将从覆盖 BeginPlay() 开始:

    void AHowTo_UMGGameMode::BeginPlay()
    {
        Super::BeginPlay();
        ChangeMenuWidget(StartingWidgetClass);
    }

    覆盖父类(由单词Super引用)中的函数时,正如现在覆盖BeginPlay,务必调用该函数的父类版本。由于函数版本仅意图在现有过程的最后增加步骤,因此在该函数首行中调用 Super::BeginPlay

  4. 接下来,同样在HowTo_UMGGameMode.cpp中,需定义菜单间的变化。如视口中存在活跃的用户控件,需将其移除。然后可新建用户控件,并将其添加到视口中。

    void AHowTo_UMGGameMode::ChangeMenuWidget(TSubclassOf<UUserWidget> NewWidgetClass)
    {
        if (CurrentWidget != nullptr)
        {
            CurrentWidget->RemoveFromViewport();
            CurrentWidget = nullptr;
        }
        if (NewWidgetClass != nullptr)
        {
            CurrentWidget = CreateWidget<UUserWidget>(GetWorld(), NewWidgetClass);
            if (CurrentWidget != nullptr)
            {
                CurrentWidget->AddToViewport();
            }
        }
    }

    此代码将创建所提供 控件 的实例,并将其放置在屏幕上。该代码同样可将其移除,以便每次仅有一个活跃控件,尽管 虚幻引擎 可同时处理显示多个控件并与之交互。由于在视窗中将其移除和清除(或修改)引用其的所有变量,都将导致虚幻引擎的垃圾回收系统将其清除,因此无需直接销毁控件。

  5. 最后,需要在 玩家控制器(Player Controller) 类上设置输入模式。为此,在项目中添加基于 玩家控制器 的新C++类。游戏开始时,只需在此类中调用一个额外函数,便能确保与UI元素进行交互。

    NewClass.png PlayerController.png

    在HowTo_UMGPlayerControlle.h中,将向该类添加以下覆盖:

    public:
        virtual void BeginPlay() override;

    在HowToUMGPlayerController.cpp中,将添加被覆盖的函数:

    void AHowTo_UMGPlayerController::BeginPlay()
    {
        Super::BeginPlay();
        SetInputMode(FInputModeGameAndUI());
    }

代码框架已构建完毕,以创建和显示菜单并在无需要时将其删除。现在返回 虚幻编辑器,开始设计菜单资源吧!

成品代码

HowTo_UMG.Build.cs

// Copyright 1998-2018 Epic Games, Inc. All Rights Reserved.

using UnrealBuildTool;

public class HowTo_UMG :ModuleRules
{
    public HowTo_UMG(TargetInfo Target)
    {
        PublicDependencyModuleNames.AddRange(new string[] { "Core", "CoreUObject", "Engine", "InputCore", "UMG" });

        //PrivateDependencyModuleNames.AddRange(new string[] {  });

        // 如使用Slate UI,则取消注释
        PrivateDependencyModuleNames.AddRange(new string[] { "Slate", "SlateCore" });

        // 如使用在线功能,则取消注释
        // PrivateDependencyModuleNames.Add("OnlineSubsystem");
        // if ((Target.Platform == UnrealTargetPlatform.Win32) || (Target.Platform == UnrealTargetPlatform.Win64))
        // {
        //      if (UEBuildConfiguration.bCompileSteamOSS == true)
        //      {
        //          DynamicallyLoadedModuleNames.Add("OnlineSubsystemSteam");
        //      }
        // }
    }
}

HowTo_UMGGameMode.h

// Copyright 1998-2018 Epic Games, Inc. All Rights Reserved.

#pragma once

#include "Blueprint/UserWidget.h"
#include "GameFramework/GameModeBase.h"
#include "HowTo_UMGGameMode.generated.h"

/**
    * 
    */
UCLASS()
class HOWTO_UMG_API AHowTo_UMGGameMode : public AGameModeBase
{
    GENERATED_BODY()

public:
    /** 移除当前菜单控件,并在指定类(如有)中新建控件。*/
    UFUNCTION(BlueprintCallable, Category = "UMG Game")
    void ChangeMenuWidget(TSubclassOf<UUserWidget> NewWidgetClass);

protected:
    /** 游戏开始时调用。*/
    virtual void BeginPlay() override;

    /** 游戏开始时,用作菜单的控件类。*/
    UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "UMG Game")
    TSubclassOf<UUserWidget> StartingWidgetClass;

    /** 用作菜单的控件实例。*/
    UPROPERTY()
    UUserWidget* CurrentWidget;
};

HowTo_UMGGameMode.cpp

// Copyright 1998-2018 Epic Games, Inc. All Rights Reserved.

#include "HowTo_UMG.h"
#include "HowTo_UMGGameMode.h"

void AHowTo_UMGGameMode::BeginPlay()
{
    Super::BeginPlay();
    ChangeMenuWidget(StartingWidgetClass);
}

void AHowTo_UMGGameMode::ChangeMenuWidget(TSubclassOf<UUserWidget> NewWidgetClass)
{
    if (CurrentWidget != nullptr)
    {
        CurrentWidget->RemoveFromViewport();
        CurrentWidget = nullptr;
    }
    if (NewWidgetClass != nullptr)
    {
        CurrentWidget = CreateWidget<UUserWidget>(GetWorld(), NewWidgetClass);
        if (CurrentWidget != nullptr)
        {
            CurrentWidget->AddToViewport();
        }
    }
}

HowTo_UMGPlayerController.h

// Copyright 1998-2018 Epic Games, Inc. All Rights Reserved.

#pragma once

#include "GameFramework/PlayerController.h"
#include "HowTo_UMGPlayerController.generated.h"

/**
    * 
    */
UCLASS()
class HOWTO_UMG_API AHowTo_UMGPlayerController : public APlayerController
{
    GENERATED_BODY()

public:
    virtual void BeginPlay() override;
};

HowTo_UMGPlayerController.cpp

// Copyright 1998-2018 Epic Games, Inc. All Rights Reserved.

#include "HowTo_UMG.h"
#include "HowTo_UMGPlayerController.h"

void AHowTo_UMGPlayerController::BeginPlay()
{
    Super::BeginPlay();
    SetInputMode(FInputModeGameAndUI());
}

3.创建菜单控件蓝图

  1. 虚幻编辑器 中,可按 编译 按钮构建新代码。利用此操作可将 用户控件 用作菜单。

    CompileFromEditor.png

  2. 现在创建被 游戏模式 用作菜单的用户控件。通过 内容浏览器 中的 新增(Add New) 按钮,即可完成此操作。控件蓝图 类位于 用户界面 类别中。需创建两个控件,并分别命名为MainMenu和NewGameMenu。可主菜单开始游戏,同时拥有可进入新游戏菜单的选项。

    CreateWidgetBP.png

  3. 双击刚刚创建的主菜单 控件,前往 蓝图设计器,可在其中创建菜单布局。

  4. 按钮文本控制板面板常用 部分拖入 图表。该按钮将被用于打开新游戏菜单。

    AddButtonAndTextBlock.png

  5. 创建正确布局的首部,便是调整按钮的位置和大小。应进行以下修改:

    • 将大小设为 -200 x -200。

    • 将位置设为(-200,-200)。

    • 将其重命名为NewGameButton,以便之后将其与功能连接时进行识别。

    ButtonDetails.png

  6. 由于未绘制一套按钮的自定义图像,因此可通过将文本块拖放到按钮上并进行以下修改,以标记按钮:

    • 设置"新游戏"的文本显示。

    • 将可见性改为"点击测试不可见"。此操作可防止文本块阻碍鼠标点击下方按钮。

    • 将命名设为NewGameText。此并非必要步骤,而是好习惯。

    TextBlockDetails.png

  7. 接下来,将使用第二个按钮和文本块,创建"退出"功能。其设置方法与新游戏按钮和文本块的设置方法相同,但有以下变化。

    • 将按钮命名设为QuitButton

    • 将按钮的位置设为(-200,-400)

    • 将文本块命名设为QuitText

  8. 完成后即可将 事件 添加到按钮,以便点击按钮时运行代码。通过在 细节面板 中找到并按下相应事件命名旁的+号,便能完成此操作。而现在,OnClicked 即是要使用的事件。同时创建NewGameButton和QuitButton控件的的该事件。

    CreateOnClickedEvent.png

    设计师可在此使用 蓝图 脚本构建功能,或C++程序员可连接调用公开函数的节点。

  9. 对于名为 OnClicked(NewGameButton) 的事件,需:

    • 连接 ChangeMenuWidget 节点,以使用之前添加到GameMode中的功能。

    • 将 ChangeMenuWidget 节点上的 新控件类** 字段设为NewGameMenu资源。

    OnClicked_NewGame.png

  10. 对于名为 OnClicked(QuitButton) 的事件,需:

    • 连接 Quit Game 节点。

    OnClicked_Quit.png

构建主菜单后,可设置在关卡启动时立即加载主菜单的GameMode资源。

4.配置游戏模式

  1. 基于项目的 GameMode,在 内容浏览器 中添加 蓝图类.利用此操作,可将这两个类上的公开变量设为需要的值。为此,需:

    • 内容浏览器 中点击的 添加 按钮。

    AddNewBPClass.png

    • 选择 HowTo_UMGGameMode 作为父类。其将被列入 所有类(All Classes) 部分。

    PickParentClassForGameModeBP.png

    • 将得到的蓝图资源命名为MenuGameMode。

  2. 为使游戏光标出现在游戏中,需进行GameMode中的相同操作,创建 PlayerController 的蓝图。

    • 内容浏览器 中再次点击 添加 按钮。

    • 常用类(Common Classes) 部分选择 玩家控制器

    • 将该蓝图命名为MenuPlayerController。

  3. 编辑MenuPlayerController。

    • 勾选 显示鼠标光标(Show Mouse Cursor) 复选框。

    GamePlayerController.png

  4. 编辑MenuGameMode。

    • 须将 启动控件类(Starting Widget Class) 设为MainMenu资源,以便在游戏开始时打开菜单。

    • 应将 默认Pawn类(Default Pawn Class) 设为 Pawn 而非 DefaultPawn,玩家便不会在菜单中乱飞。

    • 应将 玩家控制器类 设为创建的MenuPlayerController资源,以便在游戏中显示鼠标光标。

    ConfigureGameMode.png

  5. 为使用蓝图,必返回 关卡编辑器 窗口,并通过 设置 按钮修改当前 关卡场景设置

    WorldSettingsBar.png

    也可在 地图和模式 部分的 项目设置 菜单中,设置默认的游戏模式。如使用此方法,除非进行单独覆盖,所有关卡都将默认使用选定的GameMode。将基于项目设置中的偏好决定使用的方法。

  6. 将打开 场景设置面板。其将默认与 细节面板 停靠,但也可将其移动到其他位置。需将 游戏模式覆盖(Game Mode Override) 字段设为MenuGameMode资源。

    WorldSettings.png

自定义GameMode资源现已在关卡上生效,并被配置以加载主菜单,同时使用显示鼠标光标的玩家控制器。如现在运行游戏,退出按钮将预期生效,使用新游戏按钮将进入空白菜单。将在下一步中设置新游戏菜单。

5.构建二级菜单

  1. 内容浏览器 中,找到并打开之前创建的NewGameMenu资源。该菜单将包含可输入命名的 文本框、开始游戏的 按钮(输入命名前无法按下),及可返回主菜单的按钮。

  2. 为创建命名输入框,需将文本框(而非 文本块)拖入布局。

    CreateTextEntryBox.png

  3. 应按以下值配置文本框:

    • 将命名改为NameTextEntry

    • 位置(325,200)。此操作可在文本框左侧预留空间,以放置文本块。

    • 尺寸为250x40。

    • 字体大小(在"样式"标头下)为20。

    TextBoxDetails.png

  4. 利用与在上一菜单中创建按钮的相同方式,即可创建带文本块标签的游戏按钮。

    • 对于按钮:将命名改为PlayGameButton,位置 改为(200,300),尺寸 改为(200,100)

    • 对于文本块:将命名改为PlayGameText,将 可见性 设为"命中测试不可见",并将其拖到PlayGameButton的顶部。

  5. "开始游戏"按钮将有一个特殊功能:仅在文本框中输入非空白命名时启用。可使用 虚幻运动图形(UMG)绑定功能新建 已启用(Is Enabled) 字段(在 行为(Behavior) 部分下)的功能。

    PlayGameButtonDetails.png

    如游戏中具有决定有效玩家名称构成的复杂规则,或需将名字保存到C++变量中时,可能需在 GameMode 上公开 UFUNCTION 或在项目中的某处公开静态函数。但由于仅需名称字符串不为空,因此可在 控件 中的此处编写其脚本。

  6. 为确保仅在文本框非空白时启用按钮,可将文本框中的文本转换为字符串,然后检查其长度是否大于零。以下为此逻辑的显示方式:

    PlayGameButtonEnableFunction.png

  7. 再添加一个按钮,以便退出并从此处进入主菜单。添加方式与添加主菜单的"开始游戏"按钮相同,但其位置位于右下角而非左上角。为了完成此操作,点击 细节面板 中的 下拉列表,并在弹出菜单中找到适当的图示。

    • 将命名改为MainMenuButton

    • 位置 设为(-400,-200)

    • 尺寸 设为200x100

    SelectAnchor.png

    将锚放在右下角,此操作不会改变其大小和位置值的工作方式,因此需将位置值设为负值,使其在屏幕中显示。保留尺寸值为正。

  8. 现在再次添加 OnClicked 事件,向新按钮添加脚本。主菜单按钮将简单重加载主菜单控件,在 ChangeMenuWidget 函数的调用中不提供新控件,开始游戏按钮将停止菜单。显示短语 选择类 而非实际类或资产的命名,可对此进行显示。

    NewGameButtonBPs.png

    开始游戏按钮停止菜单后,将无法在游戏中进行任何操作。通常会在此处加载首个关卡、播放开场动画,或生成并拥有 Pawn

  9. 现在应该具备两种屏幕,大致如下:

    FinalScreen.png

    FinalScreen2.png

6.自行尝试!

利用所学内容,尝试以下操作:

  • 同时打开的多个菜单。

  • 在屏幕上滑入或淡入 控件,而非直接显示。

  • 查看控件支持的不同声音、样式和颜色属性。

  • 在菜单中设置 **GameMode 内如难度、角色级别等变量。

  • 创建游戏内菜单,可在玩家按键时弹出并暂停游戏,而在关闭后可继续游戏。

  • 将之前教程中构建或学习的功能关联到在 虚幻示意图形(UMG)中构建的菜单。

本教程中讲解的相关细节:

Select Skin
Light
Dark

Welcome to the new Unreal Engine 4 Documentation site!

We're working on lots of new features including a feedback system so you can tell us how we are doing. It's not quite ready for use in the wild yet, so head over to the Documentation Feedback forum to tell us about this page or call out any issues you are encountering in the meantime.

We'll be sure to let you know when the new system is up and running.

Post Feedback