一.Subsystem先做一个简单的介绍,其实可以去看大钊的文章有一篇专门讲这个的。
GamePlay框架基础上的一个增强功能,属于GamePlay架构的范围。Subsystems是一套可以定义自动实例化和释放的类的框架。这个框架允许你从5类里选择一个来定义子类(只能在C++定义):
有点像“全局变量”,也有点像是静态蓝图函数(如GetGameInstance),可以方便的在蓝图的各个地方调用。Subsystems真正的威力其实远不止这点手头上的便利,而在于接下来要谈的引擎帮你自动处理的部分。说实话,我还没理解到这个层次上
Subsystem对象的生命周期取决于其依存的Outer对象的生命周期,随着Outer对象的创建而创建,随着Outer对象的销毁而销毁。而选择依存哪种Outer对象,就是选择哪种Subsystem生命周期,靠的就是选择继承于哪个Subsystem基类。
二.简单创建一个单例。
重写父类的父类USubsystem的这三个函数:
virtual bool ShouldCreateSubsystem(UObject* Outer) const override;/** Implement this for initialization of instances of the system */virtual void Initialize(FSubsystemCollectionBase& Collection) override;/** Implement this for deinitialization of instances of the system */virtual void Deinitialize() override;
以此来看它的生命周期
bool UMyGameInstanceSubsystem::ShouldCreateSubsystem(UObject* Outer) const
{return true;
}void UMyGameInstanceSubsystem::Initialize(FSubsystemCollectionBase& Collection)
{Super::Initialize(Collection);UE_LOG(LogTemp,Warning,TEXT("Initialize"));//myRunnable = Ins = this;
}void UMyGameInstanceSubsystem::Deinitialize()
{Super::Deinitialize();UE_LOG(LogTemp, Warning, TEXT("Deinitialize"));
}
接着重点来了,定义成单例。经典的单例写法,一个static指针,一个staic获得自己的函数。
public:static UMyGameInstanceSubsystem* Ins; //static 不能加反射UFUNCTION(BlueprintCallable,Category = "MyGameInstanceSubsystem")static UMyGameInstanceSubsystem* Get();
这个指针,在CPP里面,开头需要初始化空。不然会报错,因为static的指针在类对象生成之前。
然后,在这个线程初始化完成后,将这个Ins 指针指向自己。
void UMyGameInstanceSubsystem::Initialize(FSubsystemCollectionBase& Collection)
{Super::Initialize(Collection);UE_LOG(LogTemp,Warning,TEXT("Initialize"));//myRunnable = Ins = this;
}
在Get()函数里,返回Ins这种指向的单例子系统。这样UFUNCTION蓝图,和C++里都能很方便获得。
UMyGameInstanceSubsystem* UMyGameInstanceSubsystem::Get()
{UE_LOG(LogTemp, Warning, TEXT("Get Subsystem"));return Ins;
}
三.在单例里面,写线程
1.先把线程类准备好,不继承UE的类,头文件如下。我觉得可以暂时理解为线程的生命周期,是由代码确定的,而不是直接和UE的其它类保持相同。
#include "CoreMinimal.h"
#include "HAL/Runnable.h"
#include "HAL/ThreadSafeBool.h" //线程安全
创建一个空的类,在类里面继承FRunnable。类的名字也需要改为F开头。童谣需要重写FRunnable的四个函数。
class MYPROJECT_API FMyRunable:public FRunnable //
{
public:FMyRunable();~FMyRunable();FMyRunable(FString InThreadName);virtual bool Init()override;virtual uint32 Run() override;virtual void Stop() override;virtual void Exit() override;FString ThreadName;
private:bool IsRunning;
};
2.线程的实现如下
简单的输出日志
UFUNCTION(BlueprintCallable, Category = "MyGameInstanceSubsystem")void StartThread();UFUNCTION(BlueprintCallable, Category = "MyGameInstanceSubsystem")void StopThread();
FMyRunable::FMyRunable()
{UE_LOG(LogTemp, Warning, TEXT("gouzaohanshu"));
}FMyRunable::~FMyRunable()
{UE_LOG(LogTemp, Warning, TEXT("xigouhanshu"));
}FMyRunable::FMyRunable(FString InThreadName):ThreadName(InThreadName)
{UE_LOG(LogTemp, Warning, TEXT("gouzaohanshu1"));
}bool FMyRunable::Init()
{return true;
}uint32 FMyRunable::Run()
{IsRunning = true;return uint32();
}void FMyRunable::Stop()
{IsRunning = false;
}void FMyRunable::Exit()
{UE_LOG(LogTemp, Warning, TEXT("Exit Thread"));
}
3.接着需要将子系统和线程联系起来
在GameInstance里面声明
//TSharedPtr<MyRunable> ref;
protected:TSharedPtr<FMyRunable> myRunnable;FRunnableThread* MyRunnableThread;
在GameInstance里需要实现,开启线程。这里首先创建一个myRunable对象,其次再开启线程,并关联到这个对象。这个时候,线程对象会自动开始跑自己的Run函数,因为它被开启了。
void UMyGameInstanceSubsystem::StartThread()
{myRunnable = MakeShared<FMyRunable>(TEXT("MyRunnable")); //创建指针MyRunnableThread = FRunnableThread::Create(myRunnable.Get(),*(myRunnable->ThreadName)); //创建线程程UE_LOG(LogTemp, Warning, TEXT("Start Thread"));
}
现在我们添加myRunable里的Run的内容,让他循环输出,每三秒一次
uint32 FMyRunable::Run()
{IsRunning = true;while (IsRunning){FPlatformProcess::Sleep(3.0);UMyGameInstanceSubsystem* Instance = UMyGameInstanceSubsystem::Get();if (Instance){UE_LOG(LogTemp, Warning, TEXT("Run Thread"));}}return uint32();
}
如果想让它停止,就在GameInstanceSubsystem里实现。
void UMyGameInstanceSubsystem::StopThread()
{if(myRunnable.IsValid()){myRunnable->Stop();}UE_LOG(LogTemp, Warning, TEXT("Stop Thread"));
}
void FMyRunable::Stop()
{IsRunning = false;
}