【Unity】【Addressable】複数のAsyncOperationHandleを一つに結合して取り扱う方法

Unity の Addressable アセットシステムで複数の AsyncOperationHandle を一つに結合して取り扱う方法についてまとめました。

Unity2020.3.40
Addressables 1.18.19

はじめに

Addressable の AsyncOperationHandlePercentComplete プロパティを使うと、以下のようにロード処理などの進捗率を取得することができます。

AsyncOperationHandle<GameObject> handle = Addressables.LoadAssetAsync<GameObject>("someAddress");
handle.PercentComplete; // 進捗率を取得できる

ここで、以下のように二つの AsyncOperationHandle を一つにまとめて進捗率を取得することを考えます。

// これら二つをまとめた進捗(PercentComplete)を取得したい
AsyncOperationHandle<GameObject> handle1;
AsyncOperationHandle<GameObject> handle2;

このようなケースのために、Addressable アセットシステムには複数の AsyncOperationHandle を結合して取り扱うための仕組みが用意されています。
この記事ではこの仕組みについてまとめます。

(なお上記の例では Addressables.LoadAssetsAsync の引数に複数のアドレスを渡せば済む話なので、あくまで例として捉えてください。

複数のAsyncOperationHandleを並列に結合する

複数の AsyncOperationHandle を並列に結合するには、Addressables.ResourceManager.CreateGenericGroupOperation を使います。
これにより得られた AsyncOperationHandlePercentComplete プロパティからは結合した全てのハンドルを踏まえた進捗率が得られます。

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.AddressableAssets;
using UnityEngine.ResourceManagement.AsyncOperations;

public sealed class Example : MonoBehaviour
{
    private IEnumerator Test()
    {
        AsyncOperationHandle<GameObject> loadHandle1;
        AsyncOperationHandle<GameObject> loadHandle2;
        var handles = new List<AsyncOperationHandle>
        {
            loadHandle1,
            loadHandle2
        };

        // 二つのAsyncOperationHandleを結合
        var groupedHandle = Addressables
            .ResourceManager
            .CreateGenericGroupOperation(handles);

        while (!groupedHandle.IsDone)
        {
            // 進捗率をログ出力
            Debug.Log(groupedHandle.PercentComplete);
            yield return null;
        }
    }
}

複数のAsyncOperationHandleを直列に結合する

次に、ある AsyncOperationHandle の結果を他の AsyncOperationHandle が使用するケースを考えます。
たとえば、「あるアセットのアドレスを取得するハンドル」と、「そのアドレスを使ってアセットをロードするハンドル」を考えます。

このようなケースでは、Addressables.ResourceManager.CreateChainOperationを使います。

using System.Collections;
using UnityEngine;
using UnityEngine.AddressableAssets;
using UnityEngine.ResourceManagement.AsyncOperations;

public sealed class Example : MonoBehaviour
{
    private IEnumerator Test()
    {
        // アドレスを取得するハンドル
        AsyncOperationHandle<string> addressHandle;

        var chainedHandle = Addressables
            .ResourceManager
            .CreateChainOperation(addressHandle, h =>
            {
                // 取得されたアドレスを使って、アセットをロードするハンドルを作成する
                return Addressables.LoadAssetAsync<GameObject>(h.Result);
            });

        while (!chainedHandle.IsDone)
        {
            // 進捗率をログ出力
            Debug.Log(chainedHandle.PercentComplete);
            yield return null;
        }
    }
}