1、什么是 UGameInstanceSubsystem
UGameInstanceSubsystem
是一个可以附加到 UGameInstance
上的对象,它的生命周期和 UGameInstance
一样长。这意味着它在游戏启动时创建,并在整个游戏生命周期内保持存在,直到游戏结束。它在任何需要的地方都可以方便地被调用,比如在任何游戏对象、玩家控制器、关卡蓝图等地方都可获取并调用子系统的方法。
优点:
-
全局访问:由于
UGameInstanceSubsystem
的生命周期与UGameInstance
一样长,因此可以在整个游戏中随时访问。这使得它非常适合用于管理全局状态或服务,例如:- 游戏设置和配置
- 全局事件管理
- 网络会话管理
- 存档和加载系统
-
模块化设计:子系统提供了一种模块化的方式来分离不同的功能。这有助于保持代码的整洁和可维护性,因为每个子系统可以专注于特定的任务或服务。
-
生命周期管理:子系统的生命周期由引擎自动管理,你不需要手动创建或销毁它们。
-
依赖注入:子系统可以通过依赖注入的方式被其他类访问,从而简化了依赖管理。
注意事项:
- 初始化时机问题,
UGameInstanceSubsystem
的初始化非常早(比BeginPlay
早) GameInstanceSubsystem
对象的生命周期,正常来说他应该是游戏启动就生成,游戏结束就销毁,但是实践中发现在编辑器中结束播放时,只是调用了deinitialize()
,没有销毁该游戏实例子系统的对象。
2、如何创建和使用
2.1 创建子系统类
首先,创建一个新的 C++ 类,继承自 UGameInstanceSubsystem
:
// MyGameInstanceSubsystem.h
#pragma once
#include "CoreMinimal.h"
#include "Subsystems/GameInstanceSubsystem.h"
#include "MyGameInstanceSubsystem.generated.h"
UCLASS()
class MYPROJECT_API UMyGameInstanceSubsystem : public UGameInstanceSubsystem
{
GENERATED_BODY()
public:
virtual void Initialize(FSubsystemCollectionBase& Collection) override;
virtual void Deinitialize() override;
// 可以蓝图调用,也可以C++里调
UFUNCTION(BlueprintCallable, Category = "UMyGameInstanceSubsystem ")
void DoSomething();
private:
// 内部状态或资源
};
// MyGameInstanceSubsystem.cpp
#include "MyGameInstanceSubsystem.h"
#include "Engine/Engine.h"
void UMyGameInstanceSubsystem::Initialize(FSubsystemCollectionBase& Collection)
{
Super::Initialize(Collection);
UE_LOG(LogTemp, Warning, TEXT("Initialized"));
}
void UMyGameInstanceSubsystem::Deinitialize()
{
UE_LOG(LogTemp, Warning, TEXT("Deinitialized"));
Super::Deinitialize();
}
void UMyGameInstanceSubsystem::DoSomething()
{
UE_LOG(LogTemp, Warning, TEXT("doing something"));
}
2.2 使用
可以在任何需要的地方访问和使用 UGameInstanceSubsystem
,它的效果类似于全局变量:
- 获取自定义子系统对象的地址
- 调用其接口
获取子系统实例化对象的方法
- 通过World:
UWorld* World = GetWorld(); UMyGameInstanceSubsystem* MySubsystem = World->GetGameInstance()->GetSubsystem<UMyGameInstanceSubsystem>(); MySubSystem->DoSomething();
- 通过GEngine
当GetWorld()
返回nullptr或者没有这个函数时,还可以通过访问全局变量GEngine
获取当前运行的World
World = GEngine->GetCurrentPlayWorld(); UMyGameInstanceSubsystem* MySubsystem = World->GetGameInstance()->GetSubsystem<UMyGameInstanceSubsystem>(); MySubSystem->DoSomething();
- 通过PlayerController:
如果有玩家控制器的引用也可以通过控制器获取// 别的方法获取控制器也是可以的 APlayerController* PlayerController = GetWorld()->GetFirstPlayerController(); UMyGameInstanceSubsystem* MySubsystem = PlayerController->GetGameInstance()->GetSubsystem<UMyGameInstanceSubsystem>();
- UObject的方法:
在任何UObject
的派生类中,可以直接使用GetGameInstance()
方法直接访问GameInstance
并获取子系统。UMyGameInstanceSubsystem* MySubsystem = GetGameInstance()->GetSubsystem<UMyGameInstanceSubsystem>();
3、子系统是怎么工作的?
在UE中,UGameInstanceSubsystem
是一个特殊的类,它的存在是为了给游戏实例提供模块化的服务。
当创建一个 UGameInstanceSubsystem
的子类时,UE会在游戏运行时自动处理这个子类的实例化和注册过程。
- 自动注册机制:
UGameInstanceSubsystem
的子类通常会通过引擎的反射系统自动注册。这是因为GameInstance
在启动时会自动查找所有继承自UGameInstanceSubsystem
的类,并创建它们的实例。 - 引擎初始化:当游戏启动时,引擎会初始化
GameInstance
,并且在这个过程中,它会创建并初始化所有注册的子系统。由于自定义的类继承自UGameInstanceSubsystem
,它会被视为一个需要被初始化的子系统。 - 反射和工厂:UE使用反射来在运行时识别和实例化对象。当使用 获取子系统的接口时,引擎会利用它的反射系统来查找和创建
UMyGameInstanceSubsystem
类的实例。 - 默认的单例模式:
UGameInstanceSubsystem
及其子类通常是作为单例实现的。这意味着无论在哪里调用GetEngineSubsystem
,它都会返回同一个实例。