大ヒット音ゲー「プロセカ」UI・3Dモデルの大型アプデを実現させた、2つの最適化【CAGC2024レポート】

2024年4月10日

株式会社Colorful Palette クライアントエンジニア

大河原 壱成

株式会社サイバーエージェントに2022年新卒入社。主にUnity製ゲームプロジェクトのグラフィックス開発に携わる。

Unity Learning Materials

株式会社Colorful Palette クライアントエンジニア

山口 智也

株式会社サイバーエージェントに2020年新卒入社。現在は株式会社Colorful Paletteでグラフィックスエンジニアとして業務。『プロジェクトセカイ カラフルステージ! feat. 初音ミク』では主に3Dやバーチャルライブ周辺の実装を担当

X
GitHub
Unity Learning Materials

サイバーエージェントのゲーム・エンターテイメント事業部(SGE)による初のカンファレンス「CyberAgent Game Conference 2024」が3月7日に開催されました。

本レポートでは2020年にリリースされたリズム&アドベンチャーゲーム『プロジェクトセカイ カラフルステージ! feat. 初音ミク』が3周年を迎えた際に行われた、3DCGとUIの大型アップデートを実現するための最適化についてご紹介します。

3DキャラクターとUIを大量アップデート。快適なUXのために最適化は必須

大河原:『プロジェクトセカイ カラフルステージ! feat. 初音ミク』(通称プロセカ)は、セガとColorful Paletteが共同開発している、VOCALOID楽曲を中心としたリズム&アドベンチャーゲームです。2020年9月にリリースされ、ユーザー数は1000万人を突破。収録曲数は2024年3月末時点で416曲にも上っています。


▲2020年、リリース当初にテレビ放送された、ゲームの概要を紹介するCM

大河原:2023年9月に行われた3周年大型アップデートでは、3DとUIの大幅改善を行いました。

3Dに関しては、キャラクターモデルをほぼ全て作り直し、キャラクターの表現力向上を目指しました。たとえば、キャラクターの髪の毛に隠れた目が、完全に隠れてしまうのではなく半分透けて見えるような表現や、目にかかった髪の毛が瞳に移りこんで揺らめく様子の表現、また、キャラクターの影の精度を向上させるなど、よりリッチなキャラクター表現を目指しました。

また、3Dキャラクターが歌い踊る曲のMV(ミュージックビデオ)の表現の改善も行いました。MV内で使用するライトの色や表現のパターン、ぼかしの演出などを追加し、全てのMVをアップデートしました。

大河原:UIは、楽曲選択画面、ゲームプレイ画面、プレイオプション選択画面をはじめとした全画面の改修を行いました。各画面でUX向上のための機能追加や、グラフィックス表現の向上などを行い、UIUXを一新しました。

大河原:こうした大量のアップデートの結果、大量のアセット改修が発生しました。この際、もしアセット管理が適切に行われなければ、無駄なアセットロードやメモリリークが発生し、アプリのクラッシュにつながる恐れがあります。また増大した処理負荷を削減しなければ、ゲームが重くなり、同様にUXの快適さに影響が生じます。

そこで私たちは、3周年大型アップデートのタイミングで多数の最適化を行いました。今回はその事例についてご説明します。

最適化は大きく3つのパターンに分かれます。

大河原:1つ目は「定常負荷の削減」です。毎フレーム実行するような処理の軽量化を目指し、フレームレートの低下や、端末の過剰な発熱を防ぐために行います。

2つ目は「スパイクの軽減」です。これはロードの処理など、1フレームで発生する長い処理の改善を行うものです。

3つ目は「メモリ使用量の削減」です。メモリリークを防ぎ、メモリが許容量をオーバーしないようにするための対策です。

今回取り組んだこれら3つの最適化について、3D最適化とアウトゲーム最適化に分けて説明していきます。

負荷を削減しながらリッチな表現を実現する「3D最適化」

大河原:はじめに、3Dの最適化事例についてご説明します。

まず基本的な作業として、スパイク削減のためにアセットのロード・アンロード処理の見直しを行いました。

演出の増加に伴い、曲のMVごとに使用するアセットが増えた結果、MVを流しながらリズムゲームをプレイしている最中に、ロードによる解放処理が行われると、スパイクが発生しプレイが中断してしまう可能性が生じました。

そこで、必要なアセットは必ずリズムゲームやMVの再生前にロードを行い、ゲームプレイ中やMV再生中のロード処理を行わないようにしました。

また、Garbage Collectionによるスパイクもリズムゲームの体感に影響するので、リズムゲームプレイ中はGarbage Collectionを止めるようにしました。プレイ後には、プレイ中に使用した3Dのリソースを全て破棄し、ロード前と同じ状態にするようにしています。

