改訂バージョン: Unreal Engine 4.19 |
今回はレベルの読み込み回りについてのお話をしたいと思います。
話を進める上で、少し内容が混乱してしまうため、便宜的に単語を定義付けたいと思います。
レベル |
UE4で拡張子がumapになっているアセットのこと |
マップ |
パーシスタントレベルと複数のサブレベルで構成されるレベルのこと |
UE4にはレベルを複数に分ける機能があります。
もちろんレベルを分けずに、親レベルであるパーシスタントレベルに全てのアクターを配置することも可能ですが、レベル分けを活用することで、よりゲームを作りやすくすることが可能です。
レベルを分ける
レベルを分ける事によるメリットは様々ですが、敢えて挙げるとすれば以下のような内容ではないでしょうか。
- マップを切り替える事なく、レベルの状況を切り替える事が出来る
(例:アクションゲームを作る際、ステージごとにレベルを分けておくと、ステージクリアした時に次のステージにすぐ切り替えられる)
- 作業者単位でレベルを分けることで、同時に一つのマップを編集できる
(例:RPGなどで背景用のレベルとギミック用のレベルをわけておくと、背景担当とギミック担当で同時に作業をすすめる事ができる)
- 一時的に配置したいアクターなど、用途によって分けることで管理が楽
(例:デバッグ用レベルを作ることで、リリース時にそのレベルだけを破棄すれば楽に完成版からデバッグ機能を無くす事ができる)
さて、実際にレベル分けを行った場合、次に必要になってくるのはレベルの読み込みタイミングの管理です。
そこでレベルの追加方法を確認しながら解説していきたいと思います。
まず、レベルウィンドウのレベルメニューから「新規作成」を選択肢し、新しいレベルを追加します。

この時追加した新規レベルは、そのまま実行しても読み込まれません。
これはレベルのデフォルトのストリーミング方法がブループリント制御になっているからです。

デフォルトのストリーミング方法
ブループリント |
ブループリントによって制御します |
常にロード済み |
パーシスタントレベルを読み込んだ際、自動的に一緒に読み込まれます
読み込み方法は同期読み込みで、このレベルの読み込み及び表示が完了するまで、次のサブレベルの読み込みは発生しません |
レベルを読み込むタイミングを決める
今回はブループリントによる制御で話を進めます。では、ブループリント制御にしたい場合はどんな時でしょうか。
- マップ遷移直後に該当レベルを非同期で読み込みたい場合
すぐに必要のないレベルなどは非同期読み込みにすることで、マップ遷移時のロード時間を早める事が期待できます。
- 任意のタイミングでレベルを読み込みたい場合
マップ遷移後すぐには必要ありませんが、ある特定のタイミングでレベルが必要になった時に読み込みたい場合です。
レベルの使用意図に合わせてこれらを使い分けます。
ちなみに1のパーシスタントレベルが読み込まれた際に自動で非同期読み込みを行いたい場合は、レベル詳細から設定できます。
変更したいレベルを選択し、レベルの詳細アイコンを押すと、そのレベルの詳細ウィンドウが開きます。


Initially Loaded |
チェックマークを入れると自動で非同期読み込みを行います |
Initially Visible |
チェックマークを入れると読み込み後に表示まで一緒に行います |
今回は任意のタイミングでのレベルの管理を行いたいので、設定はそのままにしておきます。
任意のタイミングでレベルを読み込む
さて、では任意のタイミングでレベルを読み込む手段ですが、これはUE4だと2つ用意されています。
- Level Streaming Volumeを使用する
ボリューム内にプレイヤーが侵入した場合に、指定されたレベルを指定した方法で読み込みます
ボリューム外に出た場合はアンロードされます
- ブループリント内でLoad Stream Levelノードを使用する
レベルブループリントなどで任意のタイミングに、引数のレベルを設定した方法で読み込みます
ちなみにアンロードしたい場合はUn Load Stream Levelノードを使用します
まず1のLevel Streaming Volumeを使用する場合です。
このボリュームは少々特殊で、パーシスタントレベルに置くことでしか効果を発揮しません。
レベルの読み込みを管理するボリュームなので、必ず読み込まれるパーシスタントレベルに置くのは当然ですし、そもそも別のレベルに配置して設定しようとすると警告が出ます。

次に各サブレベルのレベル詳細で、対象となる配置したLevel Streaming Volumeを選択して紐付けます。

