AssetBundleについてはまだ勉強を始めたばかりで検証中なのですが、あまりにも情報がいっぱいあるがそれぞれでやり方が違う印象があるので、
いろいろ書いていって最適解を出したいと思います。
なので、この記事はまだ(仮)です。
AssetBundleについて
スマホアプリはインストール時にサイズを50MBにに抑えないとWifi経由でのインストールしかできなくなります。
それだけではなく、オフラインのときでも使えるようにローカルにキャッシュしてほしいなど、制限が結構あります。
そこでAssetBundleを使って、追加コンテンツとしてアプリ内にダウンロードできる仕組みです。
Unityを使って何かしらのスマホアプリを作るなら、割りと使うことになりそうな仕組みですね。
AssetBundleに含められるのは、
- GameObject
- Material
- Texture
- Prefab
などなど。
AssetBundleをiOS・Android用にビルドするにはiOS Pro・Android Proのライセンスが必要になります。
なので、これらを使わなければお金は掛からないのですが、使うとなると途端に結構な金額のライセンスが必要です。
デメリットとしてメモリを多く消費するという点があるようです。
なので、このAssetBundleをどうゆう単位で作り、いつ破棄するかが結構重要になりそうですね。
また、iOSとAndroidで別に書き出す必要があり、その書き出しのタイミングで最適化がされているようです。
別々のファイルになってしまうので、Unity側でどのAssetBundleを取りに行くかの分岐が必要です。
注意点として、ScriptはAssetBundleに含められません。
iPhone側の話しですが、動的にScriptを生成することを許可していないからです。
AssetBundleを使ってみる
UnityのAssetBundleをじっくり理解する手順をまとめてみた。【アセットバンドル】 – NAVER まとめ
こちらの記事が一番読みやすかったので、これにそって試してみました。
まずはAssetBundleを作成するためのEditor拡張を入れます。
BuildPipeline.BuildAssetBundle
こちらにあるコードを参考にしてたんですが、書いてあるコードにはiOS・Android用のビルドが書かれていなかったので、
Unity AssetBundle Examples.を参考にしました。
// C# の例 // プロジェクト ウィンドウの選択されたオブジェクトからアセットバンドルを作成 // コンパイルした後は "Menu" -> "Assets" へ移動して選択肢から一つを選択して // アセットバンドルをビルド using UnityEngine; using UnityEditor; public class ExportAssetBundles { [ MenuItem("Assets/Build AssetBundle From Selection - Track dependencies")] static void ExportResource () { // 保存ウィンドウのパネルを表示 string path = EditorUtility.SaveFilePanel ("Save Resource", "", "New Resource", "unity3d"); if (path.Length != 0) { // アクティブなセレクションに対してリソースファイルをビルド Object[] selection = Selection.GetFiltered (typeof(Object), SelectionMode.DeepAssets); // via https://gist.github.com/yaeda/5410868 // require iOS Pro, Android Pro Lisence // for Android BuildPipeline.BuildAssetBundle(Selection.activeObject, selection, path + ".android.unity3d", BuildAssetBundleOptions.CollectDependencies | BuildAssetBundleOptions.CompleteAssets, BuildTarget.Android); // for iPhone BuildPipeline.BuildAssetBundle(Selection.activeObject, selection, path + ".iphone.unity3d", BuildAssetBundleOptions.CollectDependencies | BuildAssetBundleOptions.CompleteAssets, BuildTarget.iPhone); // for WebPlayer BuildPipeline.BuildAssetBundle(Selection.activeObject, selection, path + ".unity3d", BuildAssetBundleOptions.CollectDependencies | BuildAssetBundleOptions.CompleteAssets, BuildTarget.WebPlayer); Selection.objects = selection; } } [ MenuItem("Assets/Build AssetBundle From Selection - No dependency tracking")] static void ExportResourceNoTrack () { // 保存ウィンドウのパネルを表示 string path = EditorUtility.SaveFilePanel ("Save Resource", "", "New Resource", "unity3d"); if (path.Length != 0) { // アクティブなセレクションに対してリソースファイルをビルド BuildPipeline.BuildAssetBundle ( Selection.activeObject, Selection.objects, path); } } }
■gist
AssetBundle用のエディター拡張
Assets/Build AssetBundle From Selection – No dependency trackingのほうはまだ使えていないので、複数OS分のコードは書いていません。
このクラスを、
Assets/Editor
に配置します。
適当なPrefabを作成し、Prefabを選択した状態で右クリックから、Build AssetBundle From Selection – Track dependenciesを実行します。
すると、
- Asset.unity3d.android.unity3d
- Asset.unity3d.iphone.unity3d
- Asset.unity3d.unity3d
のように、WebPlayer用・android用・iphone用の3つができました。
これをDropboxに適当に配置し、URLをメモしておきます。
■参考リンク
アセットバンドルのビルド / Building AssetBundles
それでは、Unity側でAssetBundleを使ったコードを書いていきましょう。
using System; using UnityEngine; using System.Collections; public class CachingLoadExample : MonoBehaviour { void Start () { // Clear Cache Caching.CleanCache(); #if UNITY_ANDROID && !UNITY_EDITOR string url = "https://dl.dropboxusercontent.com/Asset.unity3d.android.unity3d #elif UNITY_IPHONE && !UNITY_EDITOR string url = "https://dl.dropboxusercontent.com/Asset.unity3d.iphone.unity3d #else string url = "https://dl.dropboxusercontent.com/Asset.unity3d.unity3d?dl=1 #endif StartCoroutine (DownloadAndCache ("Particle System", url, 1)); StartCoroutine (DownloadAndCache ("Sprite", url, 1)); } public IEnumerator DownloadAndCache (string assetName, string url, int version = 1) { // キャッシュシステムの準備が完了するのを待ちます while (!Caching.ready) yield return null; // 同じバージョンが存在する場合はアセットバンドルをキャッシュからロードするか、またはダウンロードしてキャッシュに格納します。 using (WWW www = WWW.LoadFromCacheOrDownload (url, version)) { yield return www; if (www.error != null) { throw new Exception ("WWWダウンロードにエラーがありました:" + www.error); } AssetBundle bundle = www.assetBundle; if (assetName == "") Instantiate (bundle.mainAsset); else Instantiate (bundle.Load (assetName)); // メモリ節約のため圧縮されたアセットバンドルのコンテンツをアンロード bundle.Unload (false); } // memory is freed from the web stream (www.Dispose() gets called implicitly) Debug.Log(Caching.IsVersionCached(url, 1)); Debug.Log("DownloadAndCache end"); } }
■gist
AssetBundleを使うサンプル
WWW.LoadFromCacheOrDownloadを使うのがミソっぽいです。
■参考リンク
アセットバンドルのダウンロード / Downloading AssetBundles
こちらの記事を読んでみると、WWW.LoadFromCacheOrDownloadを使うのがよさそうですね!
AssetBundleの読み込み方法
- 1番オススメ:LoadFromCacheOrDownload
- ダウンロードしたものが展開された状態でストレージに保存される
- 実行時の読み込みの際に、展開用のメモリが必要ない
- その分、ストレージの容量は圧迫する
- 2番目にオススメ:CreateFromFile
- 圧縮していないAssetBundleしか使えない
- 圧縮できないのでストレージの容量は圧迫する
- 非おすすめ:WWW、CreateFromMemory
- 展開に使う分のメモリが必要
- 圧縮したAssetBundleが使える → ストレージ容量は節約できる
- 過去の遺物:Resources
- AssetBundleと比べて効率が悪い。今から作るならAssetBundleを使ったほうが良い
これで、Buildすると各OSでもAssetBundleが使えます。
でも、これでやっと使えるレベル、どう使う?単位は?という道はまだほど遠い。
StreamingAssetsに配置してロードしてみる
StreamingAssetsは簡単に言えば、ファイルをそのままアプリに持っていけるフォルダだ。そこに置いたものは何であれビルド時に余計なエンコードされること無くアプリ内に配置することができる。
いまいちまだStreamingAssetsをどうゆうときに使うのか分かっていないが、普通にFileアクセスはできずWWW経由でアクセスするようだ。
これはAssetBundleにアクセスする方法と同じだから特に理解には困らないが、StreamingAssetsに置くなら、普通にAssetsディレクトリの下にPrefabとかを置くでもよいのかなーと思っていたり。
ここは次第に実感が湧いてくるんだろう。
このディレクトリにアクセスするなら、Streaming AssetとPersistent DataPathの注意点 Android、iOS Unity – 万年素人からGeekへの道こちらの記事が参考になる。
では実際にファイルをロードするコードを書いてみよう。
// StreamingAssetsからオブジェクトを取得する場合 public IEnumerator LoadStreamingAssets(string assetName, string fileName, int version = 1) { #if UNITY_ANDROID && !UNITY_EDITOR string url = "jar:file://" + Application.dataPath + "!/assets/" + fileName; Debug.Log("AndroidUrl: " + url); #elif UNITY_IPHONE && !UNITY_EDITOR string url = "file://" + Application.dataPath + "/Raw/" + fileName; #else string url = "file://" + Application.streamingAssetsPath + "/" + fileName; // Application.dataPath + "/StreamingAssets/" + fileName #endif Debug.Log("AssetBundleUrl: " + url); // キャッシュシステムの準備が完了するのを待ちます while (!Caching.ready) yield return null; // 同じバージョンが存在する場合はアセットバンドルをキャッシュからロードするか、またはダウンロードしてキャッシュに格納します。 using (WWW www = WWW.LoadFromCacheOrDownload (url, version)) { yield return www; if (www.error != null) { throw new Exception ("WWWダウンロードにエラーがありました:" + www.error); } AssetBundle bundle = www.assetBundle; if (assetName == "") Instantiate (bundle.mainAsset); else Instantiate (bundle.Load (assetName)); // メモリ節約のため圧縮されたアセットバンドルのコンテンツをアンロード bundle.Unload (false); } // memory is freed from the web stream (www.Dispose() gets called implicitly) Debug.Log(Caching.IsVersionCached(url, 1)); Debug.Log("DownloadAndCache end"); }
各パスの説明は、ストリーミングアセット / Streaming Assetsに記載されている。
あと、どうもversionを上げてもキャッシュを見に行ってしまうことがあるようで、その場合はurlの後ろにnow的なものを付けてアクセスするのが良さそうです。
System.DateTime.Now.ToString("yyyyMMddhhmmss")
こんな感じをQueryStringに含めるのがいいんですかね。
アセット依存関係の管理 / Managing asset dependencies
複数のAssetBundleに同じオブジェクトやシェーダーが含まれてしまう場合は、まったく別でメモリ確保されてしまうため、
依存関係を管理することができるようです。
BuildPipeline.PushAssetDependencies(); BuildPipeline.PopAssetDependencies();
このメソッドを使うようですが、結構ハードコーディングが必要になりそうだなーという印象です。
keijiroさんのgithubリポジトリがすごく参考になります。
keijiro/unity-shader-bundle
参考リンク
「もののけ大戦“陣”」製作事例
Unity での asset bundle による追加コンテンツの扱い方
Asset bundleなどの、Unity3d基礎知識
こちらは間違いなく見たほうがよい。
[Unite Japan 2013]シーン/メモリ/アセットバンドル
技術評論社
売り上げランキング: 4,443