大河原:次に、具体的なグラフィックスアップデートの内容と、それに対応した最適化の内容を3つご紹介します。いずれも定常負荷の削減を目的としたものです。

1つ目は、3DMVの演出において、画面内の明るい箇所と暗い箇所をより詳細に表現するために、特定領域にカラーの合成機能を複数回重ねがけする機能の追加をしました。

大河原:このアップデートにより、今まで1回だったフルスクリーン描画を、色を重ねるために複数回走らせると、負荷が重くなってしまいます。そこでフルスクリーン描画による色の重ねがけはやめて、色を重ねたい範囲にだけフラグメント処理が発生するようにしました。

複数回の重ねがけを全画面に適用させるケースがそもそも少なかったこともあり、重ねがけ範囲のみのフラグメント処理に変更したことで、定常負荷を大きく削減できました。

2つ目は、DoF(被写界深度)の最適化事例です。

DoFを活用すると、カメラのピントを表現できるようになります。今回のアプデでは、画面奥にピントが合っているときに、手前に映る前景がきれいにボケるよう、表現の改善を行いました。

大河原:Unityの標準機能でも、前景をきれいにボケさせる機能は存在しますが、負荷が重くミドル端末での描画が安定しなかったため、GaussianベースのDoFを拡張しました。これにより処理負荷を抑えつつ、前景ボケもきれいに出せるようになりました。

3つ目は、新機能となるレンズフレアの最適化です。

プロセカではキャラクターなどにライトが遮蔽されると、フレアサイズが動的に変化する仕様になっています。

▲発表スライドより引用。黄色で囲っているスポットライトの光が、キャラクターなどによって遮蔽されたとき、フレアサイズが動的に変化する

大河原:レンズフレアの最適化前はCPUで1フレアごとにMeshを生成し、Dynamic BatchingによりMeshを結合していました。遮蔽判定についてはRaycastを使用し、1フレアごとにRayをとばして判定していました。

Rayによる判定を行う場合、キャラクターやステージなどのアセット側にColliderを設定するため、遮蔽対象となるオブジェクトの数に比例してCPU負荷が高くなります。また、全てのMVのアップデートを行う以上、MVで使用するアセットの全てに対してColliderの設定をしなければならず、かなりの対応コストがかかっていました。

そこでRaycastによる遮蔽判定をやめて、深度バッファによる判定を行うことでRay判定によるCPU負荷をゼロにし、大量のCollider判定処理を不要にしました。

そのほかにもDrawProceduralによるMeshの生成、結合処理の削減のほか、Compute Shaderを用いた遮蔽判定やフレアサイズの計算処理の最適化を行いました。

カメラ処理のムダ削減で、CPU/GPU処理を大幅に軽減

Unityにおけるカメラの処理は、CPU処理、GPU処理ともに処理負荷の増加につながります。カメラの描画範囲内にオブジェクトが大量に存在すると、当然描画処理の負荷は増加します。さらに、カメラが存在するだけでもオーバーヘッドは発生します。ローエンド寄りのモバイル端末では、カメラ一つで最大1msのCPU処理が発生することもあるようです。そのため「カメラの使用数自体」を削減すること自体が、定常負荷の削減につながります。今回はカメラに関する2つの最適化事例を紹介します。

1つ目は、使用していないMVのカットで、カメラの処理を走らせないようにした事例です。

プロセカではメインの描画カメラの他に、画面内のモニターに映像を映すためのサブカメラが存在します。このサブカメラは全てのMVで使われているわけではなく、1MVの中でも特定のカットのみ使われているという限定的なものです。

そこで、MV全体でサブカメラを使用していない場合は、サブカメラ映像の生成処理すら行わないようにして、処理のオーバーヘッドとレンダリング処理を削減しました。またサブカメラを使用するMVでも、サブカメラが必要なカットのみで更新を行い、不要な処理が走らないようにしました。

大河原:2つ目は、カメラ数を削減した事例です。

アプデ前のプロセカでは、メインカメラ・サブカメラのほかに、メインのカメラが動いても、画面の位置やサイズが固定された状態で描画されるオブジェクト専用のカメラがありました。

今まではこの専用カメラで描画してレンダテクスチャに書き込んだものを一時的に保持し、メインカメラのポストプロセス処理の後に、保持しておいたレンダテクスチャをメインカメラにさらに書き込むことで対応していました。

しかし、2つのカメラを使っていても、メインカメラに映し出すべき最終的なレンダターゲットは同じです。そこで、カメラを分けずに直接メインのカメラに対して描画命令を呼ぶようにしました。これにより、今まで二つのカメラで対応していた作業を一つのカメラで対応できるようになりました。

