Unity5 AssetBundleまとめメモ

# Unity5 AssetBundle まとめメモ
今更ながら勉強した内容を忘れないためのメモ
Unity4から大幅に変更された  
~~Unity4時代のAssetBundleは互換性がない()~~  
Unity5.3からさらに変わったみたい  
[テラシュールブログ【Unity】Unity 5.3からAssetBundleはどう変わるのか…まとめ](http://tsubakit1.hateblo.jp/entry/2015/12/16/233336)  
タイプツリーが存在すればエディタのバージョンがあがっても互換は可能

## AssetBundleのビルド

### ビルドの前準備
アセットバンドルの名前にはいくつかの制限がある

* 小文字のみ
* path形式  
	Prefabs/Chara.unity3d（.unity3dが必要かどうか不明：バリアントがつくと拡張子がバリアントになる）
* 同名禁止
* フォルダに名前を与えると直下にあるサブも含めたすべてがアセットバンドル化する
* 未使用なのにAssetBundle名がリストに登録してある

対象のアセットのインスペクター下にある`Asset Labels`という部分にそれぞれ  
アセットバンドル名とバリアント名を記入できるメニューがある  
左のポップアップメニューボタンがアセットバンドル名で、押してポップアップされるメニューから`new`メニュー  
を選択してそこにpath形式で`Assets/AssetBundle.unity3d`という風に書き込む

![AssetBundle00.png](https://qiita-image-store.s3.amazonaws.com/0/83393/5e0c5e02-1068-d0b7-301c-0ef77b1f6d2c.png)
![AssetBundle01.png](https://qiita-image-store.s3.amazonaws.com/0/83393/b4afc224-8b7a-b674-8aa5-480271cea8f7.png)


ビルドを実行すると、指定した出力先でAssetsフォルダが生成されその中に生成される

### ビルドスクリプト
未だにUnity側でAssetBundleを生成してくれる機能はないので、自前で用意する必要があります  
使用するメソッドは  

```CSharp
BuildPipeline.BuildAssetBundles(
	string outputPath,
	BuildAssetBundleOptions assetbundleOptions = BuildAssetbundleOptions.None,
	BuildTarget targetPlatform = BuildTarget.WebPlayer);
```
|型|変数名|概要|
|:-:|:-:|:-:|
|string|outputPath|生成したアセットバンドルの出力先|
|BuildassetbundleOptions|assetbundleOptions|ビルドオプション|
|BuildTarget|targetPlatform|対象のプラットフォーム|


#### インスペクタでのアセットバンドル名設定によるビルドスクリプト
ビルド前準備で指定したアセットバンドル名  
アセットバンドルをビルドするときは以下のようなエディタスクリプトを組み、実行するだけで良い  
プロジェクトビューに適当にEditorフォルダを作り、ビルド用スクリプトを用意する

```CSharp
	[MenuItem("AssetBundles/Build")]
	static void BuildAssetBundles()
	{
		string outputPath = @"";
		BuildAssetBundleOptions options = BuildAssetBundleOptions.None;	// 特になし
		BuildTarget target = EditorUserBuildSettings.activeBuildTarget;	// 現在のビルドターゲットを取得
		BuildPipeline.BuildAssetBundles(, BuildAssetBundleOptions options, BuildTarget target);
	}
```

#### ビルドマップ方式のビルドスクリプト
[AssetBundleBuild](http://docs.unity3d.com/ScriptReference/AssetBundleBuild.html)を利用した方法  
AssetBUndleBuildの配列を用意し、それぞれの要素ごとの`assetBundleName`にアセットバンドル名を指定  
`assetNames`という文字列配列にbundle化するアセットのパス"Assets/"から始まるパス名（拡張子付き）で指定する  

```CSharp

	// AssetBundle一つを作成する場合
	AssetBundleBuild[] buildMap = new AssetBundleBUild[1];
	buildMap[0].assetBundleName = "assetbundle.unity3d"
	buildMap[0].assetBundleVariant = "hd";	// バリアントをつける場合は入れる
	// 含めるアセットが一つの場合
	buildMap[0].assetNames = new string[1]{"Assets/Resoruces/asset.prefab"};
	BuildPipeline.BuildAssetBundles(@"AssetBundle/Android", buildMap, 0, BuildTarget.Android);
```

### BuildAssetBundleOptions
これらの設定を`BUildPipeline.BuildAssetbundles`に指定する

Noneに指定しても、依存関係を有効化する`CollectDependencies`とオブジェクトのハッシュIDを使用する`DeterministicAssetBundle`が有効になっている

* IngoreTypeTreeChanges  
	タイプツリーが変更されても無視 = ソースが変更されても変化したとは見なされない
* DisableWriteTypeTree  
	タイプツリー自体を削除
* ForceRebuildAssetBundle  
	強制的にリビルド。変更が認められない場合はビルドしないのをさけるため
* AppendHashToAssetBundleName  
	ハッシュを追加する。生成されるファイルの名前の下にハッシュが追加され、マニフェストにも記載される

## AssetBundle Manifest
アセットバンドルに関する情報が書き込まれたテキストファイル。全体用と個別用が存在する

* シングルマニフェスト  
	全体管理のマニフェスト。依存関係等が乗る。実行時に読み込んで使用する。
	+ すべてのAssetBundle名
	+ アセットの依存関係
* 個別マニフェスト  
	対応する名前のアセットバンドルのマニフェスト。インクリメンタルビルドにだけ関係する。実行時には読み込まない
	+ CRC
	+ アセット名
	+ 依存関係
	+ Hash
	+ ClassTypes

インクリメンタルビルドは、`BuildPipeline.BuildAssetbundles()`での出力先に  
既に同名の.manifestファイルがあった場合に前回との差を比較してビルドを行うかどうかを判定する

## Variant
HDとSDを同じアセットとして扱える仕組み  
アセットバンドル名を登録するボタンの隣にある小さなボタンで設定を行う  
小文字のみで指定するとファイル名の最後に拡張子がつけられる

![AssetBundle03.png](https://qiita-image-store.s3.amazonaws.com/0/83393/f6310cee-0c39-78e9-9d80-d44a1bcddc3b.png)
![AssetBundle02.png](https://qiita-image-store.s3.amazonaws.com/0/83393/8973f606-75aa-7048-a600-1138e45434d3.png)


## AssetBundleのダウンロード
サーバ・StreamingAssetsフォルダなどに置かれたアセットバンドルをゲームにロードする

### StreamingAssetsの場合
出力したファイルをプロジェクトのStreamingAssetsフォルダ以下に

ビルド時に`bool BuildPipeline.GetCRCForAssetBundle(string targetPath, out uint crc);`を使用し  
各々のAssetBundleのCRCを取得しておく  
targetPathには出力されたアセットバンドルへのパスを拡張子まで入れる。out修飾子にCRCが帰ってくる。戻り値は成否



### WWW.LoadFromCacheOrDownload
バージョン

	WwW www = WWW.LoadFormCacheOrDownload(string url, int version, uint crc = 0);

ハッシュ版

	WwW www = WWW.LoadFormCacheOrDownload(string url, Hash128 hash, uint crc = 0);

バージョン版では、Unity4から続くキャッシュ解放問題は解消されていない(Unity5.2の時点)


### シングルマニフェストのロード
Manifestファイルに関しては`WWW(string URL)`こちらを使用する  
webPlayerやEditor実行などでStreamingAssetsフォルダを使用する場合は`"File://"`などに注意する  
AssetBundleのみをダウンロードすれば良いという場合は必須ではない

```CSharp
	string URL = @"";
	WWW download = new WWW(URL);
	yield return download;
	AssetBundleRequest request = null;
	AssetBundleManifest manifest = null;
	request = download.assetBundle.LoadAssetAsync("AssetBundleManifest", typeof(AssetBundleManifest));
	yield return request;	// ロードする
	manifest = request.asset as AssetBundleManifest;
```
Manifestクラスの主な用途
```CSharp
	// すべてのアセットバンドル名を取得する
	string[] allAssetNames = manifest.GetAllAssetBundles();
	// バリアントを持ったアセットバンドル名をすべて取得する
	string[] variants = manifest.GetAllAssetBundlesWithVariant();
	// 指定したアセットバンドルのハッシュを取得する
	Hash128 hash = manifest.GetAssetBundleHash(string assetBundleName);
	// 依存関係を持ったアセットバンドル名をすべて取得する
	string[] dependencies = manifest.GetAllDependencies(string assetBundleName);
```

[AssetBundleRequest](http://docs.unity3d.com/ja/current/ScriptReference/AssetBundleRequest.html)  
[AssetBundleManifest](http://docs.unity3d.com/ja/current/ScriptReference/AssetBundleManifest.html)

```WWW.LoadFromCacheOrDownload(string url);```

### アセットバンドルの利用

```assetbundle.LoadAsset(); ```

NGUIのAtlasなど同名のオブジェクトが複数ある場合は拡張子(.prefab)まで指定しないとロードできない


## Demo
アセットが必要になったタイミングで要求を行い、そこでダウンロードが行われる

[demo.zip](http://files.unity3d.com/vincent/assetbundle-demo/users_assetbundle-demo.zip)  

### AssetBundleManager.cs

ロードが完了したアセットバンドルを管理する、アセットバンドル名をキーとした辞書  
ロードを管理したWWWをUpdateで監視し、Errorがあったらm_DownloadingErrorsにキーをアセットバンドル名、valueにエラー内容を書き込む  
ダウンロードが完了すれば、Disposeし辞書から削除するそして
```CSharp
static Dictionary<string, LoadedAssetBundle> m_LoadedAssetBundles;
static Dictionary<string, WWW> m_DonwloadingWWWs;
static Dictionary<string, string> m_DownloadingErrors;
static List<AssetBundleLoadOperation> m_InProgressOperations;
```

## キャッシュの個別クリア

#### CRCを利用した方法
`WWW.LoadFromCacheOrDownload()`時に第三引数に当該のCRCを渡す。
このとき、Version値を固定にしているとCRC値が違えばキャッシュを削除してダウンロードし直す

通常のAB用のとは別の`WWW.LoadFromCacheOrDownload()`を用意し、CRCに適当な値を渡すことでもキャッシュがクリアされる
この場合、通常のCacheOrDownloadにCRCの指定はいらない

	WWW.LoadFromCacheOrDownload(rul, version, crc);



※Unity5.3になってからか、エラーが吐かれるようになったぽい？

	CRC Mismatch. Provided 1, calculated 99199dcb from data. Will not load AssetBundle 

----

ロードのリクエスト  
バリアントを指定する。  
バリアントに対応したアセットバンドルがある場合はそちらの名前でロードを行う

アセットバンドル名にさらにバリアントを使用するとそのアセットバンドル名内でバリアントが使用されていないアセットが登録されている場合はビルドエラーとなる
同名のアセットバンドル。片方にはバリアントがあるがもう片方にはNoneが設定されている場合別のアセットバンドル名と認識して、同名禁止のルールに抵触するため

### アセットバンドル全体のキャッシュクリア

	Caching.CleanCache();

を使用すればすぐに消える

### アセットバンドルのキャッシュの状況

	Caching.SpaceOccupied()

バイト単位のディスク使用量を返します


### 備考

1. サーバーのURL アセットバンドル名がないとバリアントの指定ができない
2. パス指定することにより階層を持ったアセットバンドル

### 参考
[http://www.slideshare.net/MakotoItoh/unite2015-assetbundle-final](http://www.slideshare.net/MakotoItoh/unite2015-assetbundle-final)