二、ActionRPGLoadingScreen.h/cpp & Slate

ActionRPG项目中增加了一个自定义模块:ActionRPGLoadingScreenModule。在这个模块中只有ActionRPGLoadingScreen.h/cpp,实现的功能有:使用Slate绘制加载界面、显示和隐藏加载界面。


Loading Screen

当然,我们也可以使用单独一个loading map来实现过渡。


ActionRPGLoadingScreen.h

除了项目自动生成的module(ActionRPGModule)之外,所有自定义的模块都要定义一个继承自IModuleInterface类的新类,然后使用IMPLEMENT_MODULE/IMPLEMENT_GAME_MODULE声明。之后,引擎会在加载模块时自动生成对应类的实例。

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

#pragma once

#include "ModuleInterface.h"
#include "Modules/ModuleManager.h"

/** Module interface for this game's loading screens */
/** 游戏加载界面的模块接口 **/
class IActionRPGLoadingScreenModule : public IModuleInterface
{
public:
    /** Loads the module so it can be turned on */
       /** 加载模块,并获得实例,不会重复加载 **/
    static inline IActionRPGLoadingScreenModule& Get()
    {
        return FModuleManager::LoadModuleChecked("ActionRPGLoadingScreen");
    }

    /** Kicks off the loading screen for in game loading (not startup) */
   /** 在游戏中打开加载界面**/
    virtual void StartInGameLoadingScreen(bool bPlayUntilStopped, float PlayTime) = 0;

    /** Stops the loading screen */
      /** 关闭加载界面 **/
    virtual void StopInGameLoadingScreen() = 0;
};

