Unreal Engine 4 公式で公開されているサンプル「ターン制ストラテジーゲーム」では、ランダムマップ生成の仕組みが紹介されています。
今回はランダムマップ生成の基礎的なやり方を見ていこうと思います。
なお、この記事は公式の動画 Blueprint Generating Procedural Rooms | Live Training | Unreal Engine(Youtube) でのレクチャーの序盤をまとめたものであり、英語が苦にならなければ動画を見ていただいたほうが良いです。
目次
0.準備
今回は空の空間にランダムで床と柱を敷き詰めていく処理を作成するため、必要なものは特定サイズの床と柱のメッシュです。簡易的なもので良いので、さっそく作っていきましょう。
まず Blank の新規プロジェクトを立ち上げ、File → New Level で新しいレベルを Default で作り、そこで作業を開始します。
ここではレベル名をDefaultMap としました。DefaultMap にはじめからついている床(メッシュ)は削除しておきます。
Modes → Geometry → Box をプレビュー画面にドラッグ&ドロップして、Details → Brush Settings の x, y, z をそれぞれ 100, 100, 20 に変更。
Brush Settings → Create Static Mesh ボタンをクリックするとウィンドウが表示されるので、任意の場所にメッシュとして保存します。ここでは名前を Floor としました。メッシュとして保存されたので、今プレビュー画面に出ているボックスは削除します。
Floor をダブルクリックして開き、Details → Materials → Material Slots に適当なマテリアルを割り当てます。ここでは M_Basic_Floor を指定しました。これで簡易的な床が完成しました。
柱も同様に、100x100x120 のボックスを用意し、Create Static Mesh でメッシュ化、適当なマテリアルを割り当てます。ここでは名前を Pillar としました。
これで準備は完了です。
1.自動で任意の範囲に床を敷き詰める
ランダムマップ生成をするということは、床などのメッシュを手動では配置しないということです。ではどのような仕組みを利用するかというと、"Add Instance" というメソッドを使います。
このメソッドを理解するため、手始めに床を敷き詰める処理を作ってみましょう。
まずは新規 Blueprint Class で Actor を作成。ここでは名前を StreamExample としました。
Components タブで Add Component → Instanced Static Mesh を選択しメッシュを追加。ここでは名前を FloorInstancedStaticMesh としました。Details → Static Mesh に 先ほど作った Floor メッシュを割り当てます。
次に、MyBlueprint → Variables の「+」から以下のように変数を3つ作成。
これらはタイルを敷き詰める量や間隔を設定するためのもので、後から値を変更できるようにすべて Instance Editable にチェックを入れておきます。
変数名 | 型 | 初期値 | 説明 |
MaxX | Integer | 32 | x軸のタイル数 |
MaxY | Integer | 32 | y軸のタイル数 |
TileSize | Integer | 100 | タイルのサイズ |
では、設定した変数の値だけタイルを敷き詰める処理を作りましょう。
StreamExample ブループリントの Construction Script タブを開きます。この Construction Script はオブジェクトが配置される際に一度だけ呼ばれるもので、自動生成には打ってつけというわけです。
メソッドを以下のように組みました。
一見複雑そうですが、縦横で指定したタイルの数(例えばここでは縦横 32x32 なので 1024個)だけ座標をずらしながら "Add Instance" をループし続けるだけのメソッドです。
コンパイルしてレベルエディタに戻ると、広大な床が出来上がっていると思います。
この状態で、Details → Default の Max X 等の数値を変えてみると、床の大きさ等が変化することが確認できます。
2.メソッドを拡張して床と柱をランダムに配置する
ブループリントを使って任意にメッシュを配置する仕組みは理解できたと思います。今度は床だけではなく、ランダムで柱も設置してみましょう。
まず、StreamExample に新しい変数を2つ追加。
1つは各座標のタイルの種類を保存しておく TileType。もう1つは TileType の末尾のインデックス数を記録しておく LastIndex です。どちらも今回の例では無くても構わないものですが、タイルの種類を増やしたり内容が多くなってくると使う機会も増えます。
変数名 | 型 | 初期値 |
TileType | Integer配列 | 無し |
LastIndex | Integer | 0 |
そして前項で作ったメソッドを以下のように改良します。
だいぶ増えてしまいましたが、よく見るとそこまで複雑さは増していません。
"ForEachLoop" が2つに増えていますが、最初のループで TileType に1かゼロ、つまり床か柱かの情報をランダムに格納しています。
2回目のループでは TileType の中身を順番にみて、前項のように位置をずらしながら "Add Instance" を実行しています。その際に TileType の値に従って、床と柱のどちらかを "Add Instance" しているといった具合です。
コンパイルや実行をしてみると、その度にタイルの配置が変化しているのが確認できるでしょう。
3.公式サンプルのマップを見てみる
ランダムにタイルを配置する方法は理解できましたが、先ほどの処理ではゲームのマップとして使うにはいささかお粗末すぎます。公式サンプルのターン制ストラテジーゲームのマップはどのようになっているのか見てみましょう。
3.1 公式サンプルのダウンロードと立ち上げ
まずはサンプル「ターン制ストラテジーゲーム」を自環境に落とす必要があります。Epic Games Launcher の Unreal Engine タグ → ラーニング → GamePlayConcepts から「ターン制ストラテジーゲーム」を選択、画面が切り替わるので「無料」ボタンをクリックします。
しばらくするとボタンが「プロジェクトを作成する」に変化するので、クリックするとプロジェクトを自環境に落とすことができます。
あとは普段と同様、Unreal Engine を起動しプロジェクトを立ち上げれば良いです。
3.2 ビューワの配置物とプレイ画面を確認する
公式サンプル「TurnBasedStrategy」を起動してみましょう。開いたレベル DefaultMap には、WorldData という Actor しか置かれていません。
しかしプレイしてみると、マップやキャラクターが自動生成されていることが確認できます。
部屋と通路が確保され、すべて経路として繋がれており、完成されたマップなのが分かります。
3.3 マップ生成の仕組みを理解する
ここで、World Settings → Game Mode → GameMode Override を None に設定します。また、Contents Browser → Blueprints → QuadTree → AABB ブループリントをクリックし、Details → Rendering → Actor Hidden In Game のチェックを外します。
この状態でプレイをし、マウスとキーボードをうまく操作させてマップを下から見上げてみてください。以下のように見えるはずです。
完全に真下から見上げたのが下の画像。
こう見てみると、区画毎に部屋が一つずつ割り当てられているのことが分かります。
実はこれ、一般的なダンジョン自動生成アルゴリズムなのです。
このアルゴリズムの内容については、以下サイトなどが分かりやすいでしょうか。
不思議なダンジョンの作り方(Qiita)
公式サンプル「TurnBasedStrategy」のブループリントは非常に複雑ですが、処理の原理はこういうことなのです。コードは当然いつでも見られるので、時間があれば解読してみてください(筆者は途中で面倒になってやめました)。
【参考リンク】
0 件のコメント:
コメントを投稿