AssetBudleの運用で経験した苦悩と挫折と解


知識ゼロの状態からAssetBudleの運用を頼まれ、苦悩と挫折の中、培った経験を記述します。
Unityバージョンは4.6.8です。5系ではだいぶいろいろ見直されたと聞いているので楽しみです。

#ビルドをしないと確認できないという苦悩
##苦悩
これはどういうことかというと、例えばあるAssetBudleに含まれるマスターデータの数値がおかしいなどでリソースを差し替える場合

---

データ入力者から納品
↓
AssetBudleビルド
↓
配信サーバに作成したAssetBudleをアップロード
↓
クライアント(スマートフォン)に配信サーバからダウンロード
↓
データ入力者で確認

---

というフローを踏み、ここで
「あ、直すのはこのマスタではなかった！！」
とすると...

---

データ入力者から納品
↓
AssetBudleビルド
↓
配信サーバに作成したAssetBudleをアップロード
↓
クライアント(スマートフォン)に配信サーバからダウンロード
↓
データ入力者で確認

---

という**無限ループ**がはじまったのです！！

もちろん、ちゃんとデータ入力をすればよいのですが、ときには複雑なデータを入力をすることもあったり、人間は間違えるのものなので、そこに問題があるとは考えません。

このフローの一番のボトルネックは
**ビルドするの非常に時間がかかる**
というところです。

規模によりますが、だいたいこの1フローあたり10分～1時間はかかると思ってよいでしょう。

