跳至主要內容

c4.Subsystem|子系统

Mr.Si大约 4 分钟u++

头像
什么是子系统?
头像
虚幻引擎中的子系统是生命周期受控的自动实例化类。

生命周期

头像
下面是引擎预定义的几个子系统
生命周期Subsystem 类
引擎(Engine)UEngineSubsystem
编辑器(Editor)UEditorSubsystem
游戏实例(GameInstance)UGameInstanceSubsystem
世界(World)UWorldSubsystem
本地玩家(LocalPlayer)ULocalPlayerSubsystem

创建和使用

1. 继承对应的子系统

2. 基本函数

头像
没有什么特殊的,无法就是是否创建、构造、析构。
ExorcistGameInstanceSubsystem.h
#pragma once

#include "CoreMinimal.h"
#include "Subsystems/GameInstanceSubsystem.h"
#include "MyGameInstanceSubsystem.generated.h"

UCLASS()
class EXORCIST_API UMyGameInstanceSubsystem : public UGameInstanceSubsystem
{
	GENERATED_BODY()
public:
	//是否创建这个子系统
	virtual bool ShouldCreateSubsystem(UObject* Outer)  const override;
	//初始化
	virtual void Initialize(FSubsystemCollectionBase& Collection) override;
	//销毁
	virtual void Deinitialize() override;
};


3.蓝图中使用

头像
然后呢?
头像
和你在正常的UObject中一样,添加成员变量、函数,利用宏反射标记暴露给蓝图就能使用了。

4. C++中调用

MyEngineSubsystem

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

MyEditorSubsystem

UMyEditorSubsystem* MySubsystem = GEditor->GetEditorSubsystem<UMyEditorSubsystem>();

MyGameInstanceSubsystem

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

MyWorldSubsystem

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

MyLocalPlayerSubsystem

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

再谈生命周期

GetOuter()

头像
GetGameInstance函数定义中的GetOuter()到底是什么啊?
UGameInstance* UGameInstanceSubsystem::GetGameInstance() const
{
	return Cast<UGameInstance>(GetOuter());
}
头像

UE中讲究落叶归根。对于 UGameInstanceSubsystem,它的 "根" Outer 就是它所附着的 UGameInstance 实例。


头像
不明觉厉。
头像
既然咱们理解不了,咱们应该抛开现象看本质,康康这个到底是一个什么东西!
头像

从源码上来看GetOuter() 是 UObjectBase 类中的一个内联返回UObject指针的函数,用于获取当前对象的外部对象,即包含当前对象的对象。

返回的就是
返回的就是UObject
头像
这里的FORCEINLINE是什么?

#defineFORCEINLINE__forceinline

头像

FORCEINLINE是一个宏,也就是__forceinline 用于强制将函数进行内联展开。

头像

可是为什么我能在 UGameInstanceSubsystem 调到UObjectBase的成员函数呢?

头像

这就和UE的继承结构有关了。这个类目开篇我们已经介绍过:
UE中几乎所有类都继承自 UObjectUGameInstanceSubsystem 类作为子系统也不例外。

继承特性

提示

在C++中,继承分为公有继承、保护继承和私有继承三种方式。 对于公有继承(public inheritance),子类可以调用父类的公开(public)成员函数.

             UObject————————————————————————————
               |                                |
           USubsystem                           |
               |                                |
 UGameInstanceSubsystem——Cast<UGameInstance>(GetOuter())
  • UObject 是 Unreal Engine 中所有对象的基类。
  • USubsystem 是一个子系统抽象类,继承自 UObject
  • UGameInstanceSubsystem 也是是一个子系统抽象类,继承自 USubsystem
头像
也就是说通过GetOuter()将`UObject`指针强制转成`UGameInstance`类指针。然后绑定关联UGameInstance到UGameInstanceSubsystem是吗。
头像
是的,这样我们可以直接通过UGameInstanceSubsystem访问UGameInstance。
头像
我有个疑问,既然他都是UObject指针,他们的生命周期是怎么判定的?
头像

如果我们翻看其他类型的Subsystem,可以知道GetOuter()返回的指针各自转换成各自类型的指针,各自类型的指针意味着有各自的生命周期。

头像
自此,`UGameInstanceSubsystem`表象已经被我们摸透。

CDO | Class Default Object

头像
我看到注释里有个CDO是什么意思啊。
头像
Man,这其实是反射的内容知识。

在 Unreal Engine 中,每个通过反射系统注册的 UClass 都会在引擎初始化时自动生成一个 Class Default Object(简称 CDO)。 本章不会展开说明,我们会在后续的反射章节中详细展开。

UMyObject* CDO = GetDefault<UMyObject>();

不要试图在 CDO 构造阶段(UObject 构造函数里)做任何依赖运行时世界或子系统的操作, 这些操作应该放到更安全的生命周期阶段(如 BeginPlay() 或子系统的初始化回调)中。