これでボリューム内にプレイヤーが侵入した際に、紐付けたレベルが読み込まれるようになります。
読み込み方法についてはボリューム側で設定できます。

Streaming Usage
SVB Loading |
非同期読み込みのみ |
SVB Loading and Visibility |
非同期読み込み後レベル表示(デフォルト設定) |
SVB Visibility Blocking on Load |
同期読み込み後、表示完了まで待つ |
SVB Blocking on Load |
同期読み込みのみ行う |
SVB Loading Not Visible |
非同期読み込み後に非表示状態を保つ。Load Stream LevelやShould Be Visbleでも表示状態にならないので注意 |
次に2のブループリント内でLoad Stream Levelノードを使用する場合です。
この関数は処理待ちが発生するLatentノードなので、関数では使えません。使用する場合はイベント内やマクロで使用しましょう。

Level Name |
レベル名 |
Make Visible After Load |
読み込み後に表示を行うかどうか |
Should Block on Load |
同期読み込みを行うかどうか |
レベルの表示タイミングを制御する
さて、これでレベルの読み込み関連は一通り紹介しましたが、もう少し踏み込んでみましょう。
それはレベルの表示タイミングです。
レベルには、ロード済の状態でも表示状態と非表示状態の2種類の状態が存在します。
普段エディタで作業している時に、レベル単位で表示非表示を切り替える場合もあるかと思いますが、それと同じようにゲーム中にレベル単位で表示の切り替えが可能です。
ちなみにレベルウィンドウの各レベルの左横にある目マークのアイコンでどちらの状態か確認することができます。

ではレベルをロードするタイミングと表示するタイミングで分ける必要があるでしょうか?
メリットはあります。例えばメモリには余裕があるけど描画速度がキツイ…といった場合に有効です。
具体的にはある程度広いマップを使ったゲームなどが該当すると思います。
そこで背景レベルなどをいくつかに分割し、表示が必要でない箇所は非表示状態にしておけば、必要な時に素早く表示状態にできるというわけです。
似たような機能としてUE4にはWorldCompositionというオープンワールド用の機能が存在しますが、それを使わない場合はこういったテクニックを使うという選択肢も視野に入れてみてはどうでしょう。
話を戻しましょう。実際の実装方法としましては、二通り存在します
- Load Stream Levelノードを使用する
引数のMakeVisibleAfterLoadをTrueにしておけば、レベルが読み込まれている状態に再度呼び出すと表示処理のみが実行されます。
ただしこの方法だと非表示への切り替えできません。
- Get Streaming Levelを使用して直接Should be Visibleを設定する
Trueにすることで表示状態にでき、Falseにすることで非表示状態にもできます。
直接LevelStreamingのメソッドを触るのでちょっとプログラマ的には気持ちが悪いかもです。

さて、このように表示タイミングを分けた場合、どうしても表示完了のタイミングを知りたいという状況があるかと思います。なぜならUE4のレベルの表示は一瞬で切り替わるものではなく、次第に表示されるような仕様になっているからです。つまり表示完了待ちというタイミング発生するわけです。
そこで指定レベルの表示が完了したかを調べる方法があります。
Get Streaming LevelからIs Level Visibleで判定を取る事が可能です。

これを実際に使える形としてマクロ化したものがこちらになります。

そして実際のマクロの使用例がこちらになります。引数を配列にすることで複数のレベルに対して同時に判定を行っていますが、その必要が無い場合はもっと中身は簡潔なもになるかと思います。

また、コンソールコマンドの「stat LEVELS」でもレベルの読み込み状況がリアルタイムに確認できるため、デバッグ時はこちらを積極的に活用していきましょう
このようにレベルの読み込みや表示タイミングを状況に合わせて自在に操れるようになれば、レベル管理も様々な状況で対応できるのではないでしょうか
おまけ
Level Streaming Volumeですが、ボリュームの設定方法としては他と違って少し特殊です。
レベル詳細の方で各ボリュームと紐付けるのはワークフロー的にちょっと…と思う方もいるかもしれません。
そんな時はボリュームクラスを基底クラスにブループリントを作成できるようにエンジン側を弄ることで、解消できるかと思います。
変数で対象のレベル名を指定できるようにします。


ブループリントでボリューム内に入った時に対象レベルを読み込み(或いは表示)、出た時に破棄(或いは非表示)といった処理にすれば、他のボリューム配置と同様のワークフローで完結します。
