概要
SubSysmteについて学習しようかと思いました
基本的に参考サイトを見たものを自分が分かるようにまとめてみただけです
参考
【UE4】Subsystem, GAS, DataAssetを活用して実装した会話システムについて雑多に書いてみた - ぼっちプログラマのメモ
【UE5】Subsystem(サブシステム)を使ってみよう! - main() blog
合わせて読んだページ
UE5/UE4 C++でクラス(Class)を生成する(NewObject) 凛(kagring)のUE5/UE4とゲーム制作と雑記ブログ
DOFI LAB BLOG: UE4: 親のBegin Playなどのイベントを呼び出す
UE4 プログラミングサブシステムを試してみる #UnrealEngine4 - Qiita
内容
おかずさんの記事に載っているslideshareが一番わかりやすいです。
SubSystemとしては色んなものがあるようです。
簡易に作成
ゲーム中常駐しているようなものが欲しいため、GameInstanceSubSystemを継承して作ることにしました。
記事を参考に、「コンストラクタ」「初期化」「破棄」「Tick」「簡易な関数」を用意してみました。
#pragma once
#include "CoreMinimal.h"
#include "Subsystems/GameInstanceSubsystem.h"
#include "MyGameInstanceSubsystem.generated.h"
/**
*
*/
UCLASS()
class UE52ADXV1LE_API UMyGameInstanceSubsystem : public UGameInstanceSubsystem, public FTickableGameObject
{
GENERATED_BODY()
/// コンストラクタ
UMyGameInstanceSubsystem();
/// 初期化
virtual void Initialize(FSubsystemCollectionBase& Collection);
/// 破棄
virtual void Deinitialize();
/// Tickを使用するために必須
virtual TStatId GetStatId() const override
{
RETURN_QUICK_DECLARE_CYCLE_STAT(UMyGameInstanceSubsystem, STATGROUP_Tickables);
}
/// Tick
/// Tickを使用するためにはFTickableGameObjectを継承する必要がある
virtual void Tick(float DeltaTime) override;
UFUNCTION(BlueprintCallable, BlueprintPure)
int32 GetNumber() { return Number; }
private:
int32 Number = 0;
};
#include "MyGameInstanceSubsystem.h" UMyGameInstanceSubsystem::UMyGameInstanceSubsystem() { } void UMyGameInstanceSubsystem::Initialize(FSubsystemCollectionBase& Collection) { Super::Initialize(Collection); } void UMyGameInstanceSubsystem::Deinitialize() { Super::Deinitialize(); } void UMyGameInstanceSubsystem::Tick(float DeltaTime) { }
これだけで基本要素は出来上がり、下記のような形でどこからでも呼び出せるようになるようです。
c++側では以下の形で呼び出せそうです。
適当なアクターで呼び出してみました。
#include "MyActor.h"
#include "MyGameInstanceSubsystem.h"
void AMyActor::BeginPlay()
{
Super::BeginPlay();
UGameInstance* GameInst = GetWorld()->GetGameInstance();
if (!IsValid(GameInst))
{
return;
}
auto MySub = GameInst->GetSubsystem< UMyGameInstanceSubsystem>();
if (!IsValid(MySub))
{
return;
}
UE_LOG(LogTemp, Log, TEXT("UMyObject::BeginPlay: %d"), MySub->GetNumber());
}
処理をSubsystem内でなく外で行う
Subsystemに処理を書けばいいのですが、処理を外で行う方法を試してみます。
色んな人がHelperという名前でこういうことをやってたみたいです。
同じ命名規約をしていたため、そういうクラスがあるのかと思ってましたが、ただのUObjectです。
#pragma once
#include "CoreMinimal.h"
#include "UObject/NoExportTypes.h"
#include "MyGameInstanceSubsystemHelper.generated.h"
/**
*
*/
UCLASS()
class UE52ADXV1LE_API UMyGameInstanceSubsystemHelper : public UObject
{
GENERATED_BODY()
UMyGameInstanceSubsystemHelper();
public:
void Show();
};
#include "MyGameInstanceSubsystemHelper.h"
UMyGameInstanceSubsystemHelper::UMyGameInstanceSubsystemHelper()
{
}
void UMyGameInstanceSubsystemHelper::Show()
{
UE_LOG(LogTemp, Log, TEXT("UMyGameInstanceSubsystemHelper::Show()"));
}
作成したクラスをSubsystem側で使用できるように作成します。
#pragma once
#include "CoreMinimal.h"
#include "Subsystems/GameInstanceSubsystem.h"
#include "MyGameInstanceSubsystemHelper.h"
#include "Tickable.h"
#include "MyGameInstanceSubsystem.generated.h"
/**
*
*/
UCLASS()
class UE52ADXV1LE_API UMyGameInstanceSubsystem : public UGameInstanceSubsystem, public FTickableGameObject
{
GENERATED_BODY()
/// コンストラクタ
UMyGameInstanceSubsystem();
/// 初期化
virtual void Initialize(FSubsystemCollectionBase& Collection);
/// 破棄
virtual void Deinitialize();
/// Tickを使用するために必須
virtual TStatId GetStatId() const override
{
RETURN_QUICK_DECLARE_CYCLE_STAT(UMyGameInstanceSubsystem, STATGROUP_Tickables);
}
/// Tick
/// Tickを使用するためにはFTickableGameObjectを継承する必要がある
virtual void Tick(float DeltaTime) override;
public:
UFUNCTION(BlueprintCallable, BlueprintPure)
int32 GetNumber() { return Number; }
UPROPERTY(Transient, BlueprintReadOnly)
TObjectPtr< UMyGameInstanceSubsystemHelper> SubsystemHelper;
private:
int32 Number = 0;
};
#include "MyGameInstanceSubsystem.h"
UMyGameInstanceSubsystem::UMyGameInstanceSubsystem()
{
}
void UMyGameInstanceSubsystem::Initialize(FSubsystemCollectionBase& Collection)
{
Super::Initialize(Collection);
SubsystemHelper = nullptr;
SubsystemHelper = NewObject< UMyGameInstanceSubsystemHelper>();
if (IsValid(SubsystemHelper))
{
SubsystemHelper->Show();
}
}
void UMyGameInstanceSubsystem::Deinitialize()
{
Super::Deinitialize();
SubsystemHelper = nullptr;
}
void UMyGameInstanceSubsystem::Tick(float DeltaTime)
{
}
実際にゲームをプレイしてログを確認すると、UE_LOGのログが出ていることが確認できます。つまり、問題なく作成されていることが確認できます。
これで処理を分けられます。
Subsystem内の関数ではなく、Helperの関数を呼び出して行うこともできます。
BPで作成した関数を利用する
Subsystemが常駐されるものです。
基本的には、Subsystemに対して何かを処理を行うものかと思います。
ですが、それではBP側で対応ができないです。
以下では、Subsystemに処理を加えて何かをするのではなく、SubsystemHelperのBPで処理を加えて行う方式です。
このやり方なら、c++側とBP側で命名かぶらなければ自由に追加し放題です。
#pragma once
#include "CoreMinimal.h"
#include "UObject/NoExportTypes.h"
#include "MyGameInstanceSubsystemHelper.generated.h"
/**
*
*/
UCLASS(Abstract, Blueprintable, meta = (ShowWorldContextPin))
class UE52ADXV1LE_API UMyGameInstanceSubsystemHelper : public UObject
{
GENERATED_BODY()
UMyGameInstanceSubsystemHelper();
public:
UFUNCTION(BlueprintNativeEvent, BlueprintCallable)
void Initialize();
virtual void Initialize_Implementation();
UFUNCTION(BlueprintCallable)
void Show();
};
#include "MyGameInstanceSubsystemHelper.h"
UMyGameInstanceSubsystemHelper::UMyGameInstanceSubsystemHelper()
{
}
void UMyGameInstanceSubsystemHelper::Initialize_Implementation()
{
UE_LOG(LogTemp, Log, TEXT("UMyGameInstanceSubsystemHelper::Initialize_Implementation()"));
}
void UMyGameInstanceSubsystemHelper::Show()
{
UE_LOG(LogTemp, Log, TEXT("UMyGameInstanceSubsystemHelper::Show()"));
}
Subsystem側でHelperのBPを所持するようにします。
#pragma once
#include "CoreMinimal.h"
#include "Subsystems/GameInstanceSubsystem.h"
#include "MyGameInstanceSubsystemHelper.h"
#include "Tickable.h"
#include "MyGameInstanceSubsystem.generated.h"
/**
*
*/
UCLASS()
class UE52ADXV1LE_API UMyGameInstanceSubsystem : public UGameInstanceSubsystem, public FTickableGameObject
{
GENERATED_BODY()
/// コンストラクタ
UMyGameInstanceSubsystem();
/// 初期化
virtual void Initialize(FSubsystemCollectionBase& Collection);
/// 破棄
virtual void Deinitialize();
/// Tickを使用するために必須
virtual TStatId GetStatId() const override
{
RETURN_QUICK_DECLARE_CYCLE_STAT(UMyGameInstanceSubsystem, STATGROUP_Tickables);
}
/// Tick
/// Tickを使用するためにはFTickableGameObjectを継承する必要がある
virtual void Tick(float DeltaTime) override;
public:
UFUNCTION(BlueprintCallable, BlueprintPure)
int32 GetNumber() { return Number; }
UPROPERTY(Transient, BlueprintReadOnly)
TObjectPtr< UMyGameInstanceSubsystemHelper> SubsystemHelper;
private:
TSubclassOf< class UMyGameInstanceSubsystemHelper> SubsystemHelperClass;
int32 Number = 0;
};
#include "MyGameInstanceSubsystem.h"
UMyGameInstanceSubsystem::UMyGameInstanceSubsystem()
{
static ConstructorHelpers::FClassFinder< UMyGameInstanceSubsystemHelper> BluePrintFile(TEXT("/Game/BP_MyGameInstanceSubsystemHelper"));
if (BluePrintFile.Class) {
SubsystemHelperClass = (UClass*)BluePrintFile.Class;
}
}
void UMyGameInstanceSubsystem::Initialize(FSubsystemCollectionBase& Collection)
{
Super::Initialize(Collection);
SubsystemHelper = nullptr;
if (SubsystemHelperClass)
{
SubsystemHelper = NewObject< UMyGameInstanceSubsystemHelper>(GetTransientPackage(), SubsystemHelperClass);
}
if (IsValid(SubsystemHelper))
{
SubsystemHelper->Initialize();
}
}
void UMyGameInstanceSubsystem::Deinitialize()
{
Super::Deinitialize();
SubsystemHelper = nullptr;
}
void UMyGameInstanceSubsystem::Tick(float DeltaTime)
{
}
BPを作成させます。ファイル検索の都合上で名前は決まってます。
試しにc++からオーバーライドした関数と、BPで関数を用意してみます。
ゲームをプレイでInitializeが呼ばれることが確認できます。
また、BPで作った関数も呼び出せます。