第2章 運用フェーズのアセット肥大化とProject Auditorによる調査

財津 大睦Zaitsu Hiromu

株式会社サムザップ

運用が続くプロジェクトでは、アプリの容量とメモリ使用量が少しずつ増加します。この章で扱うのは、対策を知っているチームでも肥大化が再発する理由と、その止め方、そして増え続けているアセットを見つける手段です。個々のアセットの軽量化そのものは扱いません。

2.1 運用フェーズで肥大化が進む理由

肥大化は、日々の運用のなかで少しずつ蓄積します。シーズンイベントのために追加された高解像度のバナー、期間限定キャラクターのボイスとカットイン、コラボ企画で受け入れた外部制作の素材、告知が終わったのに残っているキャンペーン用のアセット。1件ずつは許容範囲でも、リリースを重ねるうちに無視できない量になります。しかも蓄積が進むほど見直しのコストが上がるため、整理の優先度は下がっていきます。

原因は技術よりも、運用のルールが緩むところにあります。運用フェーズでは、次の4つが繰り返し起こります。

  • 規約が古くなる:端末や要件が変わって規約と実態がズレても、誰も規約を書き直さない、今回だけと言って通した例外が積み重なる
  • チェックが甘くなる:締め切りが近づくと納品素材の確認が省略され、担当が交代したときも引き継ぎが中途半端になる
  • 一時的な対応が残り続ける:急ぎで無圧縮テクスチャや個別出力のスプライトを入れたまま、後で戻す作業が行われない
  • 判断基準が記録されていない:設定の根拠がドキュメントに残っていないため、担当者が変わると同じ基準を再現できない

いずれもツールの導入だけでは解決しません。

2.2 規約を運用し続ける

達成したい指標を決め、動作保証端末を選び、最も負荷の掛かるシーンで検証する。この規約の作り方そのものは、前作『Unity パフォーマンスチューニング バイブル』の第1章が扱っています。運用フェーズで問題になるのは、決めた後です。

規約は決めたそばから実態とズレていきます。そこで、動作保証端末の変更、Unityやレンダリングパイプラインの更新、計測方法の変更といったタイミングを、見直すきっかけとして先に決めておきます。とくに計測方法が変わると同じアセットでも数値が動くため、測り方も規約と一緒に残しておきます。

規約の数値は、書き出しやインポート、自動ビルドのいずれかで検査が走る形にします。止めるか通すかは項目ごとに決め、通す場合も実測値をビルドのログやレポートとしてチーム全員が見える場所に残してください。本人の画面にだけ警告を出す形は読み飛ばされます。

規約を超えているアセットの修正は、値を直せば終わりではありません。圧縮設定を変えるなら見た目が許容範囲に収まるか、バンドルを組み替えるなら参照が外れていないかを、本番と同じ条件で確認する必要があります。前作が警告しているとおり、量産後の作り直しは発注から品質チェック、デバッグまでを含む工程です。まとめて直すのではなく、リリースのたびに計測して、増えた分をその都度修正するのが理想です。

2.3 肥大化のパターン分類

素材の種類ごとに対策をまとめると、どの素材が重いかは分かっても、なぜ重くなったのかまでは分かりません。原因が残れば同じ肥大化が再発します。そこで素材の種類を横断し、原因ごとに6つに分類します。

表2.1: 肥大化の6パターン

パターン何が起きているか
圧縮設定の誤りインポート設定が未圧縮や既定値のまま放置され、必要な数倍の容量を占めています
解像度の過剰最大解像度を指定しないまま取り込み、面積に比例して増えています
余分な付随データ使わない補助データを有効にしたまま同梱し、メモリ上にも保持しています
形式の選択ミス用途やプラットフォームに合わないフォーマットを選び、容量を余分に使うか、実行時に展開されてメモリを消費しています
まとめ方の不一致Sprite Atlasやバンドルのまとめ方が実際の利用単位と合っておらず、1枚のSpriteの表示でアトラス全体を読み込むなど、使わないものまで一緒にロードしています
バンドル間の重複共有アセットを登録しないまま参照し、参照元のバンドルの数だけ同梱しています

前半4つはアセットのImport時に判定できますが、後半2つは他のアセットとの関係で決まるため、その時点では判定できません。この違いが、次節で対策を二段に分ける理由になります。

