Unreal Engine 4 C++ Slate 介绍——用C++和Slate创建菜单(二)

来源:互联网 发布:斗鱼叶子淘宝 编辑:程序博客网 时间:2024/05/08 17:14

  • Unreal Engine 4 C Slate 介绍用C和Slate创建菜单二
    • 第一步样式设置
    • 第二步加入到Game Module
    • 第三步创建一个Style类
    • 第四步定义你的风格
    • 第五步使用你的风格
    • 总结

Unreal Engine 4 C++ Slate 介绍——用C++和Slate创建菜单(二)

好记性不如烂笔头啊,还是记录一下!


欢迎来到教程的第二部分关于使用虚幻引擎4中的Slate和C++创建游戏菜单!在上一个教程中,我们使用Slate为我们的游戏创建了一个非常简单并且…普通的标题屏幕/主菜单。现在可能已经过时了,因为这种形式的菜单看起来更像我们的系统的应用程序菜单,而不是游戏应该有的华丽界面!今天,我们将通过引入样式来解决这个问题!使用样式后我们可以改变我们的通用“按钮”,变成像我们想要的样子!教程的资源可以在这下载:UIpack RPG.zip


第一步:样式设置

首先要做的事是设置样式集和,它将用于加载和引用我们的样式。样式本身会在虚幻编辑器中指定,使我们能够对设计进行大量更改,而无需重新编译代码(我们目前必须为样式和布局更改都进行编译)。我们设置的Style Set,需要调用MenuStyles,它是一个纯静态类。

  • MenuStyles.h
// Copyright 1998-2015 Epic Games, Inc. All Rights Reserved.// MenuStyles.h - Provides our Style Set and allows us to load and reference UI Styles specified in-editor. #pragma once#include "SlateBasics.h"class FMenuStyles{public:    // Initializes the value of MenuStyleInstance and registers it with the Slate Style Registry.    static void Initialize();    // Unregisters the Slate Style Set and then resets the MenuStyleInstance pointer.    static void Shutdown();    // Retrieves a reference to the Slate Style pointed to by MenuStyleInstance.    static const class ISlateStyle& Get();    // Retrieves the name of the Style Set.    static FName GetStyleSetName();private:    // Creates the Style Set.    static TSharedRef<class FSlateStyleSet> Create();     // Singleton instance used for our Style Set.    static TSharedPtr<class FSlateStyleSet> MenuStyleInstance;};

这些方法注释已经阐明的很清楚了,看名字也大概知道作用。后面我们将在我们的Game Module中调用Initialize()Shutdown()方法。Get()方法会在以后我们需要加载特定样式集时使用。GetStyleSetName()用于引擎检索我们的样式集名称。这些方法的实现同样很简单:

  • MenuStyles.cpp
// Copyright 1998-2015 Epic Games, Inc. All Rights Reserved.#include "SlateTutorials.h"#include "MenuStyles.h"#include "SlateGameResources.h" TSharedPtr<FSlateStyleSet> FMenuStyles::MenuStyleInstance = NULL;void FMenuStyles::Initialize(){    if (!MenuStyleInstance.IsValid())    {        MenuStyleInstance = Create();        FSlateStyleRegistry::RegisterSlateStyle(*MenuStyleInstance);    }}void FMenuStyles::Shutdown(){    FSlateStyleRegistry::UnRegisterSlateStyle(*MenuStyleInstance);    ensure(MenuStyleInstance.IsUnique());     MenuStyleInstance.Reset();}FName FMenuStyles::GetStyleSetName(){    static FName StyleSetName(TEXT("MenuStyles"));    return StyleSetName;}TSharedRef<FSlateStyleSet> FMenuStyles::Create(){    TSharedRef<FSlateStyleSet> StyleRef = FSlateGameResources::New(FMenuStyles::GetStyleSetName(), "/Game/UI/Styles", "/Game/UI/Styles");    return StyleRef;}const ISlateStyle& FMenuStyles::Get(){    return *MenuStyleInstance;}

