拡大Q&A:Addressablesを使ったメモリとビルドサイズの最適化

PATRICK DEVARNEY / UNITY TECHNOLOGIESContributor
Apr 28, 2023|17 分
拡大Q&A:Addressablesを使ったメモリとビルドサイズの最適化
このウェブページは、お客様の便宜のために機械翻訳されたものです。翻訳されたコンテンツの正確性や信頼性は保証いたしかねます。翻訳されたコンテンツの正確性について疑問をお持ちの場合は、ウェブページの公式な英語版をご覧ください。

2月、私はユニティ・アクセラレート・ソリューションズのシニア・ソフトウェア開発コンサルタントとしての役割の一環として、アドレスアブルズ・アセット・システムに関するテクニカル・ウェビナーを担当した。ライブ・セッションでは、プロジェクトのランタイム・メモリとビルド・サイズを最適化するために使える様々なプロファイリング・ツールをデモした。ウェビナーの最後には質疑応答が行われ、私たちのチームには答える時間を超えるほどの質問が寄せられた。

以下は、そのクロージングQ&Aの延長であり、より多くのご質問にお答えすることができます。

Q:カジュアルゲーム、アーケードゲーム、パズルゲームなどの軽いゲームに、メモリーの問題がなければ、アドレスシステムは必要なのでしょうか?
いいえ。そうではないかもしれないが、アドレサブル・システムがメモリ性能を向上させるだけではないことを覚えておくといいだろう。コンテンツをロードするタイミングを選択できるようにすることで、ロード時間を改善することができる。Addressablesでコンテンツを構築することで、時間をかけずに反復的なビルドが可能になります。たとえば、ちょっとしたスクリプトの変更であれば、すべてのバンドルを再構築する必要はないかもしれません。

Q:シーンが切り替わったとき、ロードされたアセットは解放されますか?
いいえ。可能性はある。Addressableからロードされたアセットで、参照カウントが0であるため解放可能なものは、シーン遷移中にメモリからアンロードされる可能性があります。非追加的にシーンから移行する場合は、Resources.UnloadUnusedAssets()を呼び出します。これはCPUに負担がかかるが、アセットバンドルを部分的にアンロードすることができる。

Q:オブジェクト・プーリングとAddressablesはうまく機能するのか?
いいえ。はい。Addressablesからオブジェクトを一度ロードし、その複数のコピーをインスタンス化してプールを作成することができる。プールが終わったら、すべてのオブジェクトを破棄し、アセットのロードに使用したAsyncOperationHandleを解放します。

Q:グループとバンドルは一度にメモリにロードされるのか?
いいえ。AddressablesグループはEditorだけの概念です。実行時にはバンドルだけを扱う。バンドルは必要なときだけメモリにロードされ、必要なコンテンツだけがロードされる。

例:あなたは10人のキャラクターが入ったバンドルを持っています。あなたは3つの文字を読み込むようアドレスブルズに依頼する。バンドルのメタデータと3つの文字が読み込まれる。

Q:アセットをリリースする場合、AsyncOperationHandleとAssetReferenceのどちらを保持する必要がありますか?
いいえ。使い終わったらコンテンツをリリースする責任があるので、ハンドルネームは残しておくことをお勧めします。

例えば、私たちのチームのメンバーは、AssetReference上でInstantiate/Releaseを直接呼び出すのを避けるために、ハンドルのルートを使うことがよくあります。

Q:小さなバンドルが多いことのデメリットは?
いいえ。この文書では、バンドルが多すぎることのデメリットをいくつか挙げている。

Q:バンドル内の資産が必要になったとき、同じバンドル内の他の資産はどのようなオーバーヘッドを持つのか?リモートバンドルならダウンロードしなければならないが、バンドル内の未使用アセットによるメモリーオーバーヘッドは本当にないのだろうか?
いいえ。リモートバンドルは、使用する前に完全にダウンロードされます。

ロードされたアセットバンドルのアンロードされたアセットは、実行時のオーバーヘッドを最小限に抑えます。バンドルからアセットをロードするときは、必ずバンドルのメタデータをロードする必要があります。このメタデータの一部には、バンドル内のすべてのアセットの一覧を示す目次が含まれます。バンドル内のアセットが多ければ多いほど、メタデータも大きくなる。

このメモリのオーバーヘッドは、Unity Memory Profilerでキャプチャを取ることで確認できます。All Of Memory "タブには、メモリ上にあるすべての "SerializedFile "オブジェクトのリストが、バンドルごとに1つずつ表示される。これらのオブジェクトは、あなたのバンドルのメタデータである。

