UE4中的SubSystem的使用

引言

Subsystems 是一套可以定义、自动实例化和释放的类的框架。可以将其理解为 GamePlay 级别的 Component.

UE在4.22版本的时候开始引入Subsystems,并在4.24完善.SubSystem其实可以算是GamePlay框架基础上的一个增强功能,属于GamePlay架构的范围.

Subsystem的好处是利用它的特性可以很好的安排好对象的生命周期,也会使程序的架构更加的清晰.

SubSystem分类及其生命周期

  • UEngineSubsystem(继承自 UDynamicSubsystem,UDynamicSubsystem继承自 USubsystem)
    UEngine* GEngine
    代表引擎,数量1。 Editor或Runtime模式都是全局唯一,从进程启动开始创建,进程退出时销毁。
    UEngine::Init()
  • UEditorSubsystem(继承自 UDynamicSubsystem,UDynamicSubsystem继承自 USubsystem)
    UEditorEngine* GEditor
    代表编辑器,数量1。 顾名思义,只在编辑器下存在且全局唯一,从编辑器启动开始创建,到编辑器退出时销毁。
  • UGameInstanceSubsystem (继承自 USubsystem)
    UGameInstance* GameInstance
    代表一场游戏,数量1。 从游戏的启动开始创建,游戏退出时销毁。这里的一场游戏指的是Runtime或PIE模式的运行的都算,一场游戏里可能会创建多个World切换。
  • UWorldSubsystem (继承自 USubsystem)
    UWorld* World
    代表一个世界,数量可能>1。其生命周期,跟GameMode是一起的。(EWorldType:Game,Editor,PIE,EditorPreview,GamePreview等 )
  • ULocalPlayerSubsystem (继承自 USubsystem)
    ULocalPlayer* LocalPlayer:代表本地玩家,数量可能>1。
    UE支持本地分屏多玩家类型的游戏,但往往最常见的是就只有一个。LocalPlayer虽然往往跟PlayerController一起访问,但是其生命周期其实是跟UGameInstance一起的(默认一开始的时候就创建好一定数量的本地玩家),或者更准确的说是跟LocalPlayer的具体数量挂钩(当然你也可以运行时动态调用AddLocalPlayer)。

为什么要使用 Subsystems

  • 更适用的生命周期

    Subsystems 的生命周期可以依存于Engine,Editor,World,LocalPlayer

  • 更简

    Subsystems 自动创建、释放,提供 Initialize()、Deinitialize(),并且可重载

  • 更模块化、更优雅、更封装、更易于维护、移植复用

    Subsystems 可以为不同的 Manager 创建对应的Subsystems

  • 更友好的访问接口

    Subsystem 更像以全局变量的形式来访问
    提供了 Python 脚本的访问,用于编写编辑器脚本或编写测试代码

  • Subsystem 无需覆盖引擎类。

Subsystems 的使用

定义C++子类

//声明定义:
UCLASS() 
class HELLO_API UMyEditorSubsystem : public UEditorSubsystem
UCLASS() 
class HELLO_API UMyEngineSubsystem : public UEngineSubsystem
UCLASS() 
class HELLO_API UMyGameInstanceSubsystem : public UGameInstanceSubsystem
UCLASS() 
class HELLO_API UMyWorldSubsystem : public UWorldSubsystem
UCLASS() 
class HELLO_API UMyLocalPlayerSubsystem : public ULocalPlayerSubsystem

//注:使用UEditorSubsystem需要在Build.cs里加上EditorSubsystem模块的引用,因为这是编辑器模块
if (Target.bBuildEditor)
{
    PublicDependencyModuleNames.AddRange(new string[] { "EditorSubsystem" });
}

添加函数和方法

UCLASS()
class HELLO_API UMyScoreSubsystem : public UGameInstanceSubsystem
{
    GENERATED_BODY()
public://重载的函数,可以做一些初始化和释放操作
    virtual bool ShouldCreateSubsystem(UObject* Outer) const override { return true; }
    virtual void Initialize(FSubsystemCollectionBase& Collection)override;
    virtual void Deinitialize()override;
public:
    UFUNCTION(BlueprintCallable)
    void AddScore(float delta);
public:
    UPROPERTY(EditAnywhere, BlueprintReadWrite)
        float Score;
};

使用

//UMyEngineSubsystem获取
UMyEngineSubsystem* MySubsystem = GEngine->GetEngineSubsystem<UMyEngineSubsystem>();

//UMyEditorSubsystem的获取
UMyEditorSubsystem* MySubsystem = GEditor->GetEditorSubsystem<UMyEditorSubsystem>();

//UMyGameInstanceSubsystem的获取
UGameInstance* GameInstance = UGameplayStatics::GetGameInstance(...);
UMyGameInstanceSubsystem* MySubsystem = GameInstance->GetSubsystem<UMyGameInstanceSubsystem>();

//UMyWorldSubsystem的获取
UWorld* World=MyActor->GetWorld();  //world用各种方式也都可以
UMyWorldSubsystem* MySubsystem=World->GetSubsystem<UMyWorldSubsystem>();

//UMyLocalPlayerSubsystem的获取
ULocalPlayer* LocalPlayer = Cast<ULocalPlayer>(PlayerController->Player)
UMyLocalPlayerSubsystem * MySubsystem = LocalPlayer->GetSubsystem<UMyLocalPlayerSubsystem>();

Subsystem蓝图里面也可以访问

生命周期

各个模块的生命周期
enter image description here

创建对象的生命周期
enter image description here

如何用好Subsystems?

1. Subsystem是GamePlay级别的Component。
2. USubsystem只是个普通的UObject。
3. Subsystem是有状态的。
4. 尽量不要再写Manager类了。
5. 引擎源码里已经定义了一些Subsystem,方便 抄袭 (学习)。

参考文章

  1. 《InsideUE4》GamePlay架构(十一)Subsystems – 知乎 (zhihu.com)
  2. UE4.22 Subsystem分析 – 知乎 (zhihu.com)
  3. 【UE4 C++】编程子系统 Subsystem – 砥才人 – 博客园 (cnblogs.com)
  4. 编程子系统 | 虚幻引擎文档 (unrealengine.com)
  5. 【技术总结】UE4中的Subsystem – 夜溅樱 – 博客园 (cnblogs.com)