main() blog

プログラムやゲーム、旅、愛する家族について綴っていきます。

【UE5】Subsystem(サブシステム)を使ってみよう!

概要

特定のタイミングで管理(生成/破棄)されるインスタンスを作る仕組みです。
Enigne、Editor、GameInstance、World、LocalPlayerと同じライフサイクルで管理が可能です。
〇〇Managerを作りたい!という時に有用な機能です。

基底クラスによってランタイム(ゲーム)、ワールド(エディタ/ランタイム)、エディタなどで使用できるSubsystemを指定できます。

UGameInstanceSubsystem
UWorldSubsystem
ULocalPlayerSubsystem
UEngineSubsystem
UEditorSubsystem

プロジェクト毎にシングルトンを用意して組み込むことでも可能ですが、初期化のタイミングやアクセス方法など実装方法がまちまちになってしまいます。
UEで用意されている機能で、同等の仕組みが実装できるようになります。

※本記事はC++での開発を前提としています。

環境

UnrealEngine 5.4.2

GameInstanceSubSystem

ゲーム起動時に生成され、終了時に破棄されます。
ゲーム中のマネージャー系クラスとして使用できるとおもいます。

UGameInstanceSubsystemを継承します。

#pragma once

#include "CoreMinimal.h"
#include "Subsystems/GameInstanceSubsystem.h"
#include "MyGameInstanceSubsystem.generated.h"

/**
 * 
 */
UCLASS()
class HOGE_API UMyGameInstanceSubsystem : public UGameInstanceSubsystem
{
    GENERATED_BODY()

public:
    virtual void Initialize(FSubsystemCollectionBase& Collection);

    virtual void Deinitialize();
};
#include "MyGameInstanceSubsystem.h"

void UMyGameInstanceSubsystem::Initialize(FSubsystemCollectionBase& Collection)
{
    UE_LOG(LogTemp, Log, TEXT("UMyGameInstanceSubsystem::Initialize()"));

    Super::Initialize(Collection);
}

void UMyGameInstanceSubsystem::Deinitialize()
{
    UE_LOG(LogTemp, Log, TEXT("UMyGameInstanceSubsystem::Deinitialize()"));

    Super::Deinitialize();
}

テストレベルなど起動してゲームを起動、終了することでGameInstanceSubsystemの初期化、初期化解除が呼ばれていることが確認できます。

Tick処理

SubsystemにTick処理を実装するには、FTickableGameObject か FTickableEditorObject を継承することで実装できます。

#pragma once

#include "CoreMinimal.h"
#include "Subsystems/GameInstanceSubsystem.h"
#include "Tickable.h"
#include "MyGameInstanceSubsystem.generated.h"

/**
 * 
 */
UCLASS()
class HOGE_API UMyGameInstanceSubsystem : public UGameInstanceSubsystem, public FTickableGameObject
{
    GENERATED_BODY()

public:
    virtual void Initialize(FSubsystemCollectionBase& Collection);

    virtual void Deinitialize();

    virtual bool IsTickable() const override
    {
        return bIsInitialized;
    }

    virtual TStatId GetStatId() const override
    {
        RETURN_QUICK_DECLARE_CYCLE_STAT(UMyGameInstanceSubsystem, STATGROUP_Tickables);
    }

    virtual void Tick(float DeltaTime) override;

private:
    bool bIsInitialized = false;
};
#include "MyGameInstanceSubsystem.h"

void UMyGameInstanceSubsystem::Initialize(FSubsystemCollectionBase& Collection)
{
    UE_LOG(LogTemp, Log, TEXT("UMyGameInstanceSubsystem::Initialize()"));

    Super::Initialize(Collection);

    bIsInitialized = true;
}

void UMyGameInstanceSubsystem::Deinitialize()
{
    UE_LOG(LogTemp, Log, TEXT("UMyGameInstanceSubsystem::Deinitialize()"));

    Super::Deinitialize();

    bIsInitialized = false;
}

void UMyGameInstanceSubsystem::Tick(float DeltaTime)
{
    if (!bIsInitialized)
    {
        return;
    }

    UE_LOG(LogTemp, Log, TEXT("UMyGameInstanceSubsystem::Tick() : [%f]"), DeltaTime);
}

テストレベルなどを起動してTickが呼ばれていることが確認できます。

Subsystemの初期化(依存)

GameInstanceSubsystemなどの同じSubsytem内で初期化の順序の指定方法です。
SubsytemAより先にSubsytemBを初期化したいケースで使用します。
FSubsystemCollectionBaseのInitializeDependencyで依存をしていることで初期化の順番を制御できるようなります。

void UMyGameInstanceSubsystemA::Initialize(FSubsystemCollectionBase& Collection)
{
    Collection.InitializeDependency(UMyGameInstanceSubsystemB::StaticClass());
}

参考

docs.unrealengine.com

www.docswell.com

pafuhana1213.hatenablog.com