【Unity】Addressableアセットシステム入門 - 3つのメリットと基本的な使い方を紹介

UnityのAddressableアセットシステムの概要とメリット、基本的な使い方をまとめました。

Unity2019.2.13
Addressable1.3.8

今まではAssetBundleの管理がとても面倒だった

まずAddressableアセットシステムが生まれた経緯を知るために、これが登場する以前の状況について振り返ります。

UnityではダウンロードコンテンツをAssetBundleという圧縮形式にして配信できます。
AssetBundleで用意されているのは圧縮(ビルド)する仕組みとそれを解凍してロードをする仕組み、
あとはせいぜい次の記事で説明しているようなUnityWebRequestAssetBundleによる簡単なキャッシュの仕組みくらいです。

light11.hatenadiary.com

そのため、実際にダウンロードコンテンツをゲーム内で使うためには様々な実装を行う必要がありました。
メモリ管理や依存関係の管理など、ややこしい部分を各開発者が実装していました。

しかし実は結果的に、このうちの大部分はどの開発者も同じような実装をしていました。
それならゲームエンジンに組み込むべきだよね、ということで生まれたのがAddressableアセットシステムです。
(内部的な経緯は知りませんが、公式ブログを読む限りこんな雰囲気かと思います)

次節からは、Addressableアセットシステムで出来ることを具体的に理解するために、
今までは実装が必要だったけどAddressableによりサポートされた機能について見ていきます。

ちなみにAddressableは内部的にはAssetBundleを使用しているので、AssetBundleの基礎知識はあったほうが理解しやすいです。
AssetBundleの基本的な知識については以下の記事にまとめていますので、必要に応じて参照してください。

light11.hatenadiary.com

Addressableのメリット① - ロード元の選択

さて従来は独自実装が必要だった機能として、リソースのロード元を切り替える機能があります。

最終的にAssetBundle化してリソースを読み込むようなプロジェクトであっても、開発中はAssetBundleを使わないことがあります。
これは、AssetBundleはビルドしないと確認できず、高速なイテレーションを阻害してしまうためです。
そこで「ロード元をAssetBundleかAssetDatabaseか選べるようにする」 機能を作ることになります。

f:id:halya_11:20191124183251p:plain

Addressableでは、設定値を一つ切り替えるだけでこれを実現できるようになっています。

f:id:halya_11:20191124183050p:plain

Addressableのメリット② - 依存関係の管理

AssetBundle同士が依存している場合、ロード対象が依存しているすべてのAssetBundleを先にロードしておかないと、
ロード対象のAssetBundleが正常にロードできないという仕様があります。

light11.hatenadiary.com

このため、あるAssetBundleのロード時には依存関係を全て取得して先にロードしておく、という機能を実装する必要がありました。

しかし実はAddressableはここもサポートしてくれているので、
あるリソースをロードしようとすると依存関係にあるAssetBundleをあらかじめすべてロードしてくれます。

f:id:halya_11:20191124184508p:plain

この機能によりAddressableでは、依存関係を気にせずにリソースのロード処理を記述できます。

Addressableのメリット③ - メモリ管理

AssetBundleを扱う上でもう一つ大変だった点として、メモリ管理があります。

たとえば、いま下図のようにAssetBundle Aをロードするとします。
AssetBundle AにはAsetBundle Bが紐づいているのでこれもロードする必要があります。

f:id:halya_11:20191124185436p:plain

次に下図のようにAssetBundle Cをロードするとします。
AssetBundle CにもAsetBundle Bが紐づいており、これはすでにロードされているのでそれを使います。

f:id:halya_11:20191124185639p:plain

ここでAssetBundle Aをアンロードすることを考えます。
このとき、他に依存関係がなければ紐づいているAssetBundle Bもアンロードしなければいけませんが、
この時点ではAssetBundle BがAssetBundle Cに紐づいているためまだアンロードしてはいけません。

f:id:halya_11:20191124190027p:plain

このあとAssetBundle Cをアンロードする際に、AssetBundle Bをアンロードする必要があります。

f:id:halya_11:20191124190059p:plain

このように、AssetBundleのメモリ管理をちゃんとやるのは面倒です。
内部的に参照カウンタを持っておくなどの実装が必要になってきます。

Addresssablesはこのような依存関係を踏まえた参照カウンタの仕組みを内包しています。
すなわち、あるAssetBundleに対して一回ロードをしたら必ず一回アンロードする、
というルールだけを守っていれば、依存関係を含めてメモリがきれいな状態が保証されます。

