前回はC88に出展した自作ゲームの事例解説をしました。
今回も引き続き、ゲームでの事例解説をしたいと思います。
前回はゲームのメインの進行管理を解説していましたが、今回はレベルの管理、UMGの扱いについてなど補助的な部分を扱ってみようと思います。
全体のレベル管理
まず今回はステージが複数ある前提のあるゲームなので、レベルの進行管理は当然複数ある前提で設計しています。レベルが複数ある場合に問題となるのはそのレベルの切り替え時です。上位レベル以外の下位にあるレベルは破棄されるとその情報を全て失います。ここで上手くやってあげないと画面の切り替えがおかしくなったり、BGMやSEが途切れたり等の問題が発生します。
幸いUE4には複数のレベルを同時に読み込むことが出来る仕組みが存在しています。これを上手く使ってレベルの切り替えを行なってあげましょう。全体の図としては以下のような状態になります。
まずメインレベル(パーシスタントレベルと呼びます)を作成し、その中に全てのステージを構成するサブレベルを突っ込んでおきます。そしてこれをゲーム中にストリーミングします。これをレベルストリーミングと呼びます。
レベルストリーミングはゲーム開始後、サブレベルを自由にいつでも読み込む事ができます。もちろん破棄するタイミングも自由です。
エディター上では"ウィンドウ"→"レベル"のウィンドウを開くことによってレベルのストリーミング等の管理が可能です。
基本的にはパーシスタントレベルに全てのレベルを入れておき、必要なレベルを自由に管理します。レベル編集のロックや可視性のトグルなどもここで行ないます。
指定レベルを右クリックし、ストリーミング方法を変更する事によって、いつレベルを読み込むのか指定ができます。ここでブループリントを指定しておけば、ブループリント上で読み込むタイミングを指定する事が可能となります。
あとはパーシスタントレベルのレベルブループリント上で"Load Stream Level"を呼びます。ここでレベル名の指定以外に"Make Visible After Load"というフラグをチェックしておくと、読み込みが終了すると同時に指定のサブレベルの処理が走るようになります。読み込みが終了したら画面のフェードインなどの処理をさせて、突然パっと画面が切り替わらないような工夫もしておきましょう。
これでゲーム中に自由なタイミングでレベルをストリーミングロードできるようになります。当然ストリーミングは非同期読み込みですので、その間も別の処理、つまりローディングアニメーションを入れたりもできます。
リトライやステージの切り替え
当然レベルはリトライやステージの切り替えが必要です。まずリトライの場合は、単純にレベルをOpen Levelすればいいというものではありません。特にサブレベルのOpen Levelは禁止です。どうやら最悪ハングアップしてしまうようです。
基本的にはパーシスタントレベルをOpen Levelするか、"Reset Level"ノードを使います。Reset LevelはGame Modeから呼び出せるノードですが、その名の通り現在のレベルをリセットします。
もしリトライ処理をするのなら直前にGame Instanceクラス上にフラグを作り、Game Instanceにリトライをしましたという情報を保存しておきます。Game Instanceに関しては過去に解説をどうぞ。
あとはリセットされた時にEvent BeginPlayが呼ばれるので、その時にリトライかを分岐処理で判断しておきます。
これでリトライから来た場合には、諸々の処理を飛ばしていきなりゲーム画面に突入するようにフローを構築していきます。上手くいけばタイトル画面等を飛ばして、同じステージの内容を最初からやり直せるようになるはずです。
ステージ切り替えの場合も考え方は同じです。Game Instanceに次はどこへいけばいいのか情報を保持させておき、Load Stream Levelでレベルを読み込ませます。読み込みが完了した際に以前のステージのレベルは"Unload Stream Level"で破棄します。
これでリアルタイムのレベルストリーミングができるようになります。上手くいけばスムーズなレベル切り替えが出来るようになるでしょう。
UMGでちょっと苦労したところ
UMGは優秀なGUIまたはHUDの画面を作成するためのツールです。慣れればすぐにメニュー画面などは作成出来てしまうでしょう。
UMGでのアニメーション処理部分。長いので全て入らず。#UE4Study https://t.co/1HI5L3aASN
— alwei (@aizen76) 2015, 8月 24
実際このようなアニメーションなどもすぐに出来てしまいます。今回はスコアなども割り当てるためにGame Stateクラス上に情報を保存しておき、まとめて計算するという方式で表示させています。
複雑な計算式を必要とする場合にはMath Expression(数学式)ノードはとても便利なものですので、活用しましょう。
問題はここからでした。UMGは現在4.8の段階でアニメーション内に音を仕込むことができません。更に再生しているアニメーションの現在時間も取得できません。これには少し苦労させられました。
少し残念なやり方ですが、再生を開始してからDelayノードで時間を置いてから順次音を再生させていきます。アニメーションを見ながら音が聴けるわけではないので、直感的ではないです。この部分に関してはUMGの改善ポイントだと思いますので改善に期待したいです。
UMGはActionやAxisイベントが使えない
これにも苦労させられましたが、UMG上ではActionやAxisによる入力イベントを取得できません。なのでメニュー周りの入力はかなり面倒です。今回のゲームでは、UMG内の"On Key Up"関数をオーバーライドし、その内部で"Gey Key"情報からどのキーを押しているのか全て保存する事にしました。
どのキーを押したかは全てBoolean変数として保存しておきます。
あとはEvent Tickで入力監視を行ない、押されたキーに応じた処理を行ないます。
しかしこの方法は汎用性こそあれ、スマートではないですし、Tickは処理がタイミング依存です。やはりキーを入力した瞬間のイベント送信するべきと思います。今回は時間の兼ね合いもあってこういう手段をとりましたが、もっと上手い方法はあると思います。
そしてできればUMGの入力周りももっとスマートに出来るようになってほしいですね。
ポーズ処理
最後にオマケですが、ゲームのポーズ処理(一時停止)に関してです。UE4はこの部分に関して、ポーズが出来るように最初から設計されています。
UE4に元からポーズシステムがあるおかげで、一瞬でポーズ画面できたぜ。#UE4Study https://t.co/3oe3EeCnlH
— alwei (@aizen76) 2015, 8月 10
やっている事は本当に単純です。
レベルブループリント上でポーズキーが入力されたイベントを取得し、ポーズ画面のUMG ウィジェットブループリントを作成します。
あとはウィジェットブループリント内で"Set Game Paused"を呼ぶだけです。ポーズが終わったら、もう一度Pausedのフラグを外して呼び出せばポーズ終了です。
ね?簡単でしょう?
ちなみにポーズをかけてもUIはしっかりと動きますので、ご安心を。もし他に動かしたいアクターがいる場合には、アクターデフォルト値にある、"詳細"のTickから"Tick Even When Paused"のフラグにチェックを入れると、そのアクターのEvent Tickだけは動くようになります。
ゲームを作った人はどんどんノウハウや事例を公開しましょう
今回は補助的な部分が多かったですが、意外と知らない情報が多かった人も多いと思います。また制作した際の苦労話は確実にこれからの人のために役立つ情報となります。
ぜひUE4でゲームを作った人は自分のゲームがどうやって作られているのか、解説してみてください。すごく自分も勉強になりますし、他の人にもそんなアイディアがあったのか!というとても良い参考資料となります。#UE4Study
— alwei (@aizen76) 2015, 8月 20