2.4 Import時の自動適用とビルド前の検査

再発を止めるには、アセットのImport時に正しい設定を適用したうえで、それをすり抜けたものをビルド前に見つけます。

自動適用の手段は2つあります。条件によって値を変えるなら、Importの前後に処理を差し込めるAssetPostprocessorを使います。型ごとの初期値を固定するだけならPresetで足ります。

ただし、Import時の適用だけではすり抜けが起きます。.metaファイルごと持ち込まれたアセットは設定を保持したまま入ってくるうえ、前節の後半2つはこの段階では判定できません。

そこでビルド前や自動ビルドの段階で、規約に反するアセットが混ざっていないかを検査します。テクスチャの最大解像度やメッシュのポリゴン数などの制限規約をテストとして書いておけば、エディターと自動ビルドの両方で同じ基準を使えます。AssetRegulationManagerのように、この用途のOSSも公開されています*1。

こうした検査を仕組みにせず、規約を文書に書いて周知するだけの運用は、冒頭に挙げた規約が古くなる状態と、判断基準が残らない状態をそのまま招きます。ルールを決めるのは人の仕事ですが、守らせる部分は機械的に任せてください。

2.5 バンドル間の重複が起きる仕組み

6パターンのうち、最も見えにくいのがバンドル間の重複です。原因が個々のアセットの設定ではなく、アセットどうしの参照関係にあるためです。Addressablesはアセットをグループ単位で管理し、ビルド時にグループごとのバンドルへまとめます。このとき、Addressableに登録していないアセットは暗黙の依存として扱われ、参照している側のバンドルそれぞれに複製されます。

登録していない共有アセットの複製(上)と、専用グループに登録した場合(下)

図2.1: 登録していない共有アセットの複製(上)と、専用グループに登録した場合(下)

図の例では、4MBのテクスチャを2つのPrefabが参照しているだけで、バンドルの合計が10MBになります。このテクスチャを専用グループに登録して明示的な依存にすれば、複製が解消されて合計は6MBまで下がります。参照元が増えればその数だけ複製され、同時にロードすればメモリにも同じだけ展開されるため、放置するほど影響が大きくなります。なお、実際のバンドルは圧縮されるため、数値は仕組みを示す目安です。

こうした重複は目視では追いきれないため、ツールで検出します。Addressablesの場合はAddressables Analyze*2が使えます(2.0系で一時削除され、2.3.1で復活しています)。検出した重複を自動で解消する機能もありますが、グループ構成が書き換わるため、適用後はビルド結果まで確認してください。

2.6 肥大化したアセットを調査する

規約と検査で新しい肥大化は止められますが、すでに積み上がっているアセットは別途探し出す必要があります。アセットのプロファイリングには、用途の異なるツールが複数あります。

表2.2: 容量とメモリの調査に使うツール

ツール分かること
Build Report / Editor Logビルドサイズのカテゴリ別内訳
Addressables AnalyzeAddressablesの重複バンドル依存の検出と修正
Project Auditorプロジェクト全体のインポート設定と静的な構成の問題
Memory Profiler実行時のメモリ使用量とオブジェクトの参照関係

このうちMemory Profilerは、前作の第3章「プロファイリングツール」が扱っています。本章では、前作で紹介していないProject Auditorを取り上げます。

2.7 Project Auditorで洗い出す

Project Auditorは、プロジェクト内のアセット、コード、設定を静的に解析し、問題のある箇所を一覧として出力するツールです。規約に落とすところまで手が回っていない段階でも、何が問題になりうるかを把握できます。長く実験段階のパッケージでしたが、2025年2月に1.0がリリースされ、正式版になりました。以降の画面と項目は、Unity 6.4とRulesパッケージ1.0.3で確認したものです。

2.7.1 導入方法はUnityのバージョンで異なる

Unity 6.4より前では、Package Managerからcom.unity.project-auditorを追加します*3。6.4以降はEditorに同梱されたため追加は不要ですが*4、判定に使うルールがcom.unity.project-auditor-rulesに分離されたので、そちらを導入しないと解析できません(図2.2)。

Rulesパッケージ。解析本体はEditorのモジュールとして存在する

図2.2: Rulesパッケージ。解析本体はEditorのモジュールとして存在する