大河原:この2点の対応により、MVによっては、サブカメラと追従オブジェクト描画カメラの最大2つのカメラの更新がなくなったため、大幅に処理が軽減されました。また、追従オブジェクトを書き込むためのレンダテクスチャをメモリに割り当てる必要がなくなり、メモリを大きく節約することができました。

Shader Variantの見直しで、不要なコンパイルを発生させないように

大河原:そのほかの最適化についても、簡単に紹介します。

1.半透明シェーダのOverdraw軽減(目的:定常負荷の削減)

ライブステージのスポットライトなどは、半透明オブジェクトが描画をしないよう、アルファがゼロの状態で表現されることがありました。その場合でも半透明シェーダの処理は行われてしまうので、画面に表示されていないシェーダの処理が走ってしまっていました。

スポットライトなどの描画で特にネックなのは、フラグメントシェーダの処理です。頂点自体は少なくても、画面上に対してライトのオブジェクトが大きな割合を占める演出があり、かつライト表現は重ねがけが頻繁に発生するため、オーバードローが発生してしまっていました。

そこで、半透明用シェーダに対して、アルファのプロパティ値がゼロの場合、頂点シェーダで頂点座標を全てゼロにすることで、描画スケールをゼロにしてフラグメントの処理をカットするようにしました。これにより不要なオーバードローを削減できました。

2.Shader Variantの見直し(目的:メモリ使用量の削減)

プロセカでは、ランタイム処理最適化のために機能ごとのShader Variantを定義していましたが、開発する中で徐々にバリアント分岐が増えていき、最終的に長期メモリの観点で無視できなくなりました。

シェーダーバリアントとは、シェーダ内にKeywordを定義することで、Keyword有効無効時の処理分岐を考慮したシェーダを、1ファイル内に記述できるものです。これにより不要な分岐がランタイム中に走らなくなり、GPU処理の最適化につながります。

ただし、Keyword有効無効を考慮した全ての組み合わせを網羅するシェーダのプログラムがプリコンパイルされるので、Keywordを追加するたびに指数関数的に消費メモリが増加してしまいます。特にポストプロセスシェーダーではKeywordによる分岐が多く発生していたので、全てのKeywordの組み合わせを考慮すると、約210メガも消費している状況でした。

そこでVariantの定義を設けました。まず、計算負荷の低い処理は常時処理しても問題ないと考え、分岐処理を行わないようにしました。次に、分岐条件がUniform変数の場合は、通常のif文で処理するようにしました。これによりKeyword数が削減され、約470キロまでポストプロセスシェーダーのメモリ使用量を落とすことができました。

3.インゲーム中のシェーダのコンパイル回避対策(目的:スパイク軽減)

最適化前はインゲーム中に、シェーダのコンパイルによるスパイクが発生していました。

シェーダは描画命令が出ると同時にコンパイルを行います。そのため、シェーダキーワードが有効無効と切り替わったタイミングでそのKeywordの組み合わせが存在しなかった場合、コンパイルが発生してしまいます。演出上、どのKeywordや表現の組み合わせが発生するかはデザイナーの制作次第なので、どのキーワードパターンが発生してもコンパイルが発生しないようにしなければなりません。

そこで活用したのが、UnityのShader Variant Collectionです。ここにシェーダとキーワードの組み合わせを登録し、シェーダーバリアントのウォームアップを呼ぶと、任意のタイミングでプリコンパイルを実行できます。

インゲーム中に使用するシェーダと全ての組み合わせをShader Variant Collectionに登録し、インゲームのロード中にプリコンパイルを行うことで、インゲーム中に不要なコンパイル処理が発生しないようにしました。

4.圧縮形式に関する対策(目的:メモリ使用量の削減)

MVのアップデートにより1MVに使用するアセットが増えると、消費メモリが懸念材料となりました。そこで、役割ごとにテクスチャの圧縮サイズを設定し、実機上の見た目を最優先に考慮しつつ、用途によって圧縮倍率を設定しました。

この作業は、NVDIAが公開している圧縮形式や、サイズごとの視覚的評価を参考にして行いました。例えばシェーダでの計算処理用のマップなどはASTC4×4に、キャラクターやエフェクトのカラーマップはASTC6×6に設定するなど、アセットによって最適な圧縮形式を選択しました。

操作時のストレスを軽減するアウトゲーム最適化

山口:ここからは、アウトゲームのUIなどに関する最適化事例についてご紹介します。アウトゲームの最適化は大きく2つのポイントを意識して行いました。

1つ目は、操作時のストレス軽減です。各種画面でのスパイクや待ち時間の削減により、操作の快適性や画面遷移のスムーズさの向上を目指しました。

