引言
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蓝图里面也可以访问
生命周期
各个模块的生命周期

创建对象的生命周期

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