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的属性调整完全相同!对于我们的两个按钮,我们实际分配两种样式。 首先,我们分配我们的按钮样式,然后我们分配文本样式 - 没有太多担心这里,对吧?你现在有一个菜单,有一些风格化的按钮!
编译并开始你的游戏,然后测试你的主菜单!
- Unreal Engine 4 C++ Slate 介绍——用C++和Slate创建菜单(二)
- Unreal Engine 4 C++ Slate 介绍——用C++和Slate创建菜单(一)
- Unreal Engine 4 C++ Slate 介绍——用C++和Slate创建菜单(三)
- 虚幻4 Slate主线程
- [UE4]用Slate框架写UE4插件
- Unreal Engine 4 C++ Component介绍——BrainComponent
- Unreal Engine 4 C++ Component介绍——WidgetComponent
- Unreal Engine 4 —— Asset Manager介绍
- Unreal Engine 4 AI (二)用C++和Behavior Tree & Blackboard 实现简单追踪AI
- Slate教学管理工具简介
- UE4 Hello Slate
- UE4 Slate, Hello
- UE4 Slate Architecture
- UE4 Slate概述
- Unreal Engine 4 —— 常见Tips
- Unreal Engine 4 —— 常见Tips
- Unreal教学(9)——虚幻引擎名词解释(Unreal Engine 4 Terminology)
- Unreal Engine 4 Radiant UI 入门教程(二)
- Silverlight+WCF 新手实例 象棋 登陆与转向(十一)
- Silverlight+WCF 新手实例 象棋 游戏房间(十二)
- Silverlight+WCF 新手实例 象棋 游戏房间列表(十三)
- Silverlight+WCF 新手实例 象棋 WCF通讯基础(十四)
- Silverlight+WCF 新手实例 象棋 WCF通讯跨域(十五)
- Unreal Engine 4 C++ Slate 介绍——用C++和Slate创建菜单(二)
- Silverlight+WCF 新手实例 象棋 WCF通讯方式(十六)
- Silverlight+WCF 新手实例 象棋 WCF通讯轮询(十七)
- Silverlight+WCF 新手实例 象棋 回归WCF通讯应用-登陆(十八)
- Silverlight+WCF 新手实例 象棋 在线演示
- Silverlight+WCF 简单部署问题集
- Silverlight+WCF 新手实例 象棋 回归WCF通讯应用-进入房间(十九)
- Silverlight+WCF 新手实例 象棋 房间状态更新(二十)
- python tools:计算视频的 FPS,以及总帧数