45.1K Views
April 14, 23
スライド概要
2021年10月に銀座Unityもくもく会でLTした資料です。
フリーランスのTAです。 主にもくもく会でのLT資料などをアップロードしたいと思います。
リソースを適切に管理しよう おかひろ
自己紹介 おかひろ Twitter:@okahiro_p フリーランスのゲームプログラマー ソシャゲ4年半、ハイパーカジュアル半年 最近はHoudiniを勉強している 東方放置譚、東方地風陰、東方弾闘戯というゲームをリリース
はじめに ※素材:ゆるドラシル RPGツクール 素材提供サイト (yurudora.com) こんな感じのキャライラストをゲームに使いたい キャラの数は数十体(アップデートで増える予定あり) キャライラストは綺麗に見せたいので、解像度高くしたい
画像一つの容量 1385x2048で約11MB 実際はこんなに大きく使うことは少ないと思う が、後の検証がわかりやすいようにわざと大き くしておく。
作成する画面の仕様 左上の“LoadChara”ボタンを押したらキャライ ラストを画面に表示する(今回はランダムに した) キャライラストが二枚同時に表示されること はない(切り替えたら、前のイラストは不 要)
Unityでの実装方法 SceneのGameObjectにアタッチ、もしくはそれにアタッチされている ScriptableObjectなどにアタッチ Resources.Load AssetBundle/Addressables
SceneのGameObjectにアタッチ ここに全キャラのイラスト (今回は20枚)をアタッチ!
SceneのGameObjectにアタッチ Sceneに遷移した瞬間に、20 キャラ分(約220MB)のメモリが 確保されている!! (キャラを表示していなくてもメ モリ上にのっかる) キャラが100体いたら1GB以 上!!! 恐ろしい・・・ また、Sceneの最初の読み込みに 時間がかかる。 EssentialPhone(PT-1)で750ms程度。 (キャラ20体のとき) ※実機Profileの結果です
SceneのGameObjectにアタッチ SceneのGameObjectに直接アタッチでなく例えばSceneにはScriptableObject がアタッチされていて、リソースがそのScriptableObjectにアタッチされてい ても結果は同じ リソースがPrefabにアタッチされていて、SceneにはPrefabがアタッチされて いる形でも結果は同じ Sceneがロードされるときに一気にメモリに展開されるので、Scene遷移も重い 別のSceneに遷移すると、アタッチされている分のメモリは解放される
Resources.Load Resourcesフォルダにキャラのイラストを入れておく (Assetsの下であれば階層はどこでもよい)
Resources.Load Sceneロード直後(まだイラストはLoadしていない) はイラストのメモリは確保されていない イラストをロードしたら、該当のイラストの分だけメ モリ確保された イラストを切り替えたら、読み込んだ分のメモリが確 保されている。 (画面に表示されているイラストは1枚のみ) Resources.UnloadUnusedAssets を実行すると、現在表 示されている(使われている)イラスト以外のメモリ は解放される
Resources.Load メモリ確保についてはある程度適切に行われていそう Resourcesフォルダに入っているファイルが多くなると、アプリの起動に時間 がかかるようになる 適切な箇所でResources.UnloadUnusedAssetsは推奨 内部的に索引のようなものを作っていると思われる そもそもUnityが公式で推奨していない https://learn.unity.com/tutorial/assets-resources-andassetbundles#5c7f8528edbc2a002053b5a7
AssetBundle AssetBundleをビルドしておき、StreamingAssetsに格納し ておく。 ※同じファイル名のファイルのひとつはAssetBundle、も うひとつはmanifestファイル。
AssetBundle Sceneロード直後(まだイラストはLoadしていない) はイラストのメモリは確保されていない イラストをロードしたら、該当のイラストの分だけメ モリ確保された イラストを切り替えたら、読み込んだ分のメモリが確 保されている。 (画面に表示されているイラストは1枚のみ) AssetBundle.Unload(true)を実行するとメモリは解放 される。 現在表示しているイラストでもUnloadすれば解放され、 画面上のイラストも消える。 初期化(Manifest読み込みなど)処理112ms、読み込み処理時間は60~90ms程度 (暗号化なし。LZ4圧縮) EssentialPhone PH-1で測定
AssetBundle メモリ管理をきちんと行うことができる 適切な場所でAssetBundle.Unloadを呼ぶ必要がある 使い終わったからといって勝手には解放されない AssetBundleはビルドする必要がある プラットフォームごとにビルドが別 対象アセットに更新があったら、またビルドが必要 AssetBundleのAPIを呼ぶ処理を結構自前で用意しないといけない AssetBundleのビルドを行うEditor拡張 AssetBundleの依存関係を取得/解決 CDNからダウンロードしたAssetBundleをローカルキャッシュする
Addressables
Addressables BundleModeを“Pack Together”にすると、Groupごとにファイルがまとめられる。 読み込み処理時間は初回(小さなファイル読み込み)が910ms、 それ以降初回が1420ms、以降は40~70ms程度(暗号化なし。LZ4圧縮) EssentialPhone PH-1で測定
Addressables BundleModeを“Pack Separetely”にすると、Assetごとにファイルが分かれる。 読み込み処理時間は初回(小さなファイル読み込み)が1200ms、 次回以降は150-170ms程度(暗号化なし。LZ4圧縮) EssentialPhone PH-1で測定
AddressablesのBunldeMode別読み込み時間 本命の画像ファイルの前に、小さなファイルを一度読み込んでおく 初回のロード時にCatalog読み込みなど含めた初期化を行っている? 大体1000msぐらいかかった(EssentialPhone PH-1で測定) Pack Together Pack Separately 1回目の画像読み込み 1400ms 150-170ms 2回目以降の画像読み込み 40-70ms 150-170ms PackTogetherは初回で30MBのファイルをロードしているか、1回目だけ時間がかかる。 ロードしたファイルに含まれている別の画像をロードするのは早い。 すべてReleaseすると、もう一回30MBファイルロードしている模様 PackTogetherでロードしても、Profilerでは読み込んだファイルのみメモリに乗っているように 見えた PackSeparatelyは画像ごとにファイルが別れているため初回に時間がかかるというのはないが、 2回目以降も速度は変わらず、Togetherよりも時間がかかっている。
Addressables メモリ管理をきちんと行うことができる 適切な場所でAddressables.Releaseを呼ぶ必要がある Addressablesはビルドする必要がある プラットフォームごとにビルドが別 対象アセットに更新があったら、またビルドが必要 アプリのビルド前に自動的にAddressablesビルドを行うスクリプトを挟んでおくと 便利 AssetBundleと違い、自前で用意しなくてはいけない処理はあまりない 使い終わったからといって勝手には解放されない 大体用意してくれている 暗号化とか独自キャッシュを使おうとすると、カスタマイズが必要
AssetBundle/Addressablesの分け方 複数のAddressablesが別の同じリソースをアタッチしている場合 4つのAddressables アセットが同じ Spriteをアタッチし ている
AssetBundle/Addressablesの分け方 ビルドしたファイルは、4つそれぞれにSpriteが含まれる →本来のSpriteは1個だけなのに、容量は4倍になっている それぞれのAddressableアセットをロードすると、もとのSpriteは同じなのに 別Spriteとしてメモリを確保してしまう
AssetBundle/Addressablesの分け方 この場合、ScriptableObject(Addressables)がアタッチしているSpriteもAddressables に登録する。 こうすると、ビルドしたときにSpriteがそれぞれの中に含まれず、独立した Addressablesアセットになる。 ScriptableObject側からは依存関係がある扱いになり、 ScriptableObjectがロードされるとSpriteも依存関係が解決されて一緒ロードされる。
AssetBundle/Addressablesの分け方 Chara20.pngのAddressablesアセットが別になり、ScriptableObject側の容量は減った。 Profilerで該当のすべてのScriptableObjectをロードしても、Spriteはひとつな のでメモリも一つ分の確保になった。
まとめ たくさんのリソースを使う場合、使い方や量、種類などをきちんと把握し、適切な 管理方法を選択する必要がある。 Inspectorにアタッチ 一番楽 量が多くなるとSceneのロード時間が増え、メモリ展開量も増える Resources.Load 楽 量が多くなるとアプリ起動時間が増える Unity公式で非推奨 AssetBundle/Addressables 習得の壁が高い ビルド 非同期ロード 解放 ロード時間やメモリ確保量を制御できる 大きなプロジェクトではほぼ必須
ご清聴ありがとうございました