【Unity】AssetBundleを利用してアプリサイズを削減するアプローチ
Unity 2017はコチラを使いましょう。
AssetBundleの依存関係等があり、必ずアセットを含める必要がある場合はアセットをローカルに保持するのが有益かもしれません。
-------------------------------------------------------------
AssetBundleにてリソースを圧縮し、アプリサイズを削減するアプローチについてです。うまく行けばアプリのサイズが非常に小さくなります。
で、このアプローチは実際どうなんでしょう。確認してみました。
Resourcesに格納したアセット
アプリケーションを作る上で、かならずアセットを必要とします。特に動的にアセットを追加・削除する場合、Resourcesが手っ取り早いです。
でResourcesフォルダ以下にアセット郡を配置した場合、該当のアセットとアセットが参照しているアセットが、resources.assets.resSという単一のファイルにまとめて格納されるっぽいです。
ここで何が問題かと言えば、このresources.assets.resSファイルはAssetBundleで言う所の「非圧縮」の状態で保持されています。つまり、データを読む場合は高速に読めますが、ファイルサイズは大きい・・・という訳です。
特にテクスチャが生データに近いTrueColorを使用していた場合、そのサイズはかなりのサイズに至ります。*1
参考までに、ユニティちゃん立ち絵パックvol1~3(1枚だいたい1300x2048ピクセル)をTrueColor(RGBA32)で50枚ほど突っ込んだ場合、だいたい845MBという中々にグレートなサイズに育ちました。
AssetBundleに格納するアプローチ
なのでリソースをAssetBundleに格納し、AssetBundleをLZ4で圧縮するアプローチを取ります。これは以前のLZMA方式では重すぎる&メモリ食い過ぎるで現実的ではありませんでしたが、LZ4によりかなり現実的なアプローチになってきています。
コンセプトは単純、Resourcesに格納していたアセット郡をAssetBundleに格納し、AssetBundleをStreamingAssetsとしてアプリ内にそのまま格納・取り出すだけです。
AssetBundleを生成する
AssetBundleを生成する手順は基本的に普通と同様です。
つまり、ResourcesフォルダをAssetBundleResourcesと改名し、assetbundle nameにresourcesとでも設定するだけです。
あとはAssetBundleを生成すればresourcesという名前のAssetBundleが作成されます。
AssetBundleの生成時にはBuildAssetBundleOptions.ChunkBasedCompressionがオススメです。メモリ消費量及び展開速度で有利です。何も設定しなければLZMA(高圧縮)で保持されます。
AssetBundleの生成はAssetBundle Manager使うよりも、もうそのままStreamingAssetsに出しちゃった方が楽な気はします。実際にはプラットフォーム毎にStreamingAssetsを差し替えなければいけないのでケースバイケースですが。
AssetBundleを読み込む
AssetBundleのファイルをResourcesに変わりコレを読みます。また、サスペンド等の理由がない限り閉じません。
AssetBundleの読込は原則としてAssetBundleLoadFromFileを使用します。なぜかと言えば早いからです。また、LoadFromMemory等と比較して開きっぱなしでも殆どペナルティが無く、実は負荷のかかる開け閉めコストを削減出来ます。
iOSやPCはStreamingAssetsに格納したファイルはApplication.streamingAssetsPath で指定されたパスに格納されています。なので、ファイルをココから取得すればOKです。
幾つかの問題点
さて、このアプローチですが結構問題があります。というか、PCとiOS以外では逆効果になるかもしれません。
apkとipaは圧縮されている
この方法によるメリットは「resorucesが圧縮されていない」この1点に尽きます。そして特にモバイル上での圧縮の理由の大半が「apk及びipaのサイズ削減」にあります。
ここで問題となってくるのが、apk及びipaはzip圧縮されているという事実です。つまり、assetbundleで別に圧縮せずともソレナリに高い圧縮率を誇る訳です。
具体的に言えば、上で紹介している845MBのリソースはapkにする際に70MB近くまで圧縮されました。これをAssetBundle格納版で試した所78MBとAssetBundle格納版の方が大きくなりました。
これらの事から100MBルールを突破する為のアプローチとしては有効ではなさそうです。
なおAndroidはZipのまま保持するから問題無いのですが、iosはインストール時に圧縮を解凍します。例えば今回の場合ios版はインストール前は70MB前後ですがインストール後に800MB近くになるという恐ろしい可能性を持っている訳です。
iOS版でアセット群をAssetBundleで保持する事により、この問題を回避する事が出来る点はメリットとして上げても良さそうです。似たような理由で、PC版もかなりファイルサイズ削減になるので、こっちのほうが有効そうな感じはあります。
AndroidでLoadFromFileが使いにくい
先ほども書いたとおりAndroidはアプリケーションをZipで圧縮された状態で保持しており、実行時に解凍しています。つまり、単純なIO処理で内部ファイルを取得する事が出来ません。そのためAndroid系はStreamingAssetsからファイルを取得する際にはWWWを用いるもしくはapkを解凍してファイルを取り出すのが常套手段となっています。
要するにAssetBundle.LoadFromFileをStreamingAssetsに対して行えないので、必要ならば。初回起動時等にキャッシュフォルダに一度ファイルをキャッシュする必要がありそうです。
なお、このデータを読む際に「解凍」が必要になる点から、AndroidでResourcesを読み込む際にはiOSと比較してそこそこペナルティがあると言われてます。
開発サイクルに一手間加わる
AssetBundleから取り出す為には、AssetBundleに格納する必要があります。そのため、開発時にAssetBundleに圧縮する…といったフローがどうしても加わります。
この手順を外部サーバー等で行い、圧縮済みのリソースを配ることで実は最終的な時間は安くなりそうな気がしますが(開発人数による)、時間がかかるのは非常に良くありません。勿論AssetBundleを単一ではなく複数に分割することで、再ビルドの時間をかなり減らせると思いますが。
感想
割りと便利そうに思えたアセットのAssetBundle化ですが、実際に検証してみた限りiOSやPCでは有益ですが、Androidではむしろしない方が良さそうです。この開発フローのボトルネックも大体Androidのせいです。
使うならば、AndroidはResources・iOSはAssetBuindleを呼び出すようなAPIを用意するような感じにするのが良さそうです。。
関連
*1:TrueColorは綺麗だけど実際でかい。