Initialize()中,我们判断MenuStyleInstance(我们的单例指针)是否有效(就是不为空)。如果不是有效的,我们就实例化它,并且用SlateStyleRegistry来注册样式集。在Shutdown()中,我们做出了相反的,我们取消了注册样式集,确保我们的指针是唯一的(在这种情况下应该是唯一的),然后我们重置它(设置为空)
GetStyleSetName()中,我们只需将我们的样式的FName缓存为静态变量,并始终返回该值。 这样,我们就有一个简单的方式来获取我们的样式集单例。


第二步:加入到Game Module

如果现在编译代码,它无法正常运行。我们还从来没有调用我们的静态方法!找到你的游戏模块的源文件(在我的工程方案中是SlateTutorials.cpp),里面应该真正只有两行:一个包括你的模块的头文件和类似于以下内容:

IMPLEMENT_PRIMARY_GAME_MODULE(FDefaultGameModuleImpl,SlateTutorials,“SlateTutorials”);

注意FDefaultGameModuleImpl,这是用于你的游戏模块的类。很多时候不会在这里去处理任何其他事情 - 但我们需要绑定到游戏模块来初始化我们的样式集!我们如何做到这一点?好吧,Epic的做法(就是我们要使用的)似乎只是简单地定义模块类在这里 - 但要记住,如果你要做的比我们复杂很多,分为 header & source是一个更好的主意。

#include "SlateTutorials.h"#include "MenuStyles.h" //Custom implementation of the Default Game Module. class FSlateTutorialsGameModule : public FDefaultGameModuleImpl{    // Called whenever the module is starting up. In here, we unregister any style sets     // (which may have been added by other modules) sharing our     // style set's name, then initialize our style set.     virtual void StartupModule() override    {        //Hot reload hack        FSlateStyleRegistry::UnRegisterSlateStyle(FMenuStyles::GetStyleSetName());        FMenuStyles::Initialize();    }    // Called whenever the module is shutting down. Here, we simply tell our MenuStyles to shut down.    virtual void ShutdownModule() override    {        FMenuStyles::Shutdown();    }};IMPLEMENT_PRIMARY_GAME_MODULE(FSlateTutorialsGameModule, SlateTutorials, "SlateTutorials");

这里没什么难的,对吧? 我们只是定义一个自定义的模块类,只是扩展我们以前的,并添加一些必要的调用来初始化和关闭我们的游戏模块。 注意,我们花时间去取消注册 - 只是为了防止任何其他模块引入一个相同的名称(老实说,我…不知道这是多么必要。但在Strategy示例中是这么使用的,我想应该会有更好的办法)。


第三步:创建一个Style类

现在我们有了样式集,让我们继续创建一个类,我们可以使用它来建立和自定义我们的菜单样式。你有很多很多方法做到这些,来满足您的布局需要。我个人倾向于有一个单一的“全局”样式定义的东西,例如标准按钮样式。然后创建控件特定的样式集,如果自定义控件(比如我们的主菜单UI)只存在一个或两个空格。 那么我们该怎么做呢? 简单! 我们将创建一个新的GlobalMenuStyle类(和一个GlobalStyle结构…你很快会看到)。

  • GlobalMenuStyle.h
// Copyright 1998-2015 Epic Games, Inc. All Rights Reserved.// GlobalMenuStyle.h - Provides a global menu style! #pragma once#include "SlateWidgetStyleContainerBase.h"#include "SlateWidgetStyle.h"#include "SlateBasics.h"#include "GlobalMenuStyle.generated.h" // Provides a group of global style settings for our game menus! USTRUCT()struct FGlobalStyle : public FSlateWidgetStyle{    GENERATED_USTRUCT_BODY()    // Stores a list of Brushes we are using (we aren't using any) into OutBrushes.    virtual void GetResources(TArray<const FSlateBrush*>& OutBrushes) const override;    // Stores the TypeName for our widget style.    static const FName TypeName;    // Retrieves the type name for our global style, which will be used by our Style Set to load the right file.     virtual const FName GetTypeName() const override;    // Allows us to set default values for our various styles.     static const FGlobalStyle& GetDefault();     // Style that define the appearance of all menu buttons.     UPROPERTY(EditAnywhere, Category = Appearance)    FButtonStyle MenuButtonStyle;    // Style that defines the text on all of our menu buttons.     UPROPERTY(EditAnywhere, Category = Appearance)    FTextBlockStyle MenuButtonTextStyle;    // Style that defines the text for our menu title.     UPROPERTY(EditAnywhere, Category = Appearance)    FTextBlockStyle MenuTitleStyle;};// Provides a widget style container to allow us to edit properties in-editorUCLASS(hidecategories = Object, MinimalAPI)class UGlobalMenuStyle : public USlateWidgetStyleContainerBase{    GENERATED_UCLASS_BODY()public:    // This is our actual Style object.     UPROPERTY(EditAnywhere, Category = Appearance, meta = (ShowOnlyInnerProperties))    FGlobalStyle MenuStyle;    // Retrievs the style that this container manages.     virtual const struct FSlateWidgetStyle* const GetStyle() const override    {        return static_cast<const struct FSlateWidgetStyle*>(&MenuStyle);    }};

