一.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;
}