编程子系统
# 编程子系统
此文为Programming Subsystems (opens new window)的原创翻译,本文内容版权归原文所有,仅供学习,如需转载望注本文地址,翻译不易,谢谢理解。
概览UE4中的编程子系统。
在UE4中的子系统是自动被实例化的类,这些类有着自己的生命周期,这些类提供易于使用的扩展点,程序员可以立刻使用其蓝图和Python接口,而不用复杂地修改或重写引擎类。
当前支持的子系统生命周期包括:
子系统 | 继承自 |
---|---|
Engine | UEngineSubsystem类 |
Editor | UEditorSubsystem类 |
GameInstance | UGameInstanceSubsystem类 |
LocalPlayer | ULocalPlayerSubsystem类 |
比如,如果你想创建一个类继承这个基类:
class UMyGamesSubsystem : public UGameInstanceSubsystems
这会导致:
- 在UGameInstance被创建后,一个UMyGamesSubsystem的实例也会被创建。
- 在UGameInstance初始化时,子系统会调用Initialize()。
- 在UGameInstance被关闭后,子系统会调用Deinitialize()。
- 这个时候,如果子系统的引用被丢掉后没有其他引用指向它,那么该子系统会被垃圾回收。
# 使用子系统的原因
有以下几个原因使用编程子系统:
- 子系统可以节省编程时间。
- 子系统能帮你避免重写引擎类。
- 子系统能帮你避免在一个已存在很复杂的类上添加更多API。
- 子系统能让蓝图通过对用户友好的节点来获取。
- 子系统能让Python脚本获取,来进行编辑器编程或者编写测试代码。
- 子系统让代码库模块化和一致化。
子系统在创建插件的时候是很有用的。你不必对代码进行说明来让插件工作。用户只需添加插件到游戏里,然后你就准确地知道插件啥时候被实例化和初始化。最后,你只需集中精力在如何使用UE4提供的API和功能上。
# 使用蓝图获取子系统
子系统会被自动地暴露给蓝图,在理解上下文的节点中,并不需要转换。你需要使用标准UFUNCTION()标记来控制哪些API对蓝图是可用的。
如果你在蓝图上右击,勾选Context Sensitive,然后搜索子系统,你得到的会和下面相似,每个主要类型都有一个分类,然后每个具体子系统也有一个入口。
如果你从上面添加了一个节点,你会得到像下面这样的。
# 使用Python获取子系统
如果你在使用Python,你可以使用内置的访问器来获取子系统,就像下面的例子那样:
my_engine_subsystem = unreal.get_engine_subsystem(unreal.MyEngineSubsystem)
my_editor_subsystem = unreal.get_editor_subsystem(unreal.MyEditorSubsystem)
2
# 子系统的生命周期
# Engine子系统
class UMyEngineSubsystem : public UEngineSubsystem { ... };
在Engine子系统模块加载时,子系统会在模块的Startup()执行后执行Initialize(),在模块的Shutdown()执行后执行Deinitialize()。
这些子系统可以通过GEngine获取:
UMyEngineSubsystem MySubsystem = GEngine->GetEngineSubsystem<UMyEngineSubsystem>();
# Editor子系统
class UMyEditorSubsystem : public UEditorSubsystem { ... };
在Editor子系统模块加载时,子系统会在模块的Startup()执行后执行Initialize(),在模块的Shutdown()执行后执行Deinitialize()。
这些子系统可以通过GEditor获取:
UMyEditorSubsystem MySubsystem = GEditor->GetEditorSubsystem<UMyEditorSubsystem>();
# GameInstance子系统
class UMyGameSubsystem : public UGameInstanceSubsystem { ... };
这些子系统能通过UGameInstance获取:
UGameInstance* GameInstance = ...;
UMyGameSubsystem* MySubsystem = GameInstance->GetSubsystem<UMyGameSubsystem>();
2
# LocalPlayer子系统
class UMyPlayerSubsystem : public ULocalPlayerSubsystem { ... };
这些子系统能通过ULocalPlayer来获取:
ULocalPlayer* LocalPlayer = ...;
UMyPlayerSubsystem * MySubsystem = LocalPlayer->GetSubsystem<UMyPlayerSubsystem>();
2
# 子系统例子
在下面的例子中,我们想通过给游戏添加状态系统来记录获取资源的数量。
我们可以创建UMyGamesGameInstance继承自UGameInstance,然后给它添加IncrementResourceStat()函数。但是我们知道最终,这个团队也想添加其他状态,比如状态聚合器和保存/加载的状态等等。因此,我们想把这些放到一个类中,比如UMyGamesStatsSubsystem。
现在再来回想下,我们可以创建UMyGamesGameInstance然后添加一个UMyGamesStatsSubsystem成员,然后再给它添加一个获取器,并连接Initialize和Deinitialize函数,但是如果这样做会有几个问题:
- 没有游戏相关的UGameInstance派生类。
- UMyGamesGameInstance存在,但是它会有大量的函数,给它添加更多的函数并不好。
在一个复杂的游戏里,有充分的理由去继承UGameInstance,但是当有你不需要的子系统时,最好使用子系统,这样可有选择的减少编码。
我们最终创建的类如下:
UCLASS()
class UMyGamesStatsSubsystem : public UGameInstanceSubsystem
{
GENERATED_BODY()
public:
// Begin USubsystem
virtual void Initialize(FSubsystemCollectionBase& Collection) override;
virtual void Deinitialize() override;
// End USubsystem
void IncrementResourceStat();
private:
// All my variables
};
2
3
4
5
6
7
8
9
10
11
12
13
14