インストール

さて概念がわかったところで、ここからはAddressableを実際に触って理解していきます。
まずはインストールを行います。

AddressableアセットシステムのインストールはPackage Managerから行います。

f:id:halya_11:20191124190511p:plain

Package Managerについては以下の記事で説明していますので、必要に応じて参照してください。

light11.hatenadiary.com

アドレスを付ける

それでは実際にAddressableの機能を使ってみます。

Addressableの基本として、まずはアセットに「アドレス」を付けます。
AddressableではアセットのパスやAssetBundle名ではなくこのアドレスを指定してアセットをロードします。
直接パスやAssetBundle名を指定しないことで、メリット①で説明したロード元の切り替えが容易になります。

さてAddressableをインストールすると、アセットのインスペクタにAddressableというトグルが表示されます。

f:id:halya_11:20191112162621p:plain

このチェックボックスを有効にすると、アドレスが割り振られます。

f:id:halya_11:20191112162833p:plain

デフォルトはそのアセットのパスとなっていますが、自由に変えることができます。

f:id:halya_11:20191112162918p:plain

また、アドレスが付いたアセット一覧はWindow > Asset Management > Addressables > Groupsから見ることができます。
このウィンドウを開くとDefault Local Groupに先ほどのアセットがアドレスとともに表示されていることが確認できます。

f:id:halya_11:20191112163058p:plain

このウィンドウからは既存のアドレスを編集したり、アセットをドラッグ&ドロップすればアドレスを追加したりもできます。

アドレス名を指定してロードする

次にこのアセットをアドレス経由でロードしてみます。
ロードにはAddressables.LoadAssetAsync()を使います。

using UnityEngine;
using UnityEngine.AddressableAssets;

public class Example : MonoBehaviour
{
    private void Start()
    {
        Addressables
            .LoadAssetAsync<GameObject>("Example") // アドレスを文字列で指定
            .Completed += op => {
                // 結果を取得してインスタンス化
                // 本来はエラーハンドリングなど必要
                Instantiate(op.Result);
            };
    }
}

使い終わったらAddressables.Release()でアンロードします。

シリアライズしてロードする

もう一つのロード方法として、AssetReferenceXxxクラスをシリアライズする方法があります。

[SerializeField]
private AssetReferenceGameObject _reference;

これをシリアライズするとInspectorからアドレスを選択できます。

f:id:halya_11:20191112170849p:plain

そしてここにアサインした状態でLoadAssetAsync()を使うと対象のアセットをロードできます。

using UnityEngine;
using UnityEngine.AddressableAssets;

public class Example : MonoBehaviour
{
[SerializeField]
private GameObject _gameObject;

    [SerializeField]
    private AssetReferenceGameObject _reference;

    private void Start()
    {
        _reference
            .LoadAssetAsync()
            .Completed += op => {
                Instantiate(op.Result);
            };
    }
}

こちらも使い終わったら_reference.ReleaseAsset()でアンロードします。
またこの方法の場合はアドレス名をリネームしてもソースコードの変更は不要です。

Play Mode

さてAddressableには、リソースをAssetDatabaseから読み込んだりAssetBundleから読み込んだりと、読み込みモードを簡単に変更できる機能があります。
これを使うにはAddressables GroupsウィンドウのPlay Mode Scriptドロップダウンを開きます。

f:id:halya_11:20191112171957p:plain

それぞれのモードの説明は下記のとおりです。

項目名 説明
Use Asset Database(faseter) AssetDatabaseからアセットを読み込む
Simulate Groups(advanced) AssetDatabaseからアセットを読み込むが、
AssetBundleの依存関係を分析して
あたかもAssetBundleから読み込んでいるように振る舞う
Use Existing Build(requires built groups) AssetBundleから読み込む(必要に応じてビルドする)

Use Existing Buildにする場合は、再生する前にビルドをする必要があります。
ビルドはAddressables GroupsウィンドウからBuild > New Build > Default Build Scriptを選択します。

f:id:halya_11:20191112173020p:plain

また本記事では説明しませんが、AssetBundleの読み込み元やビルド先のパスは自由に設定できます。

まとめ

以上、Addressableアセットシステムの概念とメリット、簡単な使い方をまとめました。
この記事で紹介していない機能については他の記事にまとめていく予定です。

参考

docs.unity3d.com