虚幻4之Slate学习二 —— Slate控件样式

  1. 内容简介
  2. 简单梳理
  3. 定义自定义Slate控件样式
  4. 自定义单例类
    1. 注册和注销
    2. FSlateStyleSet
    3. 共享指针和共享引用
  5. 创建单例实例
  6. 总结
  7. 相关链接

内容简介

  • 自定义控件样式
  • 生成样式集合,用于加载和引用我们的样式

简单梳理

为了能够在控件上显示自定义的各类属性,使用我们要使用自定义的控件样式。使用我们需要创建一个基于SlateWidgetSytle的C++类。在这里可以定义许多的自定义样式。比如最常用的FSlateBrush用于显示图片,FSlateFontInfo用于表示字体信息。接着我们思考的是怎么指定这些相关属性,即怎么指定图片等信息。有两个方法,一是直接用硬编码,指定文件地址。二是转变成蓝图类,可视化选择我们指定的内容。使用现在的问题是如何让在不同地方的Slate控件能够获取获取到这些内容。这里的方法是写一个单例,并在GameModule中生成唯一实例以供调用。

定义自定义Slate控件样式

创建基于SlateWidgetStyle的C++类MySlateWidgetStyle,在UE4 Editor中的内容浏览器中创建自定义的控件样式蓝图,并选择样式为刚创建的C++类,命名为BP_MySlateWidgetStyle,具体操作如图:


可以测试一下是否成功,添加如下代码

/** MySlateWidgetStyle.h 部分代码 **/

#pragma once

#include "CoreMinimal.h"
#include "Styling/SlateWidgetStyle.h"
#include "SlateWidgetStyleContainerBase.h"
#include "SlateBrush.h"
#include "MySlateWidgetStyle.generated.h"

/**
 * 
 */
USTRUCT()
struct MYSLATE_API FMySlateStyle : public FSlateWidgetStyle
{
    GENERATED_USTRUCT_BODY()

    FMySlateStyle();
    virtual ~FMySlateStyle();

    // FSlateWidgetStyle
    virtual void GetResources(TArray<const FSlateBrush*>& OutBrushes) const override;
    static const FName TypeName;
    virtual const FName GetTypeName() const override { return TypeName; };
    static const FMySlateStyle& GetDefault();

    //code needed to write 
    UPROPERTY(EditAnyWhere, Category = MenuHUD)
    FSlateBrush BackGroupBrush;
};

这样在BP_MySlateWidgetStyle中产生如下效果

自定义单例类

接下来再来捋一下接下来的思路,我们需要在MyHUDWidget中获取到我们蓝图类中的样式。这里我们可以通过ISlateStyle::GetWidgetStyle。所以我们需要有一个ISlateStyle,这里我们可以通过得到FSlateStyleSet,它是继承于ISlateStyle的,可以直接隐式转换。接着我们FSlateGameResources::New函数来获取蓝图类中的所有样式生成FSlateStyleSet。而这一些列操作需要一个单列来实现,而单例也需要在某个地方实例化。


创建一个不继承任何类的类,命名为MyStyle。单例模式大同小异,可以按照如下进行模仿

/** MyStyle.h **/
#pragma once

#include "CoreMinimal.h"
#include "ISlateStyle.h"

/**
 * 
 */
class MYSLATE_API MyStyle
{
public:
    static void Initialze();
    static FName GetStyleSetName();
    static void ShutDown();
    static const ISlateStyle& Get();

private:
    static TSharedRef<class FSlateStyleSet> Create();
    static TSharedPtr<class FSlateStyleSet> MytyleInstance;
};
/** MyStyle.cpp **/
#include "MyStyle.h"
#include "SlateStyleRegistry.h"
#include "SlateGameResources.h"

//由于此单例不用构造函数,所以成员初始化写在这里
TSharedPtr<FSlateStyleSet> MyStyle::MyStyleInstance = NULL;

void MyStyle::Initialze()
{
    if (!MyStyleInstance.IsValid())
    {
        MyStyleInstance = Create();
        FSlateStyleRegistry::RegisterSlateStyle(*MyStyleInstance);

    }
}

