UE教程|编程子系统

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

编程子系统

虚幻引擎编程子系统概览。

在这里插入图片描述

虚幻引擎(UE) 中的子系统是生命周期受控的自动实例化类。这些类提供了易用的扩展点,程序员可直接获得蓝图和Python公开,同时避免繁复的引擎类修改或覆盖。

当前支持的子系统生命周期包括:

子系统继承自
引擎UEngineSubsystem
编辑器UEditorSubsystem
游戏实例UGameInstanceSubsystem
本地玩家ULocalPlayerSubsystem

举例而言,如果创建了一个派生自此基类的类:

	class UMyGamesSubsystem : public UGameInstanceSubsystem
复制完整片段

将出现以下结果:

  1. 创建 UGameInstance 之后,还会创建一个名为 UMyGamesSubsystem 的实例。
  2. UGameInstance 初始化时,将在子系统上调用 Initialize()
  3. UGameInstance 关闭时,将在子系统上调用 Deinitialize()
  4. 此时将放弃对子系统的引用,如果不再有对子系统的引用,则其将被垃圾回收。

使用子系统的原因

使用编程子系统有以下几个原因:

  • 子系统可节省编程时间。
  • 子系统使您无需覆盖引擎类。
  • 子系统使您无需再已经很复杂的类上添加更多API。
  • 子系统使您能通过用户友好的类型化节点来访问蓝图。
  • 子系统允许访问Python脚本来编写编辑器脚本或编写测试代码。
  • 子系统在代码库中提供模块化和一致性。

子系统在创建插件时尤为实用。您不需要代码相关的说明即可让插件工作。用户只需将插件添加到游戏中,就可以确切了解插件将在何时被实例化和初始化。因此,您可以专注于UE4中提供的API和功能的使用方式。

用蓝图访问子系统

子系统将自动公开到蓝图,智能节点理解情境,且不需要强制转换。您可以使用标准的 UFUNCTION() 标记和规则来控制蓝图可以使用哪些API。

右键点击蓝图图表来显示快捷菜单并搜索"子系统",将出现类似于下图的内容。这里有每个主要类型的类目,以及每个特定子系统的单个条目。 img

如果从上添加节点,将得到如下结果。 img

用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
	};

subsystems

01

子系统是用来做什么的

于开发非游戏进程的工具或程序(如命令行工具、资源处理器、批量导出器等)。

它不依赖 UE 的游戏核心模块(如 EngineCoreUObject),仅基于轻量级的 Core 模块运行,因此能脱离编辑器 / 游戏进程独立启动

误解 1:将编程子系统与 “游戏子系统(Game Subsystem)” 混淆

  • 错误表现:试图在编程子系统中使用 AActorUWorld 等游戏模块类,导致编译失败或运行崩溃。

  • 原因:编程子系统基于 Core 模块,不依赖游戏运行时;而游戏子系统(如 UGameInstanceSubsystem)依赖 Engine 模块,仅在游戏进程中生效。

  • 避免方法

    • 明确需求:若需处理游戏内逻辑(如玩家数据),用游戏子系统;若需开发独立工具(如资源批量处理),用编程子系统
    • 检查模块依赖:编程子系统的 .Build.cs 中仅保留 Core 依赖,删除 EngineCoreUObject 等游戏相关模块。

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!"));
    }
}

看到这里也是不容易,如果觉得对你有帮助的话,麻烦点个免费的赞。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值