在这个章节会对编程子系统进行解读,然后加加上一些自己的代码实例,希望对你有所帮助。
编程子系统
虚幻引擎编程子系统概览。

虚幻引擎(UE) 中的子系统是生命周期受控的自动实例化类。这些类提供了易用的扩展点,程序员可直接获得蓝图和Python公开,同时避免繁复的引擎类修改或覆盖。
当前支持的子系统生命周期包括:
| 子系统 | 继承自 |
|---|---|
| 引擎 | UEngineSubsystem 类 |
| 编辑器 | UEditorSubsystem 类 |
| 游戏实例 | UGameInstanceSubsystem 类 |
| 本地玩家 | ULocalPlayerSubsystem 类 |
举例而言,如果创建了一个派生自此基类的类:
class UMyGamesSubsystem : public UGameInstanceSubsystem
复制完整片段
将出现以下结果:
- 创建
UGameInstance之后,还会创建一个名为UMyGamesSubsystem的实例。 UGameInstance初始化时,将在子系统上调用Initialize()。UGameInstance关闭时,将在子系统上调用Deinitialize()。- 此时将放弃对子系统的引用,如果不再有对子系统的引用,则其将被垃圾回收。
使用子系统的原因
使用编程子系统有以下几个原因:
- 子系统可节省编程时间。
- 子系统使您无需覆盖引擎类。
- 子系统使您无需再已经很复杂的类上添加更多API。
- 子系统使您能通过用户友好的类型化节点来访问蓝图。
- 子系统允许访问Python脚本来编写编辑器脚本或编写测试代码。
- 子系统在代码库中提供模块化和一致性。
子系统在创建插件时尤为实用。您不需要代码相关的说明即可让插件工作。用户只需将插件添加到游戏中,就可以确切了解插件将在何时被实例化和初始化。因此,您可以专注于UE4中提供的API和功能的使用方式。
用蓝图访问子系统
子系统将自动公开到蓝图,智能节点理解情境,且不需要强制转换。您可以使用标准的 UFUNCTION() 标记和规则来控制蓝图可以使用哪些API。
右键点击蓝图图表来显示快捷菜单并搜索"子系统",将出现类似于下图的内容。这里有每个主要类型的类目,以及每个特定子系统的单个条目。 
如果从上添加节点,将得到如下结果。 
用Python访问子系统
如果正在使用Python,则可以用内置访问器来访问子系统,如下方示例所示。
my_engine_subsystem = unreal.get_engine_subsystem(unreal.MyEngineSubsystem)
my_editor_subsystem = unreal.get_editor_subsystem(unreal.MyEditorSubsystem)
Python当前是一个实验性功能。
子系统生命周期细节
引擎子系统
class UMyEngineSubsystem : public UEngineSubsystem { ... };
复制完整片段
当引擎子系统的模块加载时,子系统将在模块的 Startup() 函数返回后执行 Initialize(),子系统将在模块的 Shutdown() 函数返回后执行 Deinitialize()。
这些子系统通过GEngine访问,如下所示。
UMyEngineSubsystem* MySubsystem = GEngine->GetEngineSubsystem<UMyEngineSubsystem>();
复制完整片段
编辑器子系统
class UMyEditorSubsystem : public UEditorSubsystem { ... };
复制完整片段
当编辑器子系统的模块加载时,子系统将在模块的 Startup() 函数返回后执行 Initialize(),子系统将在模块的 Shutdown() 函数返回后执行 Deinitialize()。
这些子系统可通过GEEditor访问,如下所示。
UMyEditorSubsystem* MySubsystem = GEditor->GetEditorSubsystem<UMyEditorSubsystem>();
复制完整片段
GameInstance子系统
class UMyGameSubsystem : public UGameInstanceSubsystem { ... };
复制完整片段
这些子系统可通过UGameInstance访问,如下所示。
UGameInstance* GameInstance = ...;
UMyGameSubsystem* MySubsystem = GameInstance->GetSubsystem<UMyGameSubsystem>();
LocalPlayer子系统
class UMyPlayerSubsystem : public ULocalPlayerSubsystem { ... };
这些子系统可通过ULocalPlayer访问,如下所示。
ULocalPlayer* LocalPlayer = ...;
UMyPlayerSubsystem * MySubsystem = LocalPlayer->GetSubsystem<UMyPlayerSubsystem>();
子系统范例
在以下示例中,我们希望为游戏添加一个统计数据系统,以跟踪收集资源的数量。
我们可以从 UGameInstance 派生,并创建 UMyGamesGameInstance,然后为其添加 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
};
01
子系统是用来做什么的
于开发非游戏进程的工具或程序(如命令行工具、资源处理器、批量导出器等)。
它不依赖 UE 的游戏核心模块(如 Engine、CoreUObject),仅基于轻量级的 Core 模块运行,因此能脱离编辑器 / 游戏进程独立启动
误解 1:将编程子系统与 “游戏子系统(Game Subsystem)” 混淆
-
错误表现:试图在编程子系统中使用
AActor、UWorld等游戏模块类,导致编译失败或运行崩溃。 -
原因:编程子系统基于
Core模块,不依赖游戏运行时;而游戏子系统(如UGameInstanceSubsystem)依赖Engine模块,仅在游戏进程中生效。 -
避免方法
:
- 明确需求:若需处理游戏内逻辑(如玩家数据),用游戏子系统;若需开发独立工具(如资源批量处理),用编程子系统。
- 检查模块依赖:编程子系统的
.Build.cs中仅保留Core依赖,删除Engine、CoreUObject等游戏相关模块。
02
测试案例,新建类的时候继承自GameInstanceSubsystem
#pragma once
#include "CoreMinimal.h"
#include "Subsystems/GameInstanceSubsystem.h"
#include "MyProgrammingSubsystem.generated.h"
/**
* 自定义编程子系统示例,继承自 GameInstanceSubsystem
*/
UCLASS()
class PROJECT2_API UMyProgrammingSubsystem : public UGameInstanceSubsystem
{
GENERATED_BODY()
public:
UMyProgrammingSubsystem();
// 在实例化时自动取调用
virtual void Initialize(FSubsystemCollectionBase& Collection) override;
// 在蓝图中的实例函数
virtual void Deinitialize() override;
UFUNCTION(BlueprintCallable, Category= "ProgrammingSystem")
void PrintHello();
private:
int32 ExampleCounter;
};
#include "MyProgrammingSubsystem.h"
#include "Engine/Engine.h" // 用于 GEngine 和日志
UMyProgrammingSubsystem::UMyProgrammingSubsystem()
: ExampleCounter(0)
{
}
void UMyProgrammingSubsystem::Initialize(FSubsystemCollectionBase& Collection)
{
Super::Initialize(Collection);
ExampleCounter = 1;
UE_LOG(LogTemp, Log, TEXT("MyProgrammingSubsystem Initialized with counter = %d"), ExampleCounter);
}
void UMyProgrammingSubsystem::Deinitialize()
{
UE_LOG(LogTemp, Log, TEXT("MyProgrammingSubsystem Deinitialized"));
Super::Deinitialize();
}
void UMyProgrammingSubsystem::PrintHello()
{
++ExampleCounter;
UE_LOG(LogTemp, Warning, TEXT("Hello from MyProgrammingSubsystem! Counter=%d"), ExampleCounter);
if (GEngine)
{
GEngine->AddOnScreenDebugMessage(-1, 2.0f, FColor::Green, TEXT("Hello from MyProgrammingSubsystem!"));
}
}
看到这里也是不容易,如果觉得对你有帮助的话,麻烦点个免费的赞。
951

被折叠的 条评论
为什么被折叠?



