Unity Addressable Asset Systemの使用方法と自動化の機能調査

#はじめに
実装したソースはこちら
https://github.com/zi-su/ScriptLibrary/tree/master/Editor
AASUtility.csにまとめています。

Unityではアセット管理としてAssetBundleがあります。
このAssetBundleを使い膨大なアセットをまとめてアセット管理を行ってきました。
そのためにそれぞれAssetBundleManagerクラスを作成していたと思います。UnityがAssetBundleManagerを提供していましたが現在ではサポートされておりません。
https://bitbucket.org/Unity-Technologies/assetbundledemo/src/default/
AssetBundleクラス自体は低レベルなクラスであり、実際のゲーム開発で使いやすくするために拡張が必要です。
オレオレAssetBundleManagerを作り出すと参照カウント付きクラスで隠蔽したり、依存バンドルを調べてロード・アンロード処理を書いたりすることが必要です。

こういった面倒な実装とAssetBundleの仕組みをもっと楽にしようということでAddressableAssetSystem（AAS）が作られました。
https://blogs.unity3d.com/jp/2019/07/15/addressable-asset-system/
Unity2018.3以降では使用可能ということです。
このAddressableAssetSystemの使い方、機能、ゲーム開発に使えるのかを調べていきます。

###使用したバージョン
Addressables ver1.2.4
Unity 2019.3.0b4

#AddressableAssetSystemとは
##公式ドキュメント
まずは公式ドキュメントです。
**一次ソースを調べることは何より重要**です。
https://docs.unity3d.com/Packages/com.unity.addressables@1.2/manual/index.html
UniteTokyo2018の講演
https://learning.unity3d.jp/355/

GettingStartedを読めば最低限の導入まではできます。
1. PackageManagerからAddressablesをインストール
2. 初期化ウィンドウが出るので初期化
3. 各アセットのインスペクタでアドレスを指定
4. Window->AssetManagement->Addressablesを開く
5. 各アセットをグループに分ける
6. BuildからBuildPlayerContentでアセットバンドルを作成

GettingStartedだけではロード処理しか書いていないのでこのままではメモリに残り続けます
ReleaseやメモリについてはMemoryManagementを読みます。
https://docs.unity3d.com/Packages/com.unity.addressables@1.2/manual/MemoryManagement.html

#実際に使用する
AASをゲーム開発で使用するケースを考えます。
マニュアルやスライドを見ているとAASの各種設定は
Window->AssetManagement->Addressablesのメニューから行っています。
GUI上でグループを作成しアセットをグループへドラッグアンドドロップすることはとてもわかりやすいです。サンプルで数十ファイルの間は。
##膨大なアセット数
ゲームに使用するアセットは軽く数百を超えていき、数万アセットにもなることがあります。
**数万を超えるアセット数に対してこの手順は人間がするものではありません**

多くの場合アセットを保存しているルートディレクトリからのパスでグループ化しフォルダ内のアセットはそのグループに属するといった設定になるでしょう。
アセットインポート時にグループの作成とグループへの追加を実装する方法がないか、APIドキュメントを読んで探します
https://docs.unity3d.com/Packages/com.unity.addressables@1.2/api/index.html

###AddressableAssetSettings
最初にAddressablesメニューを開くときに作成されたAddressableAssetSettings.assetを選択したときのインスペクタを見ます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/157391/7c840607-f34a-1274-5d5a-ae8ca2eebe2a.png)

名前からしてAASに関する設定を持っていそうです。
Groupsを開くと自分で作成したグループが追加されていることがわかります。
他にもいろいろな設定があります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/157391/6d48d9db-9c69-d96a-0050-82788451fb8e.png)

インスペクタ上で歯車をクリックするとEditScriptが表示されるのでスクリプトを確認するとScriptableObjectであることがわかります。
APIドキュメントを見るとグループの追加やアセットの登録に必要な機能があることがわかります。（CreateGroupやCreateOrMoveEntry）
[AddressableAssetSettings](https://docs.unity3d.com/Packages/com.unity.addressables@1.2/api/UnityEditor.AddressableAssets.Settings.AddressableAssetSettings.html)
このScriptableObjectを取得してきます。

```cs
        //アドレサブルアセットセッティング取得
        var s = UnityEditor.AssetDatabase.LoadAssetAtPath<UnityEditor.AddressableAssets.Settings.AddressableAssetSettings>(
            "Assets/AddressableAssetsData/AddressableAssetSettings.asset"
            );
```
###グループ作成の自動化
取得してきたAddressableAssetSettingsスクリプトを使用してグループ作成をします。
APIは **CreateGroup** を使用します。
APIドキュメントを見て引数を調べます。
[CreateGroup](https://docs.unity3d.com/Packages/com.unity.addressables@1.2/api/UnityEditor.AddressableAssets.Settings.AddressableAssetSettings.html#UnityEditor_AddressableAssets_Settings_AddressableAssetSettings_CreateGroup_System_String_System_Boolean_System_Boolean_System_Boolean_System_Collections_Generic_List_UnityEditor_AddressableAssets_Settings_AddressableAssetGroupSchema__System_Type___)
変わった引数でschemasToCopyがあります。
何を設定するのか迷いますがAddressablesメニューのDefaultLocalGroupと同じ設定にするには

```cs
        //スキーマ生成
        List<UnityEditor.AddressableAssets.Settings.AddressableAssetGroupSchema> schema = new List<UnityEditor.AddressableAssets.Settings.AddressableAssetGroupSchema>() 
{
            new UnityEditor.AddressableAssets.Settings.GroupSchemas.BundledAssetGroupSchema(),
            new UnityEditor.AddressableAssets.Settings.GroupSchemas.ContentUpdateGroupSchema()
        };
```
というリストを指定します。
初期状態のグループが
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/157391/80ec56f5-1db5-966c-26d6-043a35c03d3b.png)
AASUtility.csのCreateGroupメソッドを呼べば
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/157391/454a5f92-af9a-c3c7-0cf3-70c9b7096d52.png)
CreatedGroupというグループが作成されました。