2つ目は、長時間のプレイにも耐えられるようにしました。特にアドベンチャーシーンは滞在時間が長くなりやすいので、注意が必要です。対策としては、定常的な負荷を削り端末の発熱を抑えることに加え、時間経過によって発生していたメモリリークを抑えることが基本となります。

ここからは実際の事例について紹介します。

1.操作時のスパイク軽減について

画面遷移や各種操作のたびに待たされるストレスを軽減するために、スパイクはできるだけ削る必要があります。しかし最適化にかけられる開発期間は限られているため、優先順位をつけた対応が重要です。

今回の最適化では、プレイヤーが頻度高く触る「ホームから楽曲選択をしてリズムゲームをプレイし、またホームに戻る」というメインループを重点的に最適化する方針を立て、処理の並列化やフレーム分散、ロジックの最適化等で対応しました。

2.マスターデータのメモリ最適化

以前のプロセカでは、ロードを減らしスパイクを軽減するために、マスターデータをメモリ上に乗せっ放しにしていました。しかし長期間の運用やアップデートによってマスタのサイズが肥大化し、メモリが圧迫されていました。

そこで、マスターデータの中でもアクセス頻度が低く、メモリに乗るサイズが大きいデータについては、マスターデータをストレージからメモリに適宜呼び出し、必要なタイミングで使って最終的に解放する方式に変更しました。

3.アセットのメモリリーク解消

各画面で使用しているアセットは、画面を抜ける際にアンロードされますが、static関数やSingletonでアセットが握られているリソースが解放されず、一部メモリに残り続けてしまう場合があります。

UnityのMemory Profilerを使うとプロファイリングを効率的に調査することができますが、全てのメモリリークを解消することは難しいため、実際にクラッシュに影響を与えている度合いを踏まえて、コスパを意識して地道に対処していきました。

4.積み上がるアセットの都度破棄

プロセカの楽曲選択画面では、全ての楽曲のジャケット画像を表示しています。

▲UI変更後の楽曲選択画面。2023年9月27日公開・公式Youtube動画「プロジェクトセカイ ワンダショちゃんねる3周年スペシャル」より

山口:この画面をスクロールして多くのジャケットが表示されると、その楽曲ジャケットが全てメモリに乗ってしまっていました。そこで、事前に許容できる画面のメモリサイズ分のリングバッファを用意し、画面に表示され古くなったものから逐次破棄されるようにしました。

5.文字表示に関する対応

プロセカでは、ストーリー上でキャラクターが話すときの文字の表示にTextMeshProを使用しています。ADVシーンの字幕UIでは、左から右に順番に文字が表示されるタイプライター風の表現がされているのですが、文字数が増えるたびにMeshが再生成されてしまっていました。

山口:そこで、字幕に表示される最大文字数分のTextMeshを事前に生成し、各文字用の頂点の座標を変更することによって、表示・非常時を切り替えるようにしました。

6.定常負荷の削減を目的としたUIブラー機能に関する対応

UIブラー機能の開発中は、ブラーがかかる背景部分の動きを毎フレーム正しく反映するために、毎フレームで画面キャプチャーをしてブラー処理を行い、背景のブラーに反映していました。

しかしGPU負荷が高くなっていたため、ブラー表示を開始するタイミングで画面キャプチャーとブラー処理を行ない、その後のフレームはキャプチャー表示した画面を使い回す構造に変更しました。その結果ブラーの背景が動かなくなってしまいましたが、描画の定常負荷とUI表現のトレードオフとして、最適化を決断しました。

7.UIや2D関連の不要な描画の洗い出しと削減

ガチャ画面など、特定のカメラが不要なタイミングで描画されるケースがあったため、不要なカメラを削る作業をしました。

そのため各カメラの描写が必要なタイミングの整理を行い、カメラの描画が必要ない部分ではカメラを無効化しました。

8.UI・2D描写のOverdraw削減

プロセカのホーム画面などでは、画面の描画の解像度が高めに設定されており、GPUのフィルデートが処理のボトルネックになりやすかったため、2D画面でも半透明のOverdrawを削っていく必要がありました。

そこで、Overdrawのデバック表示で、最適化が有効な箇所を洗い出し、サイズの大きいUIパーツが複数重なっているところを結合したり、画面占有率が高いパーティクルを減らすなどして、最適化を行いました。

アウトゲームの最適化の事例紹介は以上です。

ユーザーにゲームを快適に楽しんでもらうために、最適化はとても重要な仕事です。皆さんが今後ゲームを最適化するときの考え方や、実際に最適化の問題を解決するときの参考としてご活用いただければと思います。

執筆:一本麻衣
編集:光松瞳

© SEGA/© CP/© CFM All rights reserved.

関連記事

人気記事

  • コピーしました

RSS
RSS