このメタデータの詳細については、ドキュメントをご覧ください。

Q:オープンワールドの環境で作業する場合、バンドルの半分をアンロードしたり、Resources.UnloadUnusedAssets()に依存してそれを一掃したりすることなく、個々のアセットをアンロードするために、どのようなバンドル戦略を使えばいいでしょうか?
いいえ。覚えておくべき重要なことは、コンテンツを同時に降ろすことを期待するなら、コンテンツはひとまとめにしておくべきだということだ。ゲームワールドに「静的」なコンテンツ、たとえば特定のバイオーム用の木や岩のように、プレイヤーが動かすことのないものがある場合、そのコンテンツは一緒にバンドルされるべきである。プレイヤーが拾えるアイテムのような "動的な "コンテンツは、別個にバンドルされるべきである。

このブログ記事とリンク先のGitHubレポでは、オープンワールドゲームのバンドル分割について取り上げている。また、各バンドルにかかるメモリのオーバーヘッドを減らすために、バンドルを重複排除する方法も備えている。ステージ4と5は、特にオープンワールドに関連している。

Q:AssetBundle CRC "はいつ有効にしたままにしておくべきか?
いいえ。リモートグループにはキャッシュされたAssetBundleを除いて有効にし、ローカルグループには無効にすることをお勧めします。このチェックは、ダウンロード時にデータが壊れていないことを確認するためのものだ。ローカルのアセットバンドルのチェックをする理由はほとんどない。

Q:アセットのロードやアンロードの際にCPUのパフォーマンスが問題になるため、Addressableを使用する価値がないのはどのような場合ですか?
いいえ。Addressablesシステムは、すべてのコンテンツを前もってロードする必要がないため、CPUのロードパフォーマンスにプラスの影響を与える。

シーンをロードするときにAddressableを使用しなければ、すべてのコンテンツと参照をロードしなければならない。コンテンツをAddressablesに移せば、どのコンテンツをいつ読み込むかを選択できる。

例えば、シーン内に1,000のインベントリアイテムを参照するインベントリマネージャがあるとします。Addressablesを使用しない場合、これらのインベントリアイテムのすべてのメッシュ、テクスチャ、オーディオクリップなどをロードする必要があります。このコンテンツをロードするのを待つと、シーンのロードが速くなる。

Q:アドレス指定可能な資産のすべての依存関係もアドレス指定可能である必要がありますか、それとも共有されている場合にのみ必要ですか?
いいえ。依存関係はアドレス指定可能である必要はない。依存関係は、必要に応じて、ビルドプロセス中にAddressablesに取り込まれる。

たとえば、プレーヤーのプレハブをアドレッサブルにした場合、プレーヤーのメッシュ、テクスチャ、オーディオもアドレッサブルとして手動でマークする必要はありません。バンドルがビルドされると、Addressables にまだ存在しない依存関係はすべて、自動的にプレーヤープレファブバンドルに含まれます。

Q:アセットをリリースし忘れてシーンを変更した場合、このアセットはどうなりますか?
いいえ。シーンが変わっても、ハンドルとの相互作用は本質的に悪くない。しかし、アセットをロードしてそのハンドルをリリースするのを忘れると、アセットがメモリに残ってしまいます。

Addressablesは内部参照カウントシステムを持っている。ハンドルは、私たちがこのシステムとやりとりする方法だ。アセットをロードすると参照カウントがインクリメントされ、リリースすると参照カウントがデクリメントされます。

クリエイターは、この参照カウントを最新に保つ責任がある。参照カウントが1より大きい限り、アセットはメモリ内に存在する。

Q:ウェビナーの例に関連して、私がオープンワールドのゲームを作っているとしよう。ボスはオープンワールドのどこかにいる。プレイヤーがボスのところに向かうとき、ここでどのようにアドレサブルを使えばいいのでしょうか?剣を装填するコマンドを非同期で、トリガーを使って、敵から一定の距離で送ればいいのでしょうか?
いいえ。コンテンツのロードとアンロードのタイミングを選ぶのは、微妙なラインかもしれない。プレーヤーがボスを見る必要があるときに、ボスが準備できていることを確認したいが、プレーヤーがまだ振り向いてボスを避けることができるときに、あまり早くボスをロードしたくないかもしれない。

良い点は、コンテンツのロードとアンロードのタイミングを反復できることで、最初のトライで完璧に最適化する必要はない。

