エピック ゲームズ ジャパンが主催する勉強会『UE5 Deep Dive 2023』が、2023年12月15日(金)に開催されました。
『Unreal Engine 5で極める!プロシージャル技術』と題した講演では、エピック ゲームズ ジャパン 湊 和久氏が登壇。
プロシージャルにオブジェクトを設置するUnreal Engine 5.2の新機能「Procedural Content Generation」の仕組みや使い方が解説された本講演をレポートします。
エピック ゲームズ ジャパンが主催する勉強会『UE5 Deep Dive 2023』が、2023年12月15日(金)に開催されました。
『Unreal Engine 5で極める!プロシージャル技術』と題した講演では、エピック ゲームズ ジャパン 湊 和久氏が登壇。
プロシージャルにオブジェクトを設置するUnreal Engine 5.2の新機能「Procedural Content Generation」の仕組みや使い方が解説された本講演をレポートします。
TEXT / セレナーデ☆ゆうき
EDIT / 神谷 優斗
湊氏は、2023年8月よりエピック ゲームズ ジャパンでソフトウェアエンジニアを務めています。
エピック ゲームズ ジャパン入社前は、ゲーム開発の仕事のかたわら、Unreal Engine 5(以下、UE5)でのゲーム開発入門書『Unreal Engine 5で極めるゲーム開発:サンプルデータと動画で学ぶゲーム制作プロジェクト』の執筆や、ゲームエンジンの開発や数学的背景などを解説する書籍『ゲームエンジンアーキテクチャ 第3版』の日本語版の翻訳監修などにも携わっています。
本講演は、「プロシージャル」という単語の解説からスタート。湊氏は「プロシージャル」を、「『手続き型』を意味する英単語。一定のアルゴリズムに従って、特定のアセット(3Dモデルやアニメーションなど)を自動生成すること(※)」であると説明しました。
※ ゲームメーカーズ 用語集より引用された
「ワールドを効率よく構築する」という観点では、プロシージャルを3種類に大別できると湊氏は言います。
1つめは、建物の生成などに使われる3Dモデルの組み立て。2つめは、パラメータからモデルそのものを生成するモデリング。最後に、3Dモデルの配置です。
実際のゲーム開発では、内製エンジンに独自のプロシージャルツールを実装する方法や、Houdiniなどのプロシージャルを得意とするDCCツールでの生成物をエンジンにインポートする方法が使われます。
また、「Houdini Engine(※)」の使用も選択肢のひとつとして挙げられました。
※ Houdiniの計算機能をモジュール化し、ゲームエンジン内に組み込むプラグイン。開発はゲームエディタ上で行いつつ、計算部分はHoudiniの機能を利用できる
湊氏はここで、PCGの根底にある3つの概念「ポイント」「ボリューム」「アトリビュート」について説明しました。
森を作るため、木のモデルを大量に配置する場合を考えます。
HoudiniやPCGで使われる手法では、地面に「点群(ポイントクラウド)」を散布(スキャッター)するアプローチをとります。 散布した点群に対してはノイズ関数で確率密度のばらつきを与え、一定密度以上の点をフィルタリング。残った点にオブジェクトをスポーンすることで、ばらつきのあるオブジェクト配置を行います。
講演では、スプラインメッシュで作られた要塞の中に点群を散布する方法が紹介されました。
木をスポーンさせる向きや大きさなどの生成ロジックは、ノードベースの「PCGグラフ」で行います。
PCGグラフで使用するパラメータはアクタに公開できるため、木の数が多いときには配置確率を下げるなどのコントロールが可能です。
同じくランダムで木などを配置するツールには「Foliage」がありますが、再配置には配置範囲の塗り直しが必要です。PCGは、決定済みの範囲内で再生成できる点で優れています。
木の生成後に、木とは大きさの異なる花をスポーンさせたい場合、木周辺の点はスポーン対象から除外されるべきです。しかし、点の位置情報のみでは、特定の点が木と重なっているかを判定するのは不可能です。
そこで、「ボリューム」を用いて点に体積情報を与えます。
木や花に対応したボリュームの衝突判定を用いることで、花と木の生成を調整できます。
講演では、先ほどの要塞内にボリュームを配置し、内部に木が生えない処理を実装する様子が実演されました。
なお、PCGボリュームは、ワールド上に独立したアクタとして存在するスプラインを利用することができません。
そこで、講演ではUEのタグ機能を用いた方法が紹介されました。要塞のアクターにつけたタグに基づいてスプラインを取得し、スプラインの内側にボリュームを配置することで、木の点群との交差判定をとり、除去しています。
各点は、位置以外にも確率密度やボリュームなどの情報を含んでいます。これらの情報は「アトリビュート」と呼ばれ、開発者側が自由に追加し、運用できます。
例えば、川の近くの石は河口からの距離に比例して大きくなります。そこで、河口からの距離を点がアトリビュートとして保持することで、メッシュの大きさに反映できます。
下記画像における街路樹は、レンガや落ち葉、樹木などから構成されています。
これらをプロシージャルに配置する場合、1つの街路樹を複数のオブジェクトから構成された複合的なオブジェクトとみなすことができます。複合的なオブジェクトとしてとらえることは「カプセル化」と呼ばれます。
講演では、PCGにおけるカプセル化の方法として「アセンブリ」と「Construction Script(※)」の2つが紹介されました。
※ 各アクタが構築時に実行する関数
アセンブリは、点群情報をアセット化する手法です。新たに散布した点それぞれにアセット化した点群を配置することで、一連の巨大な点群を作成できます。
Construction Scriptを用いる手法では、配置はPCGが、形状の構築はConstruction Scriptが担うといった分業が可能です。
アセンブリには、手作業で作成されたコンテンツが必要です。
手作業でレベルに配置されたコンテンツを選択し、レベルアセットとして書き出します。
生成した点群アセットは、PCGグラフにドラッグ&ドロップすることで、点群を出力するノードとして使用できます。
Construction Scriptを用いる手法では、スタティックメッシュではなく、Construction Scriptを実装したアクタをスポーンさせます。PCGにおけるアクタのスポーンには、「Spawn Actor」ノードを使用します。
講演では、「Height」プロパティを基にConstruction Scriptで高さを決定する塔のアクタを配置する実演が行われました。
PCGグラフでは、ランダムな「BoundsMax」アトリビュートを持つ点を散布します。そして、Spawn Actorで塔のアクタを生成する際に、「Height」プロパティを各点が持つ「BoundsMax」アトリビュートでオーバーライドします。
これにより、スポーンされるアクタのHeightプロパティにばらつきを与えられます。
PCGコンポーネントの「Is Partitioned」オプションを有効化することで、UE5のレベルストリーミング機能「World Partition」に対応します。
「木を管理するグリッドは小さくていいけど、塔のグリッドは大きく扱いたい」など、ボリューム感の異なるコンテンツを1つのPCGグラフで扱う場合は、「Hierarchical Generation」機能を有効化します。
この機能によって使用できるようになる「Grid Size」ノードによって、ストリーミングのグリッドサイズをグラフから制御することが可能になります。
Hierarchical Generationを使う際にPCGWorldActor配下に生成される「PCGPartitionActor」は、PCGグラフの実行にあたり、対応するグリッドサイズに属するノードネットワークのみを稼働させます。また、実行時は大きなグリッドサイズのPCGPartitionActorから順に実行されます。
なお、Grid Sizeを導入した時点でグリッドサイズに対応した親子関係が生じ、自身より小さいサイズの生成物に依存した処理を行えなくなる点には注意が必要です。
また、Hierarchical GenerationはConstruction Scriptのあるアクタの生成にも対応しており、HLODの構築も可能です。
PCGグラフにおけるノードは「PCG Element」と呼ばれ、C++やBPを用いて独自に実装できます。
BPでPCG Elementを実装する際は、「PCGBlueprintElement」を親クラスに指定します。そして、ノード実行時の処理にあたる「ExecuteWithContext」関数をオーバーライドすることで、独自の処理を実装できます。
PCGグラフ上でのメニューにノードを表示させる場合は、「Expose to Library」オプションを有効化します。なお、表示させずとも、コンテンツブラウザからドラッグ&ドロップすることでノードを配置できます。
ノードの入出力ピンは、「Input」および「Output」オプションからカスタマイズ可能です。
各ピンは、ラベル(ピンの名前)とType(型)の情報を持ちます。TypeをAnyにすると任意の型を受け付け、PointやSplineなどの型を指定すると受け付ける型を限定できます。
また、PCGにおけるワイヤーは、一度に複数の点群情報が入力されるマルチデータの仕様になっています。
BPで実装されるPCG Elementでは、オプションで指定した入力ピンの数に関係なく、基本的にノードで表示されるピンは1つのみです。そのため、1つの入力ピンから処理に使用するデータを抽出する必要があります。
講演では、「任意のデータが単一の入力として扱われる」PCGグラフの概念が図示されました。
実際に入力されるデータは、すべてのデータをラップした「PCG Data Collection」構造体です。この構造体は、点群のデータ(Point Data)やスプラインのデータなどをひとまとめにしたデータの配列を持っています。
Point Dataは、メタデータと、実際の点の配列を持ちます。そして、配列中の点のデータは、位置情報や大きさなど基本のアトリビュートとして扱われるプロパティを保持。開発者が任意で追加するアトリビュートは、メタデータとの組み合わせで保持されます。
実際にPCG Data Collectionから目的のデータを取得するには、指定したラベル名と一致するデータを検索する「Get Typed Inputs by Pin Label」などの関数を使用します。
マルチデータの入力では、ピンから取得できるデータが複数あるため、処理を複数回行うために「For Each Loop」ノードを利用します。
さらに、点群データから点のデータを1つずつ取り出すのにも「For Each Loop」が必要となるため、必然的にネストループ(ループ内ループ)の構造になります。
PCG Elementから結果を出力するには、出力用のデータ配列を作成する必要があります。
入力に使われたPoint Dataは、出力には使ってはいけないルールがあるため、出力用のコンテナとしてPoint Dataを新たに構築しなくてはいけません。
メタデータやアトリビュートを入力データから出力データへ正しく継承する場合は、「Initialize from Data」ノードを使います。
新たに作成した出力用のPoint Dataには、処理結果となるポイントの配列をセットします。これをPCG Data Collectionにセットし、リターンノードから出力します。
なお、通常の入出力ピン以外にピンを追加する場合は、Custom InputやCustom Outputが利用できます。
ここで、先ほど実演した塔の生成に対し、分布密度や確率密度ではなく「領域内に最大10か所」といった条件付けを行う方法が紹介されました。
作成するのは、入力で指定した個数のポイントをランダムで選択、Outから出力するノードです。
Point Dataに格納されたポイントをループ処理するケースでは、「For Each Loop」の代わりに「Point Loop」ノードが使えます。
Point Loopノードは、自身の「Point Loop Body」関数を呼び出して実行します。そのため、ループ処理の実体はPoint Loop Bodyをオーバーライドして実装します。
Point Loop Bodyで出力されるデータは自動的に1つのPoint Dataに集約されるため、そのままPCG Elementの出力として使用できる利点があります。
次に、ボリュームの底の高さを座標点まで引き上げるアンジュレーションを行うノードの実装が実演されました。
ここで、湊氏からループ処理における重要なポイントが3つ紹介されました。
1つめは、Point Loop Bodyにおいて戻り値として点を返すかどうかは、Return Valueにチェックをつけることで制御できる点。また、関数呼び出しはConst関数に限られる点です。
2つめは、点のメンバ変数を書き換える場合には、「Set members in PCGPoint」ノードを使う必要がある点です。
最後に、Set members in PCGPointでは、点のデータを参照形式で取得・出力する点です。
出力用ポイントを作るために入力ポイントを加工してしまった場合、想定と違う挙動になる可能性があります。そのため、「Copy Point」ノードで入力のコピーを取ってから加工することを湊氏は推奨しています。
また、PCG Elementに用意されている「Iteration Loop」や「Nested Loop」関数には、自動で非同期処理されるため利用するメリットがあることも紹介されました。
3Dモデルの横幅に等しい距離で点を打ったスプラインに、モデルを配置するシチュエーションを考えます。
アセットのピボットがセンターになっている場合、打った点の始点と終点を越えて配置され、スプラインの範囲よりも広い範囲にメッシュが配置されます。
こういった問題には、スプラインから読み取った2点の中間点に点を置きなおすアプローチが考えられます。
本来はC++でスプラインサンプラーを実装する必要がありますが、独自のPCG Elementを用いることで、BPのみで同様の処理が実現可能です。
本来はここでデモを行う予定でしたが、講演の終了時間が迫ってしまいました。
公開された資料では、さらなるPCG Elementの発展としてアトリビュートの読み書き方法や、決定論、非同期処理の対応方法などの解説も行われています。
以降の詳細は、スライド資料をご確認ください。
『Unreal Engine 5で極める!プロシージャル技術』スライド資料UE5 Deep Dive 2023ゲームのタイムアタックを中心に、ストリーミングサイト・Twitchで活動をしているストリーマー。ゲームイベントの紹介記事など、WEBメディアでの活動実績もあるが、繰り出されるダジャレのクオリティには賛否両論がある。
西川善司が語る“ゲームの仕組み”の記事をまとめました。
Blenderを初めて使う人に向けたチュートリアル記事。モデル制作からUE5へのインポートまで幅広く解説。
アークライトの野澤 邦仁(のざわ くにひと)氏が、ボードゲームの企画から制作・出展方法まで解説。
ゲーム制作の定番ツールやイベント情報をまとめました。
CEDEC2024で行われた講演レポートをまとめました。
BitSummitで展示された作品のプレイレポートをまとめました。
ゲームメーカーズ スクランブル2024で行われた講演のアーカイブ動画・スライドをまとめました。
CEDEC2023で行われた講演レポートをまとめました。
東京ゲームショウ2023で展示された作品のプレイレポートやインタビューをまとめました。
UNREAL FEST 2023で行われた講演レポートをまとめました。
BitSummitで展示された作品のプレイレポートをまとめました。
ゲームメーカーズ スクランブルで行われた講演のアーカイブ動画・スライドをまとめました。
UNREAL FEST 2022で行われた講演レポートやインタビューをまとめました。
CEDEC2022で行われた講演レポートをまとめました。