2022年11月19日(土)〜20日(日)の2日間にわたり、Unreal Engine を学ぶ全ての人を対象とした大型勉強会「UNREAL FEST WEST ’22」がリアルイベントおよびオンライン配信で開催された。初日がノンゲームデイ、2日目がゲームデイとして10講演行われたが、本稿では株式会社SNKによるゲームデイのセッション「Unreal Engineを馴染ませる 〜THE KING OF FIGHTERS XV における開発事例〜」についてレポートする。

記事の目次

    関連記事

    ノンゲーム分野で役立つUnreal Engine 5.1の新機能を一挙紹介!〜 UNREAL FEST WEST'22(1)

    イベント概要

    UNREAL FEST WEST '22

    開催日時:
    2022年11月19日(土):NON-GAME DAY
    2022年11月20日(日):GAME DAY

    開催場所:
    京都コンピュータ学院 京都駅前校 6Fホール/オンライン

    unrealengine.jp/unrealfest/west2022

    『KOF XV』の概要・各セッションの構成説明

    登壇したのは株式会社SNKの呂 朗標/ロ・ロウヒョウ氏。中国・広州出身の呂氏はゲームプログラマーとして『SAMURAI SPIRITS』(2019)、『THE KING OF FIGHTERS XV(以下、KOF XV) 』の開発に携わっている。本セッションでは『KOF XV』のプロジェクト全体像と、開発に使用したUnreal Engine(最終バージョンは4.26.1)の機能を紹介した。

    『KOF XV』は2022年2月に発売された対戦格闘ゲーム。プラットフォームはPlayStation 5、PlayStation 4、Xbox Series X|S、Windows 10、Steam、Epic Gamesに対応している。プレイ人数はオフラインで1〜2名、オンラインで2〜8名だ。『KOF』シリーズは呂氏が子どもの頃から中国各地でも親しまれているとのこと。

    【JPN】KOF XV|Official Trailer|好評発売中!

    ©SNK CORPORATION ALL RIGHTS RESERVED. 

    ゲームの基本の流れは、「タイトル」→「メインメニュー」→「モードやキャラクターなどのセレクト画面」→「ロード画面」というアウトゲーム部分と、「バトル」→「リザルト表示」のインゲーム部分に分けられる。本セッションのテーマは、アウトゲーム部分の開発フローになる。

    ▲アウトゲーム/インゲームの構成とそれぞれの使用ツール。サウンド関連はWwise、エフェクトはEffekseer、バトル部分は社内ツールで動いている。キャラクターなどの物理はKawaiiPhysicsを社内拡張して使用。オンライン通信部分はOnline Subsystemをベースにして社内実装している。Jenkinsはビルドやクック、パッケージ自動作成して開発時にインストールするなどプロジェクトの開発を支える目的で使っている

    ©SNK CORPORATION ALL RIGHTS RESERVED. 

    パッケージとクック

    最初に言葉の定義がなされた。「クック」は、ゲームで使用するアセット用にプラットフォーム固有のコンテンツを作成すること。「パッケージ」は、ゲームプロジェクトを配布形式にパッケージ化すること。「エディタビルド」は、アーティストやゲームデザイナーなど、Visual Studio環境をもっていない人でもエディタを起動できるようにバイナリファイルを作成・配布することで、この部分は全てJenkinsで自動化している。

    開発を進めていくと、プロジェクトの中に様々なデータが増えていく。しかし、全てのデータが製品版に必要なものとは限らない。テクスチャ、3Dモデル、翻訳データ込みのテキスト、デバッグ機能などは製品版に含めるものではない。そこで必要になるのがアセット管理(クックルール)だ。Unreal EngineのProject Setting-Asset Manager画面から7つの項目が紹介された。

    1.Primary Asset Type(定義したPrimaryアセットのタイプ)
    2.Asset Base Class(定義したPrimaryアセットの基底クラス)
    3.Has Blueprint Classes(Blueprintアセットを含めるかどうか)
    4.Directories(アセットのディレクトリ)
    5.Rules(クックルールなどの設定)
    6.Should Manager Determine Type And Name
    7.Should Guess Type And Name In Editor
    上記の6と7は今回はOFFにした。

    ▲Project Setting-Asset Manager画面。ここから7項目を設定する

    ©SNK CORPORATION ALL RIGHTS RESERVED. 

    ▲Primary Asset Typeを追加するためには、C++で記述することが必要となる

    ©SNK CORPORATION ALL RIGHTS RESERVED. 

    次にパッケージ設定について、Project Setting-Packaging画面から3つの項目について説明があった。1つ目のAdditional Asset Directories to Cookは、プロジェクト内で参照されているかどうかに関係なく、常にクックしなければならない .uassetファイルを含むディレクトリを指定する。

    2つ目のDirectories to never cookは、プロジェクトで参照されている場合でもクックしてはならない .uassetファイルを含むディレクトリを指定する。このディレクトリに入ってアセットはすべてクックしないことになる。ここはミスをしやすいところを指定していて、ステージを配置するようなアセットのフォルダを一括で設定している。

    3つ目のAdditional Non-Asset Directories to Packageは、常に .pakファイルに追加する必要があるファイルを含むディレクトリを指定する(.uasset以外のファイルを含むように使う)。 .uasset以外のファイルも欲しい場合に使う。

    Project Setting-Packaging画面。ここから3項目を設定する

    ©SNK CORPORATION ALL RIGHTS RESERVED. 

    上記のプロジェクト設定以外にPrimary Asset Labelsというものがあり、ニーズに合わせて細かく設定できる。実際の使用例が2つ紹介された。

    Cook RuleをNever Cookに設定すると、デバッグコンテンツと臨時ファイルを一切クックしないようになる。デバッグ機能は製品版には必要ないが開発中のデバッグフェーズには使用したいときはDevelopment Always Cookに設定する。Cook Rule以外の項目は下記のとおり。

    1.Priority(優先度)
    2.Chunk ID(チャンクID)
    3.Apply Recursively(再帰)
    4.Label Assets in My Directory(ディレクトリ内の全てにラベルを付ける。チェックが付いているとPrimary Asset Labelを配置しているフォルダ内すべてのアセットを指定するという意味になる)
    5.Explicit Assets(アセットの手動指定)
    6.Explicit Blueprint(Blueprintの手動指定)
    7.Asset Collection(コレクションの参照)

    ただし、5と6は修正するたびにPrimary Asset Labelを更新しなければならないので、あまり設定することはない。Asset Collectionは指定することが多い。

    ▲Primary Asset Labelsの画面。Asset CollectionをDebugAssetsDesignに指定すると、デバッグに関するアセットを参照してPrimary Assetの設定したルールに反映される

    ©SNK CORPORATION ALL RIGHTS RESERVED. 

    上記で紹介した方法は、『KOF XV』のプロジェクト内で全て使っているとのこと。特にPrimary Asset Typeの定義はキャラクターやUIなど大きなカテゴリで、Directories to Excludeはリファレンスミスしやすい臨時アセットなどに使用している。Primary Asset LabelsとCollectionはデバッグパッケージを作成するなどデバッグ関連、個別ルール外のアセットでニーズに合わせて使っている。

    ステージとUI

    ステージレベルの構成について

    ステージ制作の内容に入る前に、Persistent Levelの設定項目について説明があった。

    1.Camera(バトルカメラと演出カメラの2つがある)
    2.CollisionMap(揺れもの用、床を貫通しないようにコリジョンを置いているときに使う)
    3.DebugMenu(名前のとおりデバッグ機能の処理がこのレベルに入っている。レベルは後で削除するか、上述したクックルールで製品版から除外すればデバッグメニューが製品版に入ることはない)
    4.Effect(バトル中のエフェクトの関連処理)
    5.SoundMap(これもバトル中のサウンド関連の処理)
    6.ST_(ステージの本体)
    7.ST_MOB_HighEnd(スペックが問題ないプラットフォームのために用意するハイエンド版のモブレベル)
    8.ST_MOB_Standard(スペックが足りないプラットフォームのために用意するスタンダード版のモブレベル)
    9.ST_Sound(ステージ専用のサウンドを置くところ)

    ▲Persistent Levelの画面。ST_MOB_HighEndとST_MOB_Standardの対応では、アーティストたちが結構大変な思いをしたとのこと

    ©SNK CORPORATION ALL RIGHTS RESERVED. 

    各ステージのパラメータ情報、キャラクターのライトの強さや角度、特別な演出中のライトの動き、演出中のカメラの動きなどはData Assetに書き込んでステージに持たせている。もちろん、ステージ以外にキャラクターなどにもData Assetを多用しているとのことだ。

    ライトビルドについて

    MobilityがMovable以外のライトはライトビルドが必要になる。アーティストが作業中に何回もビルドして最終確認するのに時間がかかるため、作業中にライトビルドの分散ビルドを導入している。セッションでは詳しい説明は割愛されたが、検証結果が紹介された。

    1ステージ、クオリティPreviewというかたちで計測したところ、3台のPCで協力した場合、1台の半分以下の時間になる。もちろん製品版ではプレビューという設定ではなく、もっと長くなるが、だいたい半分になるとのこと。製品版用の確認はJenkinsで定期的に自動ライトビルドしてコミットしている。

    多職種で作業しやすい環境について

    Data Tableはゲームデザイナーやアーティストに渡す作業にも相性がいい。UIがUnreal Engineのとおりでわかりやすいこと、CSVとして出力可能であるため外部ソフトで編集できること、アセットのリファレンスを持たせることが可能なのでクック周りのルールを設定できることが特徴として挙げられる。

    キャラクターセレクトのUIでキャラクターEnum、テクスチャ、選択中のテクスチャ、レイアウトのパラメータなどの構造体をData Tableによってキャラクター管理を行なっている。キャラクターが描画される順番もData Tableで管理しており、それを読み込んで自動生成しているのでキャラクターの描画順の仕様変更が起きたときもプログラマーを経由せずにゲームデザイナーだけで対応できる。また、キャラクターのチームの間の間隔もData Tableに入っており、こちらはアーティストたちが調整している。

    ▲黄色い線で囲まれた部分で、Data Tableによってキャラクター管理を行なっている

    ©SNK CORPORATION ALL RIGHTS RESERVED. 

    Data Tableに限らずBlueprint共通で困ったこととしてMapを使用するときEnumがキーという状況で同じキーが追加できないということがあった。

    対策は2つある。1つ目は使用済みのEnumを一度ほかの値に設定して追加する。追加したあとは先に設定した値を戻すというもの。ただし、この方法で解決したとしてもEnumの順番にならないことが設計上問題になる場合は2つ目の対策をとる必要がある。

    Unreal Engineでは全てのパラメータをコピーできるので、コピーした値をテキストとして順番を入れ替えてペーストすれば目的どおりの順番にすることができる。

    ▲コピー&ペーストによる順番の入れ替え

    ©SNK CORPORATION ALL RIGHTS RESERVED. 

    継承、工数の削減について

    テキストをUIとして配置して、フォントやサイズなどをひとつひとつ設定するのは手間がかかる。エディタのDetails欄に親クラスが記載されているので名前を覚えておいて新規Blueprintで検索して作成すれば、継承してBlueprintのアセットができる。中身を編集して配置すれば一種のテキストスタイルのウィジェットができる。こうすることで調整コストが下がる。継承の有無によって作業効率化が大きく変わるのだ。

    同様にアーティストの工数を軽減するための試みがある。ある程度パターン化した汎用のアニメーションを流用するため1個のウィジェットを作成してアニメーションを追加する。その中にNamedSlotを追加し、このウィジェットをほかにも配置してそのNamedSlotの中にもウィジェットを追加する方法だ。ただし、これをやりすぎるとBlueprintの構造が深くなりすぎてパフォーマンスが落ちるので、呂氏の結論としても「加減が必要」とのことだ。

    • ▲▶NamedSlotを使いすぎた結果、階層が深くなり可読性とパフォーマンスが落ちた例

      ©SNK CORPORATION ALL RIGHTS RESERVED. 

    また、UMGのフォーカスが消えて入力が効かないケースがあった。マウスがあるプラットフォームならばクリックすれば解決するが、それ以外のプラットフォームでは進行不能になるケースが多い。そこで入力用のユーザーウィジェットを用意し、全ての入力を分配するように設定。その入力用ユーザーウィジェットのフォーカスが消えたとき、SetFocusToGameViewportを呼び出すことで問題を回避している。On Focus Lostはユーザーウィジェットにもともとある関数であるため、それをオーバーライドすれば出てくる。

    ▲UMGのフォーカスが消えて入力が効かないケースを解消するBlueprint。実際の開発では様々なプラットフォームがあるのでBlueprintで解決できないケースもある。その際はC++で対応している

    ©SNK CORPORATION ALL RIGHTS RESERVED. 

    Game中に2P(2人プレイ)で使われるパーツで左右対称のものがある。その場合は、各パーツを分けて数回分を反転して対応した。こうすることによってアーティストが片方のウィジェットを修正すれば見た目を調整でき、工数を削減しながら見映えの調整に使用できる。

    ▲赤枠の部分を完全に反転し、黄色枠を2回反転して2Pにしている

    ©SNK CORPORATION ALL RIGHTS RESERVED. 

    ▲2P部分のUI対応は、X軸を左右反転しているだけのシンプルなBlueprintになっている。BlueprintFunctionLibraryにこのようなノートを用意するだけだ

    ©SNK CORPORATION ALL RIGHTS RESERVED. 

    3D UIについて

    『KOF XV』には、クライマックス超必殺技という技がある。演出の表現上、画面上部の体力ゲージなど一部のUIが3D表示されることになっている。3D描画ということは、カメラとの距離やポストプロセスの影響も受ける。対応すべきは2点。1つ目はHUDとカメラの距離の再計算だ。これは単純な数式で対応している。この数式をゲーム内の言葉にすると「(カメラとHUDの)距離」=「半径(UIの描画解像度/2)」/「角度(視界FOV/2)」となる。

    ▲HUDとカメラの距離を再計算しない場合の状態

    ©SNK CORPORATION ALL RIGHTS RESERVED. 

    ▲数式はこのようになる

    ©SNK CORPORATION ALL RIGHTS RESERVED. 

    2つ目の対応はアセット元の色合いを保持するため、ポストプロセスを適用しないようにすることだ。3D WidgetのCustom Depth Stencil値をセットしてポストプロセスマテリアルから除外する。設定方法はRender CustomDepth Passにチェックを付け、CustomDepth Stencil Valueの値(図では4)をセットする。残りの値はポストフィックスマテリアルから取得、除外処理を実装するだけだ。

    ▲CustomDepth Stencil Valueの値。ここでは4と設定している

    ©SNK CORPORATION ALL RIGHTS RESERVED. 

    ▲画面上部の白い部分がそのイメージ

    ©SNK CORPORATION ALL RIGHTS RESERVED. 

    しかし対応した結果、アーティストから「Anti-aliasingを適用するとHUDがぼやけて見える」と連絡が来たそうだ。3D描画ということはポストプロセスも影響を受ける。アンチエイリアシングもポストプロセスなのでこの部分も対応する必要がある。この部分は通常の機能が適応されなかったので、Custom Depth Stencilを流用してエンジン改造によってFXAAのシェーダに反映した。PostProcessAAを経由してSceneTextureを渡し、FXAAShaderで除外処理を行なった。関連ファイルは下記の2つ。

    ・/Engine/Sauce/Run time/Renderer/Private/PostProcess/PostProcessAA.cpp
    ・/Engine/Shaders/Private/FXAAShader.usf

    最終結果は下図のとおり。テキスト部分がややハッキリ見える。ただ「正直、エンジニアの私にとっては、さほど気にするところではないと思いました」と呂氏。しかし、アーティストたちの表現に対するこだわりは強かったという。

    • ▲上が対応前、下が最終結果

      ©SNK CORPORATION ALL RIGHTS RESERVED. 

    • ▲上が対応前、下が最終結果

      ©SNK CORPORATION ALL RIGHTS RESERVED. 

    特殊演出に関する補足説明

    ステージ/UIの話の最後に、特殊演出に関する補足説明がなされた。演出中に想定外のステージオブジェクトが映り込むことがあったという。

    ▲想定外のステージオブジェクトが映り込んだ状態

    ©SNK CORPORATION ALL RIGHTS RESERVED. 

    対応方法は、事前に各セクションと相談し、セーフゾーンを決めてもらい、背景オブジェクトが映らないようにしてもらうことだった。

    ▲黄色い部分がセーフゾーン、白い部分がバトル範囲。ステージ班は使えるスペースギリギリまで使っていた

    ©SNK CORPORATION ALL RIGHTS RESERVED. 

    最適化

    ステージについて

    最適化についてはステージの処理コストを事前に策定し、チーム内で共有して基準を上回らないようにした。今回の設定は「GPU負荷6ms以下、CPU負荷(Game,Draw Call)2ms以下」。基本的に対応したのはステージに配置したオブジェクトについて、カリングを考慮したアクターのマージだけ。ポリゴンのリダクションも考慮していたが、今回は序盤にしか使っていなかった。ちなみに、開発の後半部分はアーティストだけで行なったという。

    カリングはカメラに映っていないオブジェクトを描画しない機能で、自動的に動いている。しかし、オブジェクトが多いとカリングするかしないか、という判断の処理が結構重くなるので、ステージアクターのマージが必要になる。基本的に格闘ゲームは前からの固定カメラなので映るものが限られる。下図のとおり、映るオブジェクトがだいたいわかる。その視野を考えてステージアクターをマージしている。

    ▲格闘ゲームの場合、中央と画面の両端の固定カメラになる

    ©SNK CORPORATION ALL RIGHTS RESERVED. 

    UIの最適化については、ウィジェット リフレクタを使用して、想定外のUIが表示されているか確認した。項目を選択すると、周りに白いフチが表示される。または画面上のオブジェクトをクリックすると該当の項目が黄色く選択状態になり、今の画面で何が描画されたのかわかる。開発中、無駄な処理を行なっている状態はよくある。呂氏もウィジェット リフレクタを使う前はなぜ重いのかわからないことがあったそうだ。かなり有効な機能なので、呂氏も使うことを強く勧めていた。

    ウィジェット リフレクタで確認している様子

    ©SNK CORPORATION ALL RIGHTS RESERVED. 

    ロードについて

    StorageからMemory、MemoryからAddToWorldというプロセスを経て最終的にゲームに表示する。今回のAddToWorldの部分はそれほど問題が発生しなかったため、対応したのはメモリロード部分だけだった。

    ▲Blueprintでは時計が付いているほうが非同期ロード

    ©SNK CORPORATION ALL RIGHTS RESERVED. 

    メモリロードには同期ロードと非同期ロードの2種類がある。同期ロードの特徴は早いことだが、ヒッチ(画面が停止)してしまう。これはロードを1フレームで処理しようとしていることが原因だ。それに対して非同期ロードは比較的に遅いが、ヒッチが少なめである。

    UI間のロードとバトル遷移前のロードがあるが、UI部分は同期ロード、非同期ロードの両方を使っている。画面を表示したときに一気に見るものや、アセットなど事前に非同期ロードができなかったものがあるときに同期ロードで一気に読み込んでいる。そのため表示後、ユーザーが操作しない限り表示しないものについては非同期ロードで読み込んでいる。ユーザが操作するときにロードできていなかったら同期ロードをかける。

    バトルロード部分では、セレクト画面で選択した直後にバトルが始まるので、非同期ロードを入れるタイミングがない。そのときに呂氏が指示を受けたのは、「画面が多少止まってもいいのでロード時間を短縮して欲しい」ということだった。そこで呂氏が選んだのは同期ロードだ。同期ロードを実装すると下図のような感じで一気にアセットが読み込まれ、1フレームの時間がとても長くなる。ロードアイコンすら回らない。あるプラットフォームではロードが5秒以上かかると問題となってしまう。

    ▲この例では1フレームが11秒となっている

    ©SNK CORPORATION ALL RIGHTS RESERVED. 

    ▲対応した結果。1フレームは最長でも2.9秒となった

    ©SNK CORPORATION ALL RIGHTS RESERVED. 

    最後に、呂氏から「『THE KING OF FIGHTERS XV』は弊社にとってUnreal Engineでの開発タイトルとしては2作目なので、まだそれほどノウハウが蓄積できていません。個人的には、今後もより多くのUnreal Engineの機能を導入して、クオリティを上げたいと思っています」と結びの言葉があり、セッションは終了した。

    講演動画

    UnrealEngineを馴染ませる~THE KING OF FIGHTERS XV における開発事例~ | UNREAL FEST WEST ’22

    ©SNK CORPORATION ALL RIGHTS RESERVED. 

    講演資料

    UnrealEngineを馴染ませる~THE KING OF FIGHTERS XV における開発事例~【UNREAL FEST WEST ’22】

    ©SNK CORPORATION ALL RIGHTS RESERVED. 

    TEXT&EDIT_園田省吾(AIRE Design)