手始めに、プレイヤーが特定の "ゾーン "に近づいたときに、その "ゾーン "のすべてのコンテンツをロードすることをお勧めします(例えば、プレイヤーがダンジョンの入り口に近づくと、ダンジョン内のすべてのコンテンツがロードされます)。これが不必要なメモリ圧迫を引き起こすのであれば、よりきめ細かいロードとアンロードを追加すればよい。

剣のロードが十分でない場合は、ロードトリガーをより早く開始するように移動するか、Unity ProfilerのCPUモジュールを使用してロードされているものを確認することによって剣のアセットのロード時間を改善するか、またはロードが終了したことを確認するために同期的にAddressablesを使用することを検討してください。

このドキュメントには、同期アドレス指定可能ファイルの詳細とコード・スニペットが含まれています。

Q:シーンの開始時にアドレサブルをロードする場合、ローディング・スクリーンは必要ですか?
いいえ。Addressablesからのロードは、通常、Addressables.LoadAssetAsync()のように非同期で行われます。

ローディング画面を出る前に、ロードしたくないコンテンツがあるかもしれない。これらのAsyncOperationHandleを集め、必要なものが完了するのを待ってから帰ることができる。

Q:実行時(データをロードする前)のアドレス指定可能メタデータのメモリ・フットプリントは?
いいえ。Addressablesの初期化中に、カタログファイルがロードされ、Addressablesがラベルと住所をディスク上またはリモートロケーションの資産にマッピングする方法を認識します。カタログが大きければ大きいほど、実行時のメモリ・オーバーヘッドも大きくなる。

カタログのサイズは、ラベルやGUIDを必要としないグループには含めないなど、不要なデータを取り除いたり、既存のデータのサイズを小さくすることで削減できる。例えば、グループの内部アセット命名モードをファイル名やフルパス(長くなる可能性がある)ではなくGUIDに設定する。カタログのランタイムメモリサイズは、Unity Memory Profilerで確認できます。

Q:UnityエディターがAddressableを構築している時間に何をしているのか?
いいえ。ビルドレポートのログは/Libraryフォルダに出力される。このログは、ビルドプロセスの各ステップを示している。ログに詳細を追加するには、このパスに従って "Use Detailed Build Log "を選択します:Edit > Preferences > Scriptable Build Pipeline > Use Detailed Build Logを有効にする。

ログの見方については、ビジュアルとドキュメントをご覧ください。

Q:Resources.Load()にも重複の問題があるのか?
いいえ。はい。AddressablesのコンテンツとResourcesのコンテンツを異なる "世界 "として考えることは有益である。Resourcesにテクスチャがある場合、そのテクスチャのコピーが1つResourcesファイルに含まれます。Addressablesのバンドルがそのテクスチャに依存している場合、各バンドルはそのテクスチャの暗黙のコピーを含みます。結局、ディスク上にテクスチャの複数のコピーがあり、メモリ上にも複数のコピーがある可能性がある。

この重複を避けるには、テクスチャを/Resourcesから移動し、Addressablesグループに追加します。

Q:Addressablesを使用しない場合、重複するバンドルを削除することで解決されるディスク上のサイズと同様の問題が発生しますか?
いいえ。はい。ウェビナーとスライドでは、2つのウォーターレースのシーンを重複排除することで、ビルドサイズを大幅に削減したことを紹介しています。

Q:シェーダーバリアントの重複を防ぐには?
いいえ。シェーダは、他のアセットと同じプロセスで重複排除できます - グループ内で明示的に宣言します。

ビルドに使用する Addressables グループでアセットが明示的に宣言されている場合、そのアセットが複数のバンドルで重複することはありません。

特にシェーダーについては、プロジェクトで「Shared shaders(共有シェーダー)」グループを使用するのが一般的で、アプリの寿命までメモリが必要で、多くのアセットで共有されることが予想されるシェーダーを格納します。

Q:同じプレハブを共有する2つのUnityシーンでビルドサイズが重複しますか?
いいえ。これは、シーンが依存するプレハブがAddressablesに明示的に含まれているかどうか、また、シーンが同じバンドルに含まれているか異なるバンドルに含まれているかに依存します。

重複がどのように発生するかについては、ウェビナーのスライドやこのブログ記事のステージ4で視覚的に説明しているので、そちらを参照されたい。

覚えておくべきことは、バンドルに入るすべてのコンテンツは、その依存関係にすべてアクセスできる必要があるということだ。シーンをバンドルに入れる場合、その依存関係はすべて、どちらかである必要があります:

  • Addressablesのどこかに明示的に含まれる
  • 暗黙のうちに同じバンドルに含まれる