这一个有点长,但(像前面一样)不是很复杂。首先,我们有GetResources方法 - 如果你使用任何Slate画刷(例如,定义一个SImage窗口控件的属性),你可以在这里用OutBrushes添加这些画刷。 在我们的示例中,我们的按钮和文本块样式不是画刷,所以我们不必在这个方法中做任何事情。接下来,我们有GetTypeName方法 - 这个方法给出了类型的名称,它应该匹配实际的类型名称。 这用于引用这个控件是什么类型。 GetDefault()方法允许我们设置一些默认值 - 例如,如果我们想要的话我们可以为标题屏幕设置默认字体或大小。最后,我们有三个属性。第一和第二都是关于按钮 - 按钮(SButton控件)实际采取两种样式 - 一个用于按钮本身,一个用于表示按钮上的文本的文本块。第三个属性,然后是我们的菜单标题文本。但是,这一个结构是不够的!我们实际上有一个充当容器基础的类 - 这允许我们在编辑器中可以修改这些结构的公开属性,甚至比定义更简单的实现:

  • GlobalMenuStyle.cpp
// Copyright 1998-2015 Epic Games, Inc. All Rights Reserved.#include "SlateTutorials.h" #include "GlobalMenuStyle.h" void FGlobalStyle::GetResources(TArray<const FSlateBrush*>& OutBrushes) const{}const FName FGlobalStyle::TypeName = TEXT("FGlobalStyle");const FName FGlobalStyle::GetTypeName() const{    static const FName TypeName = TEXT("FGlobalStyle");    return TypeName;}const FGlobalStyle& FGlobalStyle::GetDefault(){    static FGlobalStyle Default;    return Default;}UGlobalMenuStyle::UGlobalMenuStyle(const FObjectInitializer& ObjectInitializer)    : Super(ObjectInitializer){}

大多数这些方法是空的 - 毕竟,我们没有任何画刷注册,我同样不会设置默认值(字面上很简单,可以在GetDefault()方法中更改您的样式的默认属性)。 只要确保你的GetTypeName()返回一个FName匹配你的样式结构的名称!

示例图片


第四步:定义你的风格

现在我们已经设置了样式,你不认为是时候去定义它们了吗?对吧!启动虚幻,在内容浏览器中创建一个名为UI的新文件夹,然后在其中创建一个名为Styles的新文件夹。 然后要创建实际的样式定义,创建一个新的Slate Widget资源(用户界面->Slate Widget Style)。 将提示您选择控件样式容器 - 选择GlobalMenuStyle,并将新资产命名为Global(如果您名为其他名称,请记住您以后使用的名称)。继续打开它,并将您的属性调整到你喜欢的东西 - 随意导入一些图像,为您的按钮使用!

示例图片


第五步:使用你的风格

我们定义了我们的风格,设置了一些漂亮的设置,但是我们如何实际使用这个!
首先,将以下成员变量添加到您的主菜单UI窗口控件:

const struct FGlobalStyle * MenuStyle;

然后,进入您的源文件,并添加两个标题包含:

#include "GlobalMenuStyle.h"#include "MenuStyles.h"
  • MainMenuUI.h
