【Unity】Addressablesの基本的な使い方をざっくりまとめる

# はじめに
Unityの動的アセット管理の一つであるAddressablesの基本的な使い方を紹介します。

(動的アセット管理について、こちらにまとめてあるのでぜひご覧ください)

https://qiita.com/kiku09020/items/b6e6febaf9a73449f3b9

# セットアップ
### インストール
1. Package Managerウィンドウを開く
2. Addressablesを検索して Install
3. ツールバーの`Window / Asset Management / Addressables / Groups`を選択

![](https://storage.googleapis.com/zenn-user-upload/3bc2e957b50b-20240126.png)


### 設定
### Addressables Settingsの作成
- インストールが完了すると、Addressables Groupウィンドウにこのような表示がされるので、ボタンクリックでAddressables Settingsを作成します。
	![](https://storage.googleapis.com/zenn-user-upload/0edd3d4ce5ab-20240126.png)

- このような表示になればセットアップ完了です
	![](https://storage.googleapis.com/zenn-user-upload/a84a121a6bd1-20240126.png)


# 流れ
1. アセットをAddressables Groupに登録
2. (アセットバンドルのビルド)
3. アセットを読み込み(ロード)
4. アセットを割り当て、生成など
5. (アセットを使い終わったら、解放(リリース))



# アセットをAddressables Groupに登録
アセットを読み込むには、Addressables Groupに登録する必要があります

1. 対象のアセットを選択
2. アセットのインスペクターの上部のAddressablesという項目にチェック
![](https://storage.googleapis.com/zenn-user-upload/336c7169d344-20240127.png)
3. Selectボタンをクリックして、Addressables Groupsウィンドウを開く
4. 任意でAddressables NameやLabelなどを設定する
![](https://storage.googleapis.com/zenn-user-upload/775f4ed2c4d4-20240127.png)



# アセットを読み込み、割り当てる
スクリプトで読み込みの処理を記述します

1. `Addressables.LoadAssetAsync`で、アセットの型とキー(Addressables Name, Path, Labelなど)を指定して読み込みます
	- AssetReferenceという型を[SerializeField]で指定すると、直観的に参照できます
		![](https://storage.googleapis.com/zenn-user-upload/d16c08e23255-20240127.png)
2. 読み込むと、`AsyncOperationHandle`という戻り値を返します。
これは、読み込んだアセット乗法や読み込みの進捗状況や読み込みが完了したかどうかの情報がまとまったものです。
3. 取得した操作ハンドルのメンバの一つである`Task`を利用して、ロードが完了するまで非同期で待機します。(await/async)
4. 読み込みが完了したら、アセットを割り当てたり、インスタンス化したりします。

```cs
    // AssetReferenceという型で直観的に参照できる
    [SerializeField] AssetReference assetReference;

    private async void Start()
    {
        // アセット読み込み
        var spriteRenderer = GetComponent<SpriteRenderer>();
        var spriteHandle = Addressables.LoadAssetAsync<Sprite>(assetReference);
        var sprite = await spriteHandle.Task;

        // アセットを割り当て
        spriteRenderer.sprite = sprite;
    }
```


# バンドルのビルドについて
上記までの処理でエディタ上で一度実行すると、こんな感じになります
![](https://storage.googleapis.com/zenn-user-upload/1562a4f10521-20240127.gif)
*`Use AssetDatabaseで実行した結果`*

ゲームを開始した直後に、スプライトを指定したアセットのスプライトに動的に変更しています。

### 実機環境で試すには、ビルドをする必要がある
エディタ上ではこのように成功していますが、**エディタ上で実機環境での動作を確認するにはアセットバンドルをビルドする必要があります**
(**ゲーム自体をビルドすると、自動でバンドルもビルドされます。**)

Addressables GroupsウィンドウのBuildボタンを押して、`new Build /
 Default Script`を選択します

また、Addressables Groupsウィンドウ内のPlay Mode Scriptを`Use Existing Build`に変更します。
![](https://storage.googleapis.com/zenn-user-upload/ca98e3242482-20240128.png)

::: note
アセット構成を変更する度にビルドをするのは面倒なので、
エディタ上では基本的にPlay Mode Scriptは`Use AssetDatabase` を利用して、
たまに`Use Exisiting Build`にして実機確認、
みたいな感じで切り替えるのがいいと思います
:::

:::note 
**ビルド時に生成されるbundleファイルについて**

ビルドが完了すると、Addressables Reportウィンドウが表示されます。
ビルドにかかった時間や、ビルドされたバンドル情報などの詳細が記載されています。
![](https://storage.googleapis.com/zenn-user-upload/b4ee44188108-20240128.png)


Reportに表示された"Local catalog build location"という項目に示されたパスのフォルダ内に.bundleという拡張子のファイルが作成されます
![](https://storage.googleapis.com/zenn-user-upload/aca44b6deda9-20240128.png)

また、プロジェクトのビルド先のフォルダ内の`XX(プロジェクト名)_Data / StreamingAssets / aa(bundleVersion) / StandAloneWindows(プラットフォーム)`にも同じくバンドルファイルが配置されています。

このバンドルの中身の情報を参照して、アセットを動的に割り当ててくれてます。
:::


# 使い終わったらリリース

ロードしたアセットは、使い終わったらリリース(解放)する必要があります

例えば、先ほどのコードのようにスプライトアセットを読み込んで、あるゲームオブジェクトに割り当て、`Destroy()`などでゲームオブジェクトを削除するとします。
このとき、**ゲームオブジェクトだけが削除されるので、読み込まれたアセットはまだ残ったままです。**

なので削除時に明示的にリリースの命令を加える必要があります。

:::message
ちなみに、アセットを参照したシーンを破棄した場合は自動でアセットも自動でリリースされます。
シーン遷移する場合はわざわざ明示的にリリース命令を加えなくてもOKです
:::

### EventViewerを使って、リリースできているか確認しよう
削除後にリリースできているかを確認するのは難しいです。(ブレークポイント置いて確認できますが、面倒です)

そこで便利なツールである**EventViewerを使って、視覚的にリリースできているかを確認できます。**

Addressables Groupsウィンドウ内の`Tools / Window / Event Viewer`を選択すると、こんなのが表示されます
![](https://storage.googleapis.com/zenn-user-upload/5b79d24989f1-20240128.png)

実行すると、項目が増えます。右側の部分をクリックすると再生が一時停止し、詳細が表示されます。
一番下の項目が、先ほどのコードで割り当てたアセットです。
この項目に表示されている値(図の1という値)は、そのアセットが参照されている数です。
現在は、先ほどコードで割り当てたSpriteRendererに参照されているので、1となっています。
![](https://storage.googleapis.com/zenn-user-upload/b383306a7382-20240128.png)


<br>

次に、先ほどのコードをこんな感じに変更します
削除したらアセットをリリースするように変更します
```diff_c_sharp
    [SerializeField] AssetReference assetReference;
+   SpriteRenderer spriteRenderer;

    private async void Start()
    {
        // アセット読み込み
        spriteRenderer = GetComponent<SpriteRenderer>();
        var spriteHandle = Addressables.LoadAssetAsync<Sprite>(assetReference);
        var sprite = await spriteHandle.Task;

        // アセットを割り当て
        spriteRenderer.sprite = sprite;
    }

+   private void Update()
+   {
+       // 自身を削除
+       if(Input.GetKeyDown(KeyCode.Space)) {
+           Destroy(gameObject);
+       }
+   }

+   private void OnDestroy()
+   {
+       //アセットの解放
+       Addressables.Release(spriteRenderer.sprite);
+   }
```

実行すると、こんな感じになります
![](https://storage.googleapis.com/zenn-user-upload/ccd2d4c6e445-20240128.gif)
**Release()でリリースしているため、参照カウントが0になり、非表示になります**


**リリースしない場合、下図のようにオブジェクトを削除しても参照カウントが残ったままで、無駄にメモリが占領されてしまいます。**
![](https://storage.googleapis.com/zenn-user-upload/0842f71b13db-20240128.png)

# さいごに
GroupsやReport、EventViewerなどのツールが便利で最高！
便利なツールも駆使して、効率的にアセット管理しちゃいましょう！

### 参考
https://docs.unity3d.com/ja/Packages/com.unity.addressables@1.20/manual/index.html