関連ブログ
- [UE4][UE5]開発環境の容量を少しでも減らす 2024.08.14UE
- [UE5] PushModel型のReplicationを使い、ネットワーク最適化を図る 2024.05.29UE
- [UE5]マテリアルでメッシュをスケールする方法 2024.01.17UE
CATEGORY
2022.05.18UE4UE/ C++
執筆バージョン: Unreal Engine 4.27 |
みなさん、こんにちは。
今回はWorldSubsystemの生成に関して、便利な機能を解説していきたいと思います。
皆さんはSubsystemを活用していますか?
USubsystemクラスを派生させて新しくクラスを作るだけで、自動的にインスタンスが生成・破棄される便利な機能です。
こちらはUE4.27のSubsystemに関するドキュメントになります。
https://docs.unrealengine.com/4.27/ja/ProgrammingAndScripting/Subsystems/
基底クラスのSubsystemによって、そのSubsystemクラスはインスタンスが生成・破棄されるタイミングが異なるわけですが、今回はWorldSubsystemについて解説します。
UWorldSubsystemを基底クラスにしてSubsystemクラスを作成した場合、そのクラスはWorldのライフタイムに合わせて生成・破棄される事になります。つまり、OpenLevelなどによってパーシスタントレベルが遷移する際に、遷移前のWorldSubsystemのインスタンスは破棄され、遷移後に新しくインスタンスが生成されるというわけです。
例えばステージ制のアクションゲームで、ステージごとにパーシスタントレベルを切り替える設計だった場合、ステージごとに自動で生成・破棄されるクラスが簡単に作れるというわけです。
ステージ内にいる敵だったりアイテムだったりをまとめて管理するクラスを用意した時に、Actorクラスであればわざわざそれぞれのステージに配置したり、Objectクラスであればステージを切り替えた際に生成する処理を呼び出す必要があります。
Subsystemではそれらの手間が無くなるわけですし、配置や呼び出し忘れのミスもなくなり、とても便利だという事がわかります。
ただ、一つだけ懸念点があります。
このままだと全てのパーシスタントレベルでインスタンスが生成されるため、例えばそのクラスが必要のないレベル、先ほど例に挙げたゲームだとタイトル画面用のレベルでも敵やアイテムを管理するクラスが生成される事になります。
問題自体は無いとは思いますが、絶対に必要のないクラスを生成するのは気持ちが悪いものですよね?
可能であれば特定のパーシスタントレベルでは生成されないようにしたいものです。
というわけで、特定のパーシスタントレベルでは指定したWorldSubsystemを生成しない仕組みを作っていこうと思います。
まずはWorldSubsystemを基底としたSubsystemクラスを新規作成します。
今回は解説のためNpcManagerとEnemyManagerの2つを用意しました。
ヘッダーファイルを以下の通りにします。どちらのクラスもほぼ同じ内容になります。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
#include "CoreMinimal.h" #include "Subsystems/WorldSubsystem.h" #include "NpcManager.generated.h" UCLASS() class TESTPROJECT_API UNpcManager : public UWorldSubsystem { GENERATED_BODY() public: UNpcManager(); virtual bool ShouldCreateSubsystem(UObject* Outer) const override; virtual void Initialize(FSubsystemCollectionBase& Collection) override; virtual void Deinitialize() override; }; |
cppファイルは一旦このようにします。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 |
#include "MyWorldSettings.h" UNpcManager::UNpcManager() { } bool UNpcManager::ShouldCreateSubsystem(UObject* Outer) const { return true; } void UNpcManager::Initialize(FSubsystemCollectionBase& Collection) { Super::Initialize(Collection); if (GEngine) { GEngine->AddOnScreenDebugMessage(-1, 60.0f, FColor::Blue, TEXT("UNpcManager::Initialize!!!")); } } void UNpcManager::Deinitialize() { Super::Deinitialize(); if (GEngine) { GEngine->AddOnScreenDebugMessage(-1, 60.0f, FColor::Blue, TEXT("UNpcManager::Deinitialize!!!")); } } |
今回重要なのはShouldCreateSubsystem関数です。中身にこのクラスが生成される条件を書き込めば、必要なパーシスタントレベルでしか生成されないというわけです。
ただ、中身の実装はこの時点ではまだ出来ないため、とりあえず必ずTrueを返すようにしておきます。
次に、これらのクラスを生成させる条件ですが、今回はワールド設定を使う事にします。ワールド設定に生成を行うかどうか設定できるようにする事で、タイトル画面では生成しないといった対応が簡単に可能になるというわけです。
まずはプロジェクト固有のワールド設定を用意します。
ヘッダーファイルを以下の通りにします。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
#pragma once #include "CoreMinimal.h" #include "GameFramework/WorldSettings.h" #include "MyWorldSettings.generated.h" UCLASS() class TESTPROJECT_API AMyWorldSettings : public AWorldSettings { GENERATED_BODY() public: UPROPERTY(EditAnywhere) bool bUseNpcManager = true; UPROPERTY(EditAnywhere) bool bUseEnemyManager = true; }; |
WorldSettingの実装は以上です。NpcManager、EnemyManagerのそれぞれで生成を行うかどうかのフラグを追加しています。デフォルト値はTrueになるように初期値を指定しておきます。
最後に、用意したWorldSubsystem(今回であればNpcManager)の、ShouldCreateSubsystem関数をこのように変更します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
bool UNpcManager::ShouldCreateSubsystem(UObject* Outer) const { if (!Super::ShouldCreateSubsystem(Outer)) { return false; } if (UWorld* WorldOuter = Cast<UWorld>(Outer)) { if (AMyWorldSettings* MyWorldSettings = Cast<AMyWorldSettings>(WorldOuter->GetWorldSettings())) { return MyWorldSettings->bUseNpcManager; } } return false; } |
ワールド設定のフラグを見て、生成するかどうかの結果をboolで返すというわけです。
これでC++側の準備は完了しました。
次にエディタを起動して、プロジェクト設定を開き、デフォルトのワールド設定をプロジェクト固有のものに変更します。
次に設定を変更したいパーシスタントレベルを開き、ワールド設定を表示します。
UseNpcManagerとUseEnemyManagerのデフォルト値はTrueになっているので、それぞれにチェックマークが入っています。
この状態でプレイ実行しみると、それぞれのインスタンスが生成されている事が確認できます。
次にUseNpcManagerのチェックマークを外してプレイ実行してみます。するとEnemyManagerのみが生成されている事が確認できます。
同様にUseEnemyManagerのチェックマークを外すと、NpcManagerのみが生成されます。
今度はEnemyManagerのみが生成されるパーシスタントレベルから、NpcManagerのみが生成されるパーシスタントレベルへ移動してみます。
するとEnemyManagerが生成、レベル遷移後には破棄されて、NpcManagerが生成されているのが確認できました。
これで必要なパーシスタントレベルにだけ特定のWorldSubsystemが生成できるようになりました。
ぜひ活用して見て下さい!