サーバーに処理を任せられるクライアントサーバーモデルを採用
『エグゾプライマル』は、カプコンが開発するマルチプレイのチーム対戦型アクションゲームで、最大で10,000体と大量の恐竜が登場するのが特徴です。
同作は、アクションの気持ちよさに大きく影響するフレームレートを安定させるべく、サーバーの高い処理性能が利用できる「クライアントサーバーモデル」を採用。サーバー側が処理の多くを担うのに加え、通信を特定のタイミングで行う方式ではなく必要に応じて随時行う方式にすることで、柔軟な相互通信を実現しています。
さらに、通信環境に問題のあるプレイヤーがほかのプレイヤーに影響を及ぼさないよう、スター型(※)のネットワーク構成をとっています。
※ 各クライアントが共通のネットワークハブに直接接続するネットワーク構成
クライアントとサーバーのプログラムは、どちらも内製エンジンの「RE ENGINE」で実装されている
また、通信を伴う複雑な処理を組み立てやすくするため、RPC(※)に対応しています。
※ Remote Procedure Call。ネットワークを通じて、別システムの関数を呼び出せる仕組み。関数ごとに、サーバーのみで/クライアントのみで/双方で(マルチキャスト)実行するかどうかを指定できる
大量に出現する「群れ恐竜」の出現数を階層構造で管理
作中では、ティラノサウルスやトリケラトプスなどの大型エネミーと、ラプトルといった小型エネミーが登場します。この2種類のエネミーは互いに異なる仕組みで実装されているため、開発では前者を「単体恐竜」、後者を「群れ恐竜」と呼び区別しているそうです。
本セッションでは、特に「群れ恐竜」の制御について詳しく解説されました。
群れ恐竜は、サーバーのみに存在する「マネージャー」、サーバーとクライアントの両方に存在する「親」、クライアントのみに存在する「本体/子供」の3階層の構造で管理されています。
マネージャーは親の生成・削除と操作を担うほか、群れに恐竜を追加する、親同士を入れ替えるなど群れ全体のコントロール機能も持っています。
親は子供の生成・削除のほか、座標とパラメーターなどを通信する役割を担います。
子は群れ恐竜の中に存在する個々の恐竜であり、親からの命令に従います。
階層構造を持った群れ恐竜の処理として、「群れの死亡判定」が例として挙げられました。
子がダメージを受けてHPがなくなると、親にダメージと死亡情報を送ります。親はそれぞれの子から受け取った情報に含まれるダメージを種類ごとにまとめ、サーバー側の対応する親へと死亡情報を送ります。
サーバーでは死亡した恐竜の数や座標が正しいかを簡単に計算し、申告された分だけ群れを減らします。その後、各クライアントの親へ減らした群れの数を送信します。クライアント側の親は、群れの数が合うように群れ内の恐竜を間引いて、倒された恐竜の数を反映させています。
クライアント側で数を調整するにあたっては、すでにダメージを受けている群れを優先して間引いて違和感が生まれないよう工夫している
サーバー上に子は存在しないため、群れの座標を取得したい場合は各クライアントから位置や子供の数・向きをサーバーへと送信し、その情報を基に中央値を算出することで群れの状況を把握しています。
地形との当たり判定は空間を分割したグリッドで計算
大量の恐竜が出現する群れ表現では、地形との当たり判定が課題となりました。
従来の手法ではキャラクター1体ごとに持たせた球やカプセル状のコリジョンと地形のコリジョンとの衝突判定をとっていますが、大量のキャラクターを同時に処理すると負荷が大きくなります。
スライド中の画像はプレイヤーが使用する特殊兵装「エグゾスーツ」のコリジョン
そこで、可能な限り計算回数を減らせる代替案として、地形の高さ情報を持つ配列から地形の判定を行う方法が採用されました。
同作はPvPvEであるため、PvPを意識した階層構造の少ないレベルデザインが高さ情報と好相性だったという
レベル上の1m×1mを1グリッドとして、レベル全体を覆う配列を事前に用意します。配列の各要素には、天井の高さや、水面やマグマなど地形の属性データが格納されます。
配列データは、ゲーム内の地形をレイキャストなどでスキャンして自動生成したデータに専用のツールで調整を加えて作られています。
グリッドはレイヤー化されており、地形の変化に応じてデータを書き換えることで地形破壊やオブジェクトの動的な生成に対応している
グリッドを活用し、高速な経路探索を実現
続いては、群れ恐竜の移動を決める「経路探索」について。ナビメッシュで経路を算出する従来の手法は処理コストが高く、大量のキャラクターでは負荷の高さが問題となりました。
そこで、「ウェイポイント(※)を用い、各ウェイポイントをつなぐすべての経路をアセット化する」方法がとられました。経路が事前に計算されているため、実行時の計算コストを大きく削減できます。
※ 経路の基準となる地点。複数のウェイポイントを結んだ線が経路となる
加えて、探索の開始地点と目標地点それぞれに最も近いウェイポイントを高速で検索できるよう、空間の各グリッドに最寄りのウェイポイントを持たせています。
決定した経路を恐竜が移動する際には、恐竜同士が重ならないように押し合う「押し合い判定」が課題となりました。
群れ恐竜のように大量のオブジェクトが密集する場合、一般的な矩形の当たり判定を使用した押し出しでは負荷が高いのに加え、狭い地形では移動できなくなる、ガクガクした動きになるなどの問題が起こり得ます。
そこで、3D空間上のグリッドを使った衝突判定を採用。各恐竜が移動する際、自身の位置するグリッドに自らの情報を登録しようとします。その時、すでに別の恐竜が登録されていれば、対象のグリッドの外へ押し出されます。
グリッドから押し出すにあたっては、完全にグリッドの外へ押し出そうとするとガクガクした挙動になりやすいため、流体シミュレーションのように少し反発する程度に留めているのがポイントとのこと。
上記の押し合い判定は、恐竜が行う「壁への組み付き」などのアクションにも活用されています。同作では、ラプトルの群れが壁にぶつかると壁に組み付き、ほかの恐竜が登れる坂を形成します。
ラプトルが移動時に壁にぶつかった際には、前方のグリッドに「壁に組み付いているラプトル」がいるかどうかをチェックします。存在している場合はひとつ上のグリッドに自分も組み付くように実装すれば、ラプトルが坂を形成するようになります。
グリッドを活用することで「壁への組み付き」の処理もシンプルに実装できているそう
範囲攻撃と単体攻撃で異なるダメージ判定を使用
続いて、プレイヤーによる恐竜への攻撃判定の実装について語られました。
従来の手法では、攻撃用のコリジョンとダメージを受けるコリジョンが衝突することでダメージ判定を行っています。しかし、1,000体を超えるエネミーが出現する状況では目で見てわかるレベルでスタックが発生してしまったそう。爆発などの範囲攻撃を多用することもあって、最適化だけでは処理負荷の問題を解決できませんでした。
それを受けて、ここでも空間上に配置したグリッドを活用しました。プレイヤーが行う攻撃の形状から、攻撃を与えるグリッド座標を算出。対象となるグリッドへ攻撃情報を格納します。そして、恐竜側で自身の存在するグリッドから攻撃情報を取り出してダメージを計算します。最後にグリッド内の攻撃情報を消去して攻撃の処理が完了する仕組みです。
この仕組みであればコリジョンの計算は攻撃範囲のグリッド座標への変換程度で済み、群れの空間アクセスが並列で処理されるのも相まって効率的に範囲攻撃を処理できます。
しかし、グリッドを用いた手法は広い範囲に対する攻撃を高速で処理できる反面、スナイパーライフルのような1体1体を正確に狙う攻撃では不自然な貫通や多数ヒットを引き起こし、違和感を生んでしまいます。
そこで、範囲攻撃はグリッドを用いた手法を、単体への攻撃は従来通りコリジョンを用いた手法を採用。恐竜の体の形に沿ったダメージコリジョンを、プレイヤーのレティクル付近の恐竜にのみ貼り付けています。
攻撃の射線にいる群れはマネージャーへダメージコリジョンの割り当てを要求し、マネージャーはプレイヤー視点の手前にいる恐竜から順にコリジョンを割り当てます。大量の恐竜が存在してもコリジョンを持つのは数体程度であるため、負荷を大きく抑えられます。
画面中央部、レティクル付近の敵にのみ緑色のダメージ用コリジョンが張り付けられている
モーション再生処理を変更し、5倍以上の高速化を実現
群れ恐竜の表現においては、モーション再生処理の最適化が求められました。従来はアニメーションデータに格納されている各ジョイントの回転や位置などの情報に基づき、リアルタイムで各姿勢マトリクスなどを計算する複数のステップを経由していましたが、負荷の点からより効率的な手法が必要でした。
解決策となったのは「事前に複数ステップのモーション計算を行った結果をデータとして保持する」手法です。これを姿勢マトリクスに直接書き込むことで、5倍以上の高速化を実現しました。
ただし、一時的に保存するデータが増えるためメモリの消費が増える、60fps基準で作成されているためフレームレートに関わらず60fpsのモーションが再生されるなどのデメリットも存在します。
10,000体の恐竜を表現すべく、距離で群れを3つに分類
ゲーム内で起こるイベント「超大量発生」では、最大で10,000体の恐竜が押し寄せます。10,000体の大群を表現するにあたっては、群れとプレイヤーとの距離に応じて近距離・中距離・遠距離の三段階に分けて処理しています。
近距離群はこれまで紹介されてきた仕組みで実装され、中距離群は負荷を抑えながらも近距離群と近いアクションを取る、遠距離群とのつなぎ役として実装されています。中距離群は「車の爆発に巻き込まれる」「天井から降り注ぐ」などの派手な演出も担います。
遠距離群は「RE ENGINE」が搭載するインスタンシングに特化した描画機能によってレンダリングし、モーション負荷を大幅に削減することで数百体規模の集団を表現しています。個別のアクションなど細かい制御はできない制約があるため、違和感がないようプレイヤーから離れた位置でのみ使用されています。
遠距離群は実装の都合上、個別にコリジョンを設定できないものの、射撃が届く範囲に配置されることもあるため攻撃を当てられる必要があります。そこで、遠距離群には集団全体を覆うカプセル型のコリジョンを割り当て、攻撃を受けた時にやられているモーションの恐竜を追加で描画させています。これにより、あたかも攻撃によって群れの恐竜がデスしたかのような表現を生み出しています。
AIのプレイから最適化を探る
セッション最後のトピックは「ゲーム体験を最大化するためのAI学習の活用」。本作では、最適化のための情報を自動的に収集する方針のもと、AIに繰り返しゲームをプレイさせることで作品の改善に役立ててきました。
AIによるプレイでは、CPUやメモリーの負荷などを記録します。これにより、負荷が高いシーンはエネミーの出現数や種類を抑制し、逆に余裕がある場合は増やすという対応ができるだけでなく、迫力ある描写も数字的な根拠を持って調整できます。
プレイデータは動画でも保存されており、深層学習を用いた物体検出によってデータと実際のゲーム画面の乖離を検出し、開発者にアラートを送信します。
コリジョンの不具合で壁の裏などの非表示領域に大量のエネミーが出現してしまうケースや、設置オブジェクトの不具合によって描画が崩壊しているケースなど、異常・課題の洗い出しに役立てられています。
データベースへリクエストされた通信のサイズ情報から最適な通信サイズを算出するなど、ネットワーク通信にも深層学習は活用されています。さらに、通信パケットからパターンを見出し、データをパターンに置き換えて通信することで、通信サイズ約50%の削減に成功しています。
また、クライアントのパフォーマンスに対して最適なスペックのサーバーに接続させる仕組みもAIによって実現しています。
最後に阿部氏は「10,000体のエネミーとの戦闘をマルチプレイで実現するのは難度の高いミッションであったが、非常によいものが出来た」と振り返り、講演を締めくくりました。
「GAME CREATORS CONFERENCE ’24」公式サイト『エグゾプライマル』 公式サイト
大阪生まれ大阪育ちのフリーライター。イベントやeスポーツシーンを取材したり懐ゲー回顧記事をコソコソ作ったり、時には大会にキャスターとして出演したりと、ゲーム周りで幅広く活動中。
ゲームとスポーツ観戦を趣味に、日々ゲームをクリアしては「このゲームの何が自分に刺さったんだろう」と考察してはニヤニヤしている。