Q:すべてのゲームアセットが孤立したグループにまとめられてしまうのを防ぐために、指定されたグループ内の重複を比較することは可能ですか?
いいえ。はい。組み込みの重複排除ルールを実行し、Addressables Groupsウィンドウで資産をより良いグループ分けに並べ替えることができます。

あるいは、よりスケーラブルな方法として、独自のAddressables AnalyzeRulesを作成し、それをAnalyzeウィンドウに表示することもできる。組み込みルールは、AddressablesパッケージのC#として提供され、ベースラインとして機能する。

たとえば、"Character-"で始まるすべてのグループの重複をすべて見つけたいとします。暗黙の重複は "Shared-Character "グループに入れることができる。

Q:リモートビルドとローカルパスをカバーするつもりですか?
いいえ。ウェビナーでは "Addressables Profiles "と呼ばれるリモートパスとローカルパスについては触れなかった。しかし、Addressables Profilesがどのようなもので、どのように使用するかについては、このドキュメントで説明しています。

Q:Addressablesはクラウド・コンテンツ・デリバリー(CCD)とどのように連携するのか?
いいえ。CCDの統合については、このドキュメントで説明している。

Q:低解像度と高解像度のアドレサブルのバリエーションを実装するためのベストプラクティスを教えてください。
いいえ。GitHubのAddressables Sampleにサンプルがあります。

Q:バンドル・コンテンツが暗号化されている場合は?UnityDataToolはコンテンツも復号化しますか?
いいえ。いいえ。UnityDataToolがコンテンツを分析する前に、データを復号化する必要があります。

Q:あるUnityプロジェクトからバンドルをビルドし、別のプロジェクトからビルドされたアプリから実行時にバンドルをロードすることは、サポートされているユースケースですか?
いいえ。はい。これは複数のカタログを同時に使うことでカバーできる。

Q:InstantiateAsyncを使用することの欠点や、LoadAsync + 手動のInstantiateを使用した方が良い状況はありますか?
いいえ。Addressables.LoadAssetAsync()を使用し、Object.Instantiate() を呼び出すことを推奨します。Addressables.InstantiateAsync()は、パフォーマンス・コストが大きい。

Q:少なくとも1-2個のスプライトが変数として参照されているScriptableObjectがたくさんあります。スプライトをAddressableに変更したい場合、1つ1つAddressableへの参照を変更しなければならないのでしょうか、それとも何かコツがあるのでしょうか?
いいえ。これらのリファレンスを変換するには、エディタースクリプトを使うのがよいだろう。

AssetReferenceフィールドをScriptableObjectに追加することができます(Spriteフィールドは一時的に保持します)。そして、ScriptableObjectを繰り返し実行し、Addressablesでスプライトアセットを検索して、関連するAddressableAssetEntryを見つけ、アドレスを保存するか、ScriptableObjectに保存するAssetReferenceを作成するエディタスクリプトを書くことができます。

最後に、スプライトの直接参照を削除し、関連するコードをすべてAssetReferenceを使用するように置き換えることができます。

Q:WebGLゲームにアドレサブルを使用できますか?もしそうなら、何か具体的な注意点はありますか?
いいえ。そうだね。注意すべきことが2つある:まず、WebGLはスレッドをサポートしていないので、Tasksを使用しないでください。第2に、WebGLではキャッシュの動作が異なります。以前、リモートのAssetBundleのキャッシュに関する問題が発生したことがあります。

Q:Shader.Find("ShaderName")を使用する場合、これはビルドから来るのですか、それともAddressablesから来るのですか?
いいえ。これらは、Addressablesではなく、Unity Playerのビルドから来ています。Shader.Find()がAssetBundlesからの結果を返さない。

Q:似たような名前のグループがたくさんある場合、Addressables Groups ウィンドウを整理するにはどうすればよいですか?
いいえ。Addressables Groups UI を整理するために、ダッシュを使用したグループ階層を有効にすることができます。これは、同じような名前のグループをグループ化する。例えば、"Character-person "と "Character-person2 "は、UIでは "Character "というグループ分けの下に表示されます。

これはバンドルの作成方法には影響しない。これはUIの組織変更に過ぎない。

Addressablesフォーラムでご意見をお聞かせください。現在進行中の Tech from the Trenches シリーズの一環として、他のUnity開発者による新しい技術ブログをぜひご覧ください。