注意定义的新类以“I”开头,我们称之为“接口”(与c#中的interface不是同一个东西)。


ActionRPGLoadingScreen.cpp

文件中有三个数据类型:

  • FRPGLoadingScreenBrush : 自定义Slate笔刷
  • SRPGLoadingScreen: 自定义Slate组合widget
  • FActionRPGLoadingScreenModule : 模块接口实现类

引用

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

#include "ActionRPGLoadingScreen.h"
#include "SlateBasics.h"
#include "SlateExtras.h"
#include "MoviePlayer.h"
#include "SThrobber.h"

// This module must be loaded "PreLoadingScreen" in the .uproject file, otherwise it will not hook in time!
// 这个模块的加载阶段必须是"PreLoadingScreen"

FRPGLoadingScreenBrush

struct FRPGLoadingScreenBrush : public FSlateDynamicImageBrush, public FGCObject
{
    FRPGLoadingScreenBrush(const FName InTextureName, const FVector2D& InImageSize)
        : FSlateDynamicImageBrush(InTextureName, InImageSize)
    {
        SetResourceObject(LoadObject(NULL, *InTextureName.ToString()));
    }

    virtual void AddReferencedObjects(FReferenceCollector& Collector)
    {
        if (UObject* CachedResourceObject = GetResourceObject())
        {
            Collector.AddReferencedObject(CachedResourceObject);
        }
    }
};

这个类涉及到UE的内存管理。Slate控件并不是继承自UObject,类中的UObject类型无法使用UPROPERTY()标记,会被GC回收,如此处由LoadObject方法加载的资源对象。为了解决这个问题,需要继承FGCObject,并重写AddReferencedObjects方法。

SRPGLoadingScreen

class SRPGLoadingScreen : public SCompoundWidget
{
public:
    SLATE_BEGIN_ARGS(SRPGLoadingScreen) {}
    SLATE_END_ARGS()

    void Construct(const FArguments& InArgs)
    {
        // Load version of the logo with text baked in, path is hardcoded because this loads very early in startup
        static const FName LoadingScreenName(TEXT("/Game/UI/T_ActionRPG_TransparentLogo.T_ActionRPG_TransparentLogo"));

        LoadingScreenBrush = MakeShareable(new FRPGLoadingScreenBrush(LoadingScreenName, FVector2D(1024, 256)));
        
        FSlateBrush *BGBrush = new FSlateBrush();
        BGBrush->TintColor = FLinearColor(0.034f, 0.034f, 0.034f, 1.0f);

        ChildSlot
            [
            SNew(SOverlay)
            + SOverlay::Slot()
            .HAlign(HAlign_Fill)
            .VAlign(VAlign_Fill)
            [
                SNew(SBorder)   
                .BorderImage(BGBrush)
            ]
            +SOverlay::Slot()
            .HAlign(HAlign_Center)
            .VAlign(VAlign_Center)
            [
                SNew(SImage)
                .Image(LoadingScreenBrush.Get())
            ]
            +SOverlay::Slot()
            .HAlign(HAlign_Fill)
            .VAlign(VAlign_Fill)
            [
                SNew(SVerticalBox)
                +SVerticalBox::Slot()
                .VAlign(VAlign_Bottom)
                .HAlign(HAlign_Right)
                .Padding(FMargin(10.0f))
                [
                    SNew(SThrobber)
                    .Visibility(this, &SRPGLoadingScreen::GetLoadIndicatorVisibility)
                ]
            ]
        ];
    }

private:
    /** Rather to show the ... indicator */
    EVisibility GetLoadIndicatorVisibility() const
    {
        bool Vis =  GetMoviePlayer()->IsLoadingFinished();
        return GetMoviePlayer()->IsLoadingFinished() ? EVisibility::Collapsed : EVisibility::Visible;
    }
    
    /** Loading screen image brush */
    TSharedPtr LoadingScreenBrush;
};

Slate组合Widget。Slate是一套UI编码系统,可以用来绘制UE Editor和游戏内的UI界面(游戏内UI推荐使用UMG)。
Slate官方文档
文档不怎么详细,使用Slate的更多例子可以在引擎的源代码,还有官方Shooter Game里面查看。
ChildSlot中各个widget组合最终结果就是我们一开始看到的Loading界面。需要注意的是,Slate系统的对象内存是使用TSharedPtr来管理的。
PS:用代码来写UI总是不够直观的,虽然它更易于操作数据和绑定事件。你可以先用UMG设计表现,然后再“翻译”成Slate。新版的UE已经支持Editor Utility Widget来拓展Editor UI。

FActionRPGLoadingScreenModule

class FActionRPGLoadingScreenModule : public IActionRPGLoadingScreenModule
{
public:
       //加载module时调用
    virtual void StartupModule() override
    {
        // Force load for cooker reference
              //  这个强制load,还不确切明白其功能,应该是资源管理相关。我将其注释,也没出现问题。
        LoadObject(nullptr, TEXT("/Game/UI/T_ActionRPG_TransparentLogo.T_ActionRPG_TransparentLogo") );

        if (IsMoviePlayerEnabled())
        {
            CreateScreen();
        }
    }
    //是否是游戏模块
    virtual bool IsGameModule() const override
    {
        return true;
    }
       //在游戏中调用,显示加载界面
    virtual void StartInGameLoadingScreen(bool bPlayUntilStopped, float PlayTime) override
    {
        FLoadingScreenAttributes LoadingScreen;
        LoadingScreen.bAutoCompleteWhenLoadingCompletes = !bPlayUntilStopped;
        LoadingScreen.bWaitForManualStop = bPlayUntilStopped;
        LoadingScreen.bAllowEngineTick = bPlayUntilStopped;
        LoadingScreen.MinimumLoadingScreenDisplayTime = PlayTime;
        LoadingScreen.WidgetLoadingScreen = SNew(SRPGLoadingScreen);
        GetMoviePlayer()->SetupLoadingScreen(LoadingScreen);
    }
      //在游戏中调用,隐藏加载界面
    virtual void StopInGameLoadingScreen() override
    {
        GetMoviePlayer()->StopMovie();
    }
    //点开应用,最初进入游戏时调用,显示加载界面
    virtual void CreateScreen()
    {
        FLoadingScreenAttributes LoadingScreen;
        LoadingScreen.bAutoCompleteWhenLoadingCompletes = true;
        LoadingScreen.MinimumLoadingScreenDisplayTime = 3.f;
        LoadingScreen.WidgetLoadingScreen = SNew(SRPGLoadingScreen);
        GetMoviePlayer()->SetupLoadingScreen(LoadingScreen);
    }

};

//很重要,module都要有此声明,此处为游戏模块
IMPLEMENT_GAME_MODULE(FActionRPGLoadingScreenModule, ActionRPGLoadingScreen);

该类主要就是负责加载,显示,隐藏加载界面。有一个关键的类我们需要注意到:MoviePlayer。这个类负责进入游戏时的加载界面,还有游戏中的过场动画等。

将Loading Screen用Slate编写并且放在单独的模块,而不是使用loading map,应该就是为了在进入游戏前(加载ActionRPG模块前)显示加载界面。

以上就是该模块的所有内容了。

你可能感兴趣的:(二、ActionRPGLoadingScreen.h/cpp & Slate)