###アセットをグループへ追加・削除
各アセットを作成したグループへ追加しなければアセットバンドルに含まれません。
APIはCreateOrMoveEntryを使用します。
APIドキュメントを見て引数を調べます。
[CreateOrMoveEntry](https://docs.unity3d.com/Packages/com.unity.addressables@1.2/api/UnityEditor.AddressableAssets.Settings.AddressableAssetSettings.html#UnityEditor_AddressableAssets_Settings_AddressableAssetSettings_CreateOrMoveEntry_System_String_UnityEditor_AddressableAssets_Settings_AddressableAssetGroup_System_Boolean_System_Boolean_)
guidとtargetParentでアセットのGUIDとグループを指定するだけで良さそうです。
guidはAssetPathToGUIDで取得できます

```cs
//アセットGUIDを取得
var assetGuid = UnityEditor.AssetDatabase.AssetPathToGUID("Assets/Data/hogeMat.mat");
```
AASUtility.csのAddAssetToGroupメソッドを呼べば
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/157391/a7fde57b-36ed-a8f3-9040-b8d5e2ab06c2.png)
グループにアセットが追加されました。

##アセットにラベルを指定
複数のアセットを一括でロードする機能として**ラベル**があります。
ゲームの一ステージを構成するアセットをステージ遷移が発生する前に先読みしておきシームレスに見せるといった時に使えそうな機能です。
このラベルを設定するAPIを調べます。
AddressableAssetEntry.labelsが該当します。
APIドキュメントを見てセット関数を調べます。
[SetLabel](https://docs.unity3d.com/Packages/com.unity.addressables@1.2/api/UnityEditor.AddressableAssets.Settings.AddressableAssetEntry.html#UnityEditor_AddressableAssets_Settings_AddressableAssetEntry_SetLabel_System_String_System_Boolean_System_Boolean_System_Boolean_)
AddressableAssetEntryを取得してSetLabels関数を使用することで設定できそうです。

AddressableAssetEntryを取得する方法を調べます。
AddressableAssetSettingsに**GetAllAssets**という関数があります。
APIドキュメントを調べます。
[GetAllAssets](https://docs.unity3d.com/Packages/com.unity.addressables@1.2/api/UnityEditor.AddressableAssets.Settings.AddressableAssetSettings.html#UnityEditor_AddressableAssets_Settings_AddressableAssetSettings_GetAllAssets_System_Collections_Generic_List_UnityEditor_AddressableAssets_Settings_AddressableAssetEntry__System_Boolean_System_Func_UnityEditor_AddressableAssets_Settings_AddressableAssetGroup_System_Boolean__System_Func_UnityEditor_AddressableAssets_Settings_AddressableAssetEntry_System_Boolean__)
第一引数のリストに設定されてる全アセットを返してくれることがわかります。
アセット数が数万になりリストが膨大になる場合Filterの引数で絞れそうです。

これで取得したリストの中からラベルをセットしたいアセットのassetGUIDを検索し取得したAddressableAssetEntryのSetLabelを呼べば設定完了です。

```cs
    static void SetLabelToAsset(List<string> assetGuidList, string label, bool flag)
    {
        var s = GetSettings();
        //ラベルを追加するように呼んでおく。追加されていないと設定されない。
        s.AddLabel(label);
        List<UnityEditor.AddressableAssets.Settings.AddressableAssetEntry> assetList = new List<UnityEditor.AddressableAssets.Settings.AddressableAssetEntry>();
        s.GetAllAssets(assetList, true);
        foreach (var assetGuid in assetGuidList)
        {
            var asset = assetList.Find((a) => { return a.guid == assetGuid; });
            if(asset != null)
            {
                asset.SetLabel(label, flag);
            }
        }
    }
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/157391/02d3db11-2284-880d-fa1a-bc65a2b07c90.png)
コードから設定されました。

##自動ビルド
`UnityEditor.AddressableAssets.Settings.AddressableAssetSettings.BuildPlayerContent();`
を呼ぶだけでビルドしてくれます。
アセットバンドルビルドスクリプトを自作していた時のことを思い出すとこれだけで感動します。

#おわりに
AddressableAssetSystemはゲーム開発に使用する機能も揃っているようです。
AssetBundleManagerを使用してビルドスクリプトを自作してといった旧時代の遺物は捨て去り、Unity2018.3以降はAddressableAssetSystemを使用していきましょう。
どんどん使用されていき知見が溜まっていくことを期待してます。