5. 建立第二菜单

  1. Content Browser(内容浏览器) 中,搜寻并打开我们之前创建的NewGameMenu资源。 这个菜单包含了一个名称条目 Text Box(文本框) ,只有在输入名称后才能通过按下 Button(按钮) 来游玩游戏,还有一个可供点击以返回主菜单的Button(按钮)。

  2. 如需创建名称条目框,我们可以拖曳Text Box(文本框)(而不是 Text Block(文本块) )到布局中。

    CreateTextEntryBox.png

  3. Text Box(文本框)应使用以下值进行配置:

    • 将名字改成NameTextEntry

    • 位置是(325, 200)。 这样可以留出空间以把 Text Block(文本块) 放置于Text Box(文本框)的左侧。

    • 大小是250x40。

    • 字体大小(位于“风格”标题栏下)是20。

    TextBoxDetails.png

  4. 我们可以创建具有Text Block(文本块)标记的Play Game Button(玩游戏按钮),按照我们在上一菜单中创建Button(按钮)的相同方式进行标记。

    • 对按钮来说:将名称变更为PlayGameButton, 位置 变更为200, 300, 大小 变更为200, 100

    • 对于文本块来说: 变更名称为PlayGameText,设置 可见度 为Hit Test Visible,然后将其拖曳到PlayGameButton的顶部位置

  5. Play Game(玩游戏)的Button(按钮)有一个特殊功能 - 它只有在输入Text Box(文本框)的名称为非空时,才会被启用。 我们可以使用 Unreal Motion Graphics'(虚幻运动图形) (UMG)的bind(绑定)功能来为"Is Enabled"域创建新函数(在 操作 部分下方)。

    PlayGameButtonDetails.png

    如果用于确定组成游戏中有效玩家名称的规则很复杂,或者,如果我们需要保存名称到C++变量中,我们可以在 Game Mode(游戏模式) 中显示 UFUNCTION ,或者在我们的项目中的某个位置将其作为静态函数来处理。 但是,因为我们只关心名称字符串是否为非空,我们可以直接在 Widget(控件) 中对其进行脚本处理。

  6. 为确保仅在Text Box(文本框)非空的情况下启用Button(按钮),我们可以转换来自于Text Box(文本框)的文本到字符串中,然后检查其长度是否大于0。 以下是整个逻辑:

    PlayGameButtonEnableFunction.png

  7. 让我们再添加一个Button(按钮),这样我们可以返回并从此处转到主菜单。 它就和主菜单的玩游戏的Button(按钮)一样,但它被放置于靠近右下角而不是左上角的位置。 为完成这个目标,对于Button(按钮),您可以点击 Details Panel(详细信息面板)Anchors(菜单锚点) 下拉框,然后在弹出菜单中找到合适的图表内容。

    • 将名字改成MainMenuButton

    • 设置 位置 为 -400, -200。

    • 设置 大小 为200x100。

    SelectAnchor.png

    把菜单锚点放置在右下角不会变更大小和位置值,因此我们需要让位置值为负值,从而使之出现在屏幕上。 大小值保持为正值。

  8. 现在我们会通过再次添加 OnClicked 事件来添加脚本到新建Buttons(按钮)中。 主菜单的Buttons(按钮)会直接载入主菜单的Widget(控件),而玩游戏的Buttons(按钮)会因为没有在对 ChangeMenuWidget 函数的调用中提供新的Widget(控件)而完全取消激活菜单。 这是通过显示的 Select Class"(选择类) 来展示出来的,而不是通过实际类或资源的名称来展示的。

    NewGameButtonBPs.png

    在使用玩游戏Buttons(按钮)来取消激活菜单后,我们将无法继续在游戏中执行任何操作。 这个时候我们一般就载入第一个关卡,播放一下介绍性的过场动画、或者生成并放置 Pawn


Finished Code

HowTo_UMG.Build.cs

// Copyright 1998-2017 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-2017 Epic Games, Inc. All Rights Reserved.

#pragma once

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

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

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

    /** 移除当前菜单控件并且如果可能,从指定类中创建新控件。 */
    UFUNCTION(BlueprintCallable, Category = "UMG Game")
    void ChangeMenuWidget(TSubclassOf<UUserWidget> NewWidgetClass);

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

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

HowTo_UMGGameMode.cpp

// Copyright 1998-2017 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();
        }
    }
}