ルールを本体から切り離したことで、Editorのバージョンを上げなくてもルールだけを更新できます。

2.7.2 Assetsビューの使い方

Window > Analysis > Project Auditorを開いて解析を実行すると、結果はSummary、Code、Assets、Shaders、Project、Buildのビューに分かれます(ビューはUnityのバージョンによって増えることがあります)。容量とメモリの調査で使うのはAssetsビューで、Asset IssuesやTextures、Sprite Atlasesなど9つに分かれています。

最初に開くのはAsset Issuesです(図2.3)。検出された問題が種別ごとにまとまり、それぞれ何件見つかったかが表示されます。

Asset Issuesビュー。検証用に用意したプロジェクトでの検出例

図2.3: Asset Issuesビュー。検証用に用意したプロジェクトでの検出例

種別を展開すると個々のアセットが表示され、選択すると右側に詳細と推奨される対処が出ます。全体像がつかめたら、Texturesなど個別のビューでその内訳を見ます(図2.4)。

Texturesビュー。設定と結果のサイズが横に並ぶ

図2.4: Texturesビュー。設定と結果のサイズが横に並ぶ

このビューの利点は、設定と結果が同じ行に並ぶことです。同じ2048×2048のテクスチャでも、Compressedの行は2.7MB、Uncompressedの行は16.0MBを占めています。設定1か所の違いで6倍近い差がつくことが、Inspectorを開かずに分かります。Size列を降順にして上位からCompression、Resolution、Formatを確認すれば、圧縮設定の誤り、解像度の過剰、形式の選択ミスが見つかります。

2.7.3 検出結果を減らす

運用中のプロジェクトで解析を実行すると、検出結果は数千件を超えることがあります。この状態で上から潰そうとしても進みません。前掲の図2.3にあるFiltersで表示を絞ります。Only Major/Criticalにチェックを入れれば重要度の高いものだけが残り、Areasで対象領域を選べば容量に関わる指摘だけを表示できます。

検出された問題には重要度が付きます。ドキュメントではMajor、Moderate、Minor、Ignoredの4種類と説明されていますが、定義上はCriticalも存在します。Ignoredは重要度の段階ではなく、無視すると指定した状態を指します。対処はMajorから順に進め、Criticalが検出された場合はそちらを最優先します。静的解析では意図した設定も指摘されるため、1件ずつ確認してから直すのが安全です。

誤検知と判断した指摘にはIgnoreを付けます。Ignoreは開いているレポートに記録される状態のため、設定ファイルには書き出されず、解析をやり直すと外れます。検出のしきい値のような判定パラメーターはProjectSettings/ProjectAuditorSettings.assetに保存されるため、こちらはコミットすれば全員で揃えられます。恒常的に指摘を抑制したい場合は、自動ビルド側のスクリプトでAnalysisParams.WithAdditionalDiagnosticRulesに無効化のルールを渡します。

2.7.4 自動ビルドに組み込む

Project Auditorはコマンドラインからも実行できます*5。自動ビルドに組み込んで検出件数をリリースごとに残しておけば、どのリリースで増えたかを追跡できます。

Preferencesには、ビルド後に検出件数をログ出力する設定と、検出をエラーとして扱う設定があります。失敗判定を自前で書く前に、こちらで足りるかを確認してください。一方、保存した2つのレポートを比較するAPIは提供されていないため、前回との差分を取るには自前の処理が必要です。

なお、ビルド後の自動解析が完了せず止まったままになる、解析中にメモリを使い切るといった報告もあります*6。タイムアウトを設定し、解析の失敗がビルド全体を止めないようにしてください。

2.8 おわりに

肥大化対策は、個々のアセットを軽くする技術よりも、チームとして再発を止める仕組みづくりの比重が大きいテーマです。運用のなかで規約が緩んでいくこと自体は、どのプロジェクトでも起こります。だからこそ、基準を決めることと見直すことに人の時間を使い、守らせる部分は機械に任せる形が、結局いちばん長続きすると感じています。

Project Auditorは正式版になったばかりのツールですが、規約づくりまで手が回っていないプロジェクトが現状を把握する入り口として十分に実用的です。まずは一度解析を実行して、自分たちのプロジェクトの傾向を眺めるところから始めてみてください。