FName MyStyle::GetStyleSetName()
{
    const static FName MyStyleName(TEXT("BP_MySlateWidgetStyle"));
    return MyStyleName;
}

void MyStyle::ShutDown()
{
    FSlateStyleRegistry::UnRegisterSlateStyle(*MyStyleInstance);
    ensure(MyStyleInstance.IsUnique());
    MyStyleInstance.Reset();
}

const ISlateStyle& MyStyle::Get()
{
    return *MyStyleInstance;
}

TSharedRef<class FSlateStyleSet> MyStyle::Create()
{
    TSharedRef<FSlateStyleSet> StyleRef = FSlateGameResources::New(GetStyleSetName(), "/Game/" , "/Game/");
    return StyleRef;
}

注册和注销

  • RegisterSlateStyle:Add a slate style to the repository.
  • UnRegisterSlateStyle:Removes a slate style from the repository.

猜测:用于在 Static initialization时预加载我们需要的样式

FSlateStyleSet

  • 一个Slate样式块,包含一组指定Slate外观的命名属性。

共享指针和共享引用

注意的是这两个都是指针,主要区别是TSharedRef<>不能指向空,且TSharedPtr<>可以隐式转换成TSharedRef<>,所有这里声明时用的是TSharedPtr<>

创建单例实例

  • FDefaultGameModuleImpl中的StartupModule()和ShutdownModule()这两个函数会在加载(卸载)dll和模块后立刻调用,我们可以利用来生成实例。在MySlate中写如下代码:
    /**  MySlate.h **/
    class FMySlateModule : public FDefaultGameModuleImpl
    {
    public:
         virtual void StartupModule() override;
         virtual void ShutdownModule() override;
    };
    
  • FDefaultGameModuleImpl:Default minimal module class for gameplay modules. Does nothing at startup and shutdown.
  • 所以上述其实就是自定义了一个模块
/** MySlate.cpp **/
#include "MySlate.h"
#include "Modules/ModuleManager.h"
#include "Public/UI/MyStyle.h"
#include "SlateStyleRegistry.h"

IMPLEMENT_PRIMARY_GAME_MODULE(FMySlateModule, MySlate, "MySlate" );

void FMySlateModule::StartupModule()
{
    //先取消注册,保证不会重复注册
    FSlateStyleRegistry::UnRegisterSlateStyle(MyStyle::GetStyleSetName());
    MyStyle::Initialze();
}

void FMySlateModule::ShutdownModule()
{
    MyStyle::ShutDown();
}
  • 在您的游戏中,至少要使用IMPLEMENT_PRIMARY_GAME_MODULE注册一个模块。其他模块可以使用另一个可选的IMPLEMENT_GAME_MODULE方法进行注册。
  • 所要使用的模组,注意默认是FDefaultGameModuleImpl,所以这里我们改为FMySlateModule
  • 更多关于Module可以看官方手册

总结

这样我们就定义好了我们的控件样式,我们来测试一下是否成功,在样式蓝图中选择一张图片,并添加如下代码

/** SMyHUDWidget.h 部分代码**/
#include "MySlateWidgetStyle.h"

private:
    const FMySlateStyle *MySlateStyle;
/** SMyHUDWidget.cpp **/
#include "SMyHUDWidget.h"
#include "SlateOptMacros.h"
#include "SButton.h"
#include "SImage.h"
#include "MyStyle.h"

BEGIN_SLATE_FUNCTION_BUILD_OPTIMIZATION
void SMyHUDWidget::Construct(const FArguments& InArgs)
{
    MySlateStyle = &MyStyle::Get().GetWidgetStyle<FMySlateStyle>("BP_MySlateWidgetStyle");

    ChildSlot
    [
        SNew(SImage)
        .Image(&MySlateStyle->BackGroupBrush)

    ];

}
END_SLATE_FUNCTION_BUILD_OPTIMIZATION
  • 注意MyStyle::Get()返回的是一个集合,所以我们需要使用GetWidgetStyle(“BP_MySlateWidgetStyle”)来获取我们所定义的蓝图样式。

上述运行结果

相关链接