Unity

【Unity】Addressable Asset Systemのリソース読み込みをasync/await対応する

はじめに

Unite 2018のそろそろ楽がしたい!新アセットバンドルワークフロー&リソースマネージャー詳細解説にて、Addressable Asset Systemという新アセット管理システムの概要が発表されました。

今回はこの新アセットシステム中で用いられるリソース読み込み「Addressables.Instantiate」をawaitできるようにしてみます。

Addressables.Instantiate の戻り値

Addressables.Instantiate<T>の戻り値をみたところ、UnityEngine.ResourceManagement.IAsyncOperation<T>型が使われていました。

public static IAsyncOperation<TObject> Instantiate<TObject>(IResourceLocation location, Transform parent = null, bool instantiateInWorldSpace = false) where TObject : Object

そのためこのIAsyncOperation<T>awaitableにしてあげればよさそうです。

実装

using System.Runtime.CompilerServices;
using System.Threading.Tasks;
using UnityEngine.ResourceManagement;


public static class IAsyncOperationExtensions
{
    public static TaskAwaiter<T> GetAwaiter<T>(this IAsyncOperation<T> ap)
    {
        var tcs = new TaskCompletionSource<T>();
        ap.Completed += operation => tcs.TrySetResult(operation.Result);
        return tcs.Task.GetAwaiter();
    }
}

対象をawaitする場合、そのオブジェクトにGetAwaiterメソッドが生えていればよいためこれを拡張メソッドで追加します。
メソッド内部でTaskCompletionSourceを使って非同期処理をTaskに変換し、そのTaskAwaiterを返すことでawaitできるようにしています。

使用例

using UnityEngine;
using UnityEngine.AddressableAssets;

public class AddressableTest : MonoBehaviour
{
    [SerializeField] private AssetReference _assetReference;
    private GameObject _target;

    async void Start()
    {
        _target = await Addressables.Instantiate<GameObject>(null);
    }

    void Update()
    {
        if (_target != null)
        {
            _target.transform.Rotate(90f * Time.deltaTime, 90f * Time.deltaTime, 90f * Time.deltaTime);
        }
    }
}

まとめ

  • 対象のオブジェクトにGetAwaiterメソッドさえあればawait対象になる
  • そのときにawait管理用のオブジェクトを返す必要があるが、TaskCompletionSourceを使えば簡単に生成できる。

参考