// Copyright 1998-2015 Epic Games, Inc. All Rights Reserved.#pragma once#include "SlateBasics.h"class SLATETUTORIALS_API SMainMenuUI : public SCompoundWidget{public:    SLATE_BEGIN_ARGS(SMainMenuUI)    {}    SLATE_ARGUMENT(TWeakObjectPtr<class AMainMenuHUD>, MainMenuHUD)    SLATE_END_ARGS()    void Construct(const FArguments& InArgs);    /**    * Click handler for the Play Game! button – Calls MenuHUD’s PlayGameClicked() event.    */    FReply PlayGameClicked();    /**    * Click handler for the Quit Game button – Calls MenuHUD’s QuitGameClicked() event.    */    FReply QuitGameClicked();    TWeakObjectPtr<class AMainMenuHUD> MainMenuHUD;    const struct FGlobalStyle* MenuStyle;};
  • MainMenuUI.cpp
// Copyright 1998-2015 Epic Games, Inc. All Rights Reserved.#include "SlateTutorials.h"#include "MainMenuUI.h"#include "GlobalMenuStyle.h" #include "MenuStyles.h" BEGIN_SLATE_FUNCTION_BUILD_OPTIMIZATIONvoid SMainMenuUI::Construct(const FArguments& args){    MainMenuHUD = args._MainMenuHUD;    MenuStyle = &FMenuStyles::Get().GetWidgetStyle<FGlobalStyle>("Global");    ChildSlot    [        SNew(SOverlay)        + SOverlay::Slot()        .HAlign(HAlign_Center)        .VAlign(VAlign_Top)        [            SNew(STextBlock)            .TextStyle(&MenuStyle->MenuTitleStyle)            .Text(FText::FromString("Main Menu"))        ]        + SOverlay::Slot()        .HAlign(HAlign_Right)        .VAlign(VAlign_Bottom)        [            SNew(SVerticalBox)            + SVerticalBox::Slot()            [                SNew(SButton)                .ButtonStyle(&MenuStyle->MenuButtonStyle)                .TextStyle(&MenuStyle->MenuButtonTextStyle)                .Text(FText::FromString("Play Game!"))                .OnClicked(this, &SMainMenuUI::PlayGameClicked)            ]            + SVerticalBox::Slot()            [                SNew(SButton)                .ButtonStyle(&MenuStyle->MenuButtonStyle)                .TextStyle(&MenuStyle->MenuButtonTextStyle)                .Text(FText::FromString("Quit Game"))                .OnClicked(this, &SMainMenuUI::QuitGameClicked)            ]        ]    ];}END_SLATE_FUNCTION_BUILD_OPTIMIZATIONFReply SMainMenuUI::PlayGameClicked(){    if (GEngine)    {        GEngine->AddOnScreenDebugMessage(-1, 3.f, FColor::Yellow, TEXT("PlayGameClicked"));    }    // actually the BlueprintImplementable function of the HUD is not called; uncomment if you want to handle the OnClick via Blueprint    //MainMenuHUD->PlayGameClicked();    return FReply::Handled();}FReply SMainMenuUI::QuitGameClicked(){    if (GEngine)    {        GEngine->AddOnScreenDebugMessage(-1, 3.f, FColor::Yellow, TEXT("QuitGameClicked"));    }    // actually the BlueprintImplementable function of the HUD is not called; uncomment if you want to handle the OnClick via Blueprint    //MainMenuHUD->QuitGameClicked();    return FReply::Handled();}

总结

瞧!您的菜单现在已设置样式!这里我们做了什么的细节:
首先,在绑定我们的MainMenuHUD后,实际上通过我们的FMenuStyles类在布局前面加载Slate Widget样式。
接下来,我们调整游戏标题的STextBlock,以添加对TextStyle()的调用,并往其中传递我们的标题文本样式的地址。
这与Slate的属性调整完全相同!对于我们的两个按钮,我们实际分配两种样式。 首先,我们分配我们的按钮样式,然后我们分配文本样式 - 没有太多担心这里,对吧?你现在有一个菜单,有一些风格化的按钮!
编译并开始你的游戏,然后测试你的主菜单!

示例图片

1 0
原创粉丝点击