CommonLoadingScreen でローディング画面を作る

UE5

LyraStarterGame サンプルに入ってる CommonLoadingScreen プラグインの使い方を調べてみた。

CommonLoadingScreen プラグインは、レベルをまたいだ運用も可能な、ローディング中の画面暗転の機能を持ったプラグインである。

”CommonLoadingScreen” でぐぐるとマーケットプレイスにも同様の名前のプラグインが出てくる。が、これは LyraStarterGame のサンプルにある CommonLoadingScreen を元に拡張したものらしい。今回はマーケットプレイスに出ているものは調査対象にはしない。

ローディング画面の実装といえば、ActionRPG サンプルにも似たようなものはある。

[UE4] Action RPG の Loading Screen を自分のプロジェクトで動かしてみる|株式会社ヒストリア
執筆バージョン: Unreal Engine 4.27 皆さんこんにちは。エンジニアの森です。 今回は、Epic から公開されている「Action RPG」の Loading Screen を見てみます。 UE エディタ上でゲームを Standalone 実行すると、こちらの画面を確認できます この Loading S...

が、ActionRPG の場合は C++ 側で Slate を直接ハードコーディングしているため、任意の Widget を設定することができない。参照しているテクスチャを変える程度なら C++ コードを少しいじれば可能だが、任意に Widget でカスタマイズしたものを使おうと思うと ActionRPG のサンプルでは融通が利かない。

CommonLoadingScreen では、ローディングする際のビジュアルを Widget で指定できる。またそれだけではなく、表示する際の細かいパラメータ設定も行うことできる。

CommonLoadingScreen 導入と設定

CommonLoadingScreen を使うには、LyraStarterGame のプロジェクトから Plugins/CommonLoadingScreen を丸ごと自分のプロジェクトの Plugins 以下にコピーするだけ。
(一応書いとくが Intermediate は中間ファイルだから不要。また、自分でビルドする場合であれば、 Binaries もなくもいい)

CommonLoadingScreen が入ると、プロジェクト設定に Common Loading Screen が追加される。

プロジェクト設定

  • Loading Screen Widget
    • ローディング画面で表示する Widget を設定する。
  • Loading Screen ZOrder
    • ローディング画面で表示する Widget の ZOrder の設定。
    • デフォルト: 10000

Loading Screen Widget には 1つしか Widget をセットすることができない。

一方で、LyraStarterGame プロジェクトでは、場面に応じて複数のローディング画面が表示されているようにみえる。これは Wideget 内に NamedSlot が設置されており、NamedSlot 内に埋め込む Widget を動的に切り替えることによって実装されている。

このあたりの実装は、CommonLoadingScreen プラグインには含まれておらず、LyraStarterGame モジュール内の ULyraLoadingScreenSubsystem という GameInstanceSubsystem が管理している。

以下のプロパティはコンフィグレーション。

  • Hold Loading Screen Additional Secs
    • 主にテクスチャストリーミングによりボケ表示させないための工夫として、ロード完了後にさらに余分にローディング画面を表示しておく。
    • デフォルト: 2.0 sec
    • 以下のコンソールコマンドでも値を変更可能
      • CommonLoadingScreen.HoldLoadingScreenAdditionalSecs
  • Loading Screen Heartbeat Hang Duration
    • ローディング画面表示中の ThreadHeartBeat の設定をすることができる。
    • ThreadHeartBeat は指定した秒数以上時間がかかった場合にメインスレッドがハングしたとみなして強制終了させるための仕組み。
    • デフォルト: 0.0 sec (この状態だと未設定扱い)
  • Log Loading Screen Heartbeat Interval
    • ローディング画面中、ログを表示を行う時間。
    • ゼロだとログ表示なし。
    • デフォルト: 5.0 sec
  • Force Tick Loading Screen Even in Editor
    • ローディング画面を表示する際、FSlateApplication の Tick を強制的に呼び出して、ローディング画面を即時表示させるかどうか。
    • パッケージだとこれが必ず通るようになっているが、エディタ環境であえてオフにすることもできるようになっている。
    • デフォルト: true

以下のプロパティはデバッグ用。

  • Log Loading Screen Reason Every Frame
    • ログ表示を毎フレーム常に表示するかどうか。
    • デフォルト: false
    • 以下のコンソールコマンドでも値を変更可能
      • CommonLoadingScreen.LogLoadingScreenReasonEveryFrame
  • Force Loading Screen Visible
    • 強制的にローディング画面を表示するデバッグ機能。
    • デフォルト: false
    • 以下のコンソールコマンドでも値を変更可能
      • CommonLoadingScreen.AlwaysShow
  • Hold Loading Screen Additional Secs Even in Editor
    • ”Hold Loading Screen Additional Secs” の設定は、エディタ環境において、通常無視されるように実装されている。このプロパティを true にすると、エディタ上でも実際に指定時間分ローディング画面を維持する挙動を確認できる。
    • デフォルト: false

ローディング画面の制御

CommonLoadingScreen のローディング画面制御は大きく2種類が用意されている。

  1. ILoadingProcessInterface を継承したクラスを通して実装
  2. LoadingProcessTask を使用して BP から実装

ILoadingProcessInterface を継承したクラスを通して実装

1.の場合、ILoadingProcessInterface を継承したクラスを作る。

/** Interface for things that might cause loading to happen which requires a loading screen to be displayed */
UINTERFACE(BlueprintType)
class COMMONLOADINGSCREEN_API ULoadingProcessInterface : public UInterface
{
	GENERATED_BODY()
};

class COMMONLOADINGSCREEN_API ILoadingProcessInterface
{
	GENERATED_BODY()

public:
	// Checks to see if this object implements the interface, and if so asks whether or not we should
	// be currently showing a loading screen
	static bool ShouldShowLoadingScreen(UObject* TestObject, FString& OutReason);

	virtual bool ShouldShowLoadingScreen(FString& OutReason) const
	{
		return false;
	}
};

ILoadingProcessInterface には ShouldShowLoadingScreen メソッドがある。このメソッドにローディング画面有無の判定ロジックを組む。
実際に使用する際は、ULoadingScreenManager::RegisterLoadingProcessor で登録しておく。

auto* GameInstance = World ? World->GetGameInstance() : nullptr;
auto* LoadingScreenManager = GameInstance ? GameInstance->GetSubsystem<ULoadingScreenManager>() : nullptr;
LoadingScreenManager->RegisterLoadingProcessor(Instance);

登録されたものから1つでも ShouldShowLoadingScreen が true になるとローディング画面に遷移する。
なお、以下のクラスに ILoadingProcessInterface を実装した場合は、登録せずとも自動で判定される。

  • GameState (および、GameState の Comoponent)
  • PlayerController (および、PlayerController の Component)

この部分の判定に関しては、詳しくは ULoadingScreenManager::CheckForAnyNeedToShowLoadingScreen を参照。

LoadingProcessTask を使う

1.の方法は基本的には C++ で対応が必要な方法だった (ただし、未検証ではあるが、GameState, PlayerController への ILoadingProcessInterface 実装であれば BP だけでも可能かも)

が、C++ コード不要で BP 上だけで気軽に利用可能な方法も用意されている。それが LoadingProcessTask である。

具体的には以下のようにすると、BP でも簡単にローディング画面の切り替えができる。

上記の BP を実際に実行してみる。以下のように、簡単にローディング画面を実装することができた。

上の例は、同一のレベル上で切り替えているが、ULoadingScreenManager 自体は GameInstanceSubsystem なので、レベルの切り替わり中も利用することももちろん可能。

OpenLevel 等でパーシスタントをまたぎたい場合は、例えば GameInstance の BP に以下のような実装を行えばよい。

画像をクリックして拡大

で、こんな感じで呼び出す。

マップのロード早すぎて一瞬だけど、パーシスタントレベルまたぎでも問題なく動作するのを確認。

LoadingProcessTask は、ILoadingProcessInterface の具体的な実装例にもなっている。
プログラマであれば、ULoadingProcessTask のソースコードを読めば、C++ での実装方法もすぐに見当がつくはず。

コメント