/　||￣￣||　∧＿∧
|.....||＿＿|| （ 　　　 ）　　どうしてこうなった・・・
|￣￣＼三⊂/￣￣￣/
|　　　　|　( ./　　　　 /

なんでこうなったのかよくよく調べてみると、Unity公式にこんな記述がありました。
「アプリケーションの作成は反復的なプロセスなので、かなりの頻度でアセットを何度も修正し、これによりテストするためにはアセットバンドルを毎回の変更の度に再ビルドする必要が出てきます。エディターにアセットバンドルをロードすることは可能ですが、これは**推奨できるワークフローではありません**。その代わりに、エディターをテストする際は、**Resources.LoadAssetAtPath ヘルパー関数を使用**して、アセットバンドルを使用するときに再ビルドを回避できます。」

うん、つまり実機で確認する必要がないものはUnityエディター上で確認すればよいといこうとか!!! ![hirameki.png](https://qiita-image-store.s3.amazonaws.com/0/17192/7cf5baad-a221-3398-8323-f64c52ec4be0.png)

---

データ入力者から納品
↓
UnityエディターのResourcesフォルダ中に配置

---

このフローならビルドがないので反復が楽なはず。

なので、こんな感じのコードを(※わかりやすくするため簡略化しています)

```csharp
download = WWW.LoadFromCacheOrDownload( url, version );
AssetBundle assetBundle = download.assetBundle;
Obj = assetBundle.mainAsset;
```

こうすればいいだろう

```csharp
#if UNITY_EDITOR
    Obj = Resources.LoadAssetAtPath(assetPath, typeof(T));
#else
    download = WWW.LoadFromCacheOrDownload( url, version );
    AssetBundle assetBundle = download.assetBundle;
    Obj = assetBundle.mainAsset;
#endif
```

##挫折
と思ったけど、このようなロードする処理が複数に記述されていて、しかも微妙に違う書き方で散乱していたのです...。
影響範囲がでかすぎて触れないんゴゴゴゴ...

##解
- ロードする処理は一箇所にまとめましょう。
- 実機で確認する場合はやっぱりビルドする必要があるのでマシーンはハイスペックPCで。
- UnityのキャッシュサーバーいれればAsset import早くなるかも。結果ビルドも早くなる可能性がある。
- ローカルPCやビルドPCは、Unityを夜中に起動させるバッチを仕込んでおき立ち上げを早くしよう。

#読み込みが遅い苦悩
##苦悩
これはどういうことかというと、AssetBudleをゲーム内でつかう一般的な方法は

```csharp
WWW.LoadFromCacheOrDownload(url, version, crc)
```

このメソッドだと思うんだけど、ざっと調べた限りこのメソッドは
AssetBudleを解凍して、ディスクにキャッシュしている動作があるように思えるのですよね。
キャッシュはMacだと「/Users/ユーザ名/Library/Caches/Unity」ここらへんにあります。
でなにに苦悩したかというと

```csharp
yeild return www;
```

ここの次の処理に入るまで凄く時間がかかったのです。
正確に言うと初回は凄く時間がかかったけど、キャッシュにのると次から早いみたいな感じ。
まあ、それはそれで次からは早いのならいいんじゃねとか思ったけど

**諸事情により**←ちょっといろいろあったのでここについてはまたの機会に書ければと思います

- キャッシュを削除している

```csharp
Caching.CleanCache();
```

上記を通常プレイの範疇でのゲーム内で実行しなければならない。

- Caching.maximumAvailableDiskSpace
- Caching.expirationDelay

上記も小さいサイズ、短い期間した設定しなければいけない。

すなわち、ほとんどキャッシュが効かない状態になってしまっているので
**くそ重い!!**(他にもAssetBudleをまとめる単位が微妙と原因はあるけどね)

##挫折
**諸事情とやら**を解決するにはAssetBudleの設計(まとめる単位など)を全体的に見直する必要があり
運用中のアプリでは事故率が高いので断念

##解
容量は増えるがAssetBudleをあらかじめ解凍したようなもの(=非圧縮のAssetBudle)をつかうこと解決。
さすがに全部を非圧縮すると、とんでもない容量となるので読み込み頻度が高そうなところ(イベントのバナーなど)を非圧縮に。

#.metaファイルを管理しなかったための苦悩
ちょっと最後の苦悩はAssetBundleの話とはだいぶ違うかもしれないけど、実際に起こったことなので記述します。

##苦悩
なにを思ったかよくわからないけど、リポジトリのROOTフォルダの.gitignoreに記載が下記がありました。

```shell-session
.gitignore → コレ
├─script
│    ├─a.cs
│    └─b.cs
└─image
     ├─c.png
     └─d.png
```

```shell-session:.gitignore
*.meta
```

こうすると.metaファイルはすべてgit管理されないことになってしまい、**metaファイルに特別な意味をもつリソース**(画像など)のAssetBundleのmetaファイルがなにが正しいかわからなくなってしまったのです。
だから、ある人のローカル環境では問題なかったけど、他の人のローカル環境で確認したら問題あったり、ローカルでは問題あったけど、ビルド機でAssetBudleをビルドしたら、なぜか問題なかったり、**真実の.metaファイルはいずこに**...。

##挫折
なので、*.metaの記述を取り外したかったののですが対象ファイルが多すぎて確認が無理だったので断念。

##解
しかたないので管理するフォルダに下記のようなgitignoreファイルを追加。

```shell-session
.gitignore
├─script
│    ├─a.cs
│    └─b.cs
└─image
     ├─.gitignore → コレ
     ├─c.png
     └─d.png
```

```shell-session:.gitignore
!*.meta
```

これはどういうことかいうと、ROOTではgit管理外だけどimageフォルダ下は管理するという設定です。
gitignoreファイルがそもそも、管理しないものを記述するファイルなにそれを否定するというカオス設定に...。
それでまたimageフォルダ下のどこかのフォルダでひっくり返したりするかわけわからない状態に...。

#まとめ
- Unityエディター上からは、AssetBudleビルドなしで確認できるようにしよう。
- ビルドPCはハイスペックPC、キャッシュサーバー、バッチなどであらかじめ起動させておくなどの処置をしておきビルドのスピードを上げよう。
- AssetBudleの設計、キャッシュや非圧縮などの使い分けをしっかりし高速な読み込みを実現しよう。
- 基本的にmetaファイルはGUIDだけの記述などで意味のないものが多いけど、意味のあるやつもあるので全部管理するべき。