Unity : Async なメソッドをコルーチンなメソッドでラップする - ジャコ Lab

ジャコ Lab

プログラミング関連のメモ帳的ブログです

Unity : Async なメソッドをコルーチンなメソッドでラップする

少し前にどころか、数か月前に Unity で画像を取得する方法を調べた続きです。

前々回に Web Request を コルーチン で待機し、前回に Async/Await で待機しました。

今回は、前回作った Async/Await なメソッドコルーチン で待機しようと思います

コルーチン対応のメソッドも追加

    using System;
    using System.Collections;
    using System.Threading.Tasks;
    using UnityEngine;
    using UnityEngine.Networking;
    using UnityEngine.UI;

    public class WebRequester : MonoBehaviour
    {
        [SerializeField]
        private RawImage _rawImage;

        public async void OnClickGetTexture()
        {
            try
            {
                var texture = await GetTexture("https://picsum.photos/200");
                _rawImage.texture = texture;
            }
            catch (Exception ex)
            {
                Debug.LogError($"[WebRequester:ERROR] {ex.Message}");
            }
        }

+       public void OnClickGetTextureCoroutine()
+       {
+           StartCoroutine(GetTextureCoroutine("https://picsum.photos/200", (texture) =>
+           {
+               _rawImage.texture = texture;
+           }, (error) =>
+           {
+               Debug.LogError($"[WebRequester:ERROR] {error}");
+           }));
+       }

        private async Task<Texture2D> GetTexture(string url)
        {
            using var req = UnityWebRequestTexture.GetTexture(url);

            Debug.Log("[WebRequester] Send");
            req.SendWebRequest();
            Debug.Log("[WebRequester] Recv");

            while (!req.isDone)
            {
                // 50ms 待機
                await Task.Delay(50);
            }

            if (req.result == UnityWebRequest.Result.Success)
            {
                var texture = DownloadHandlerTexture.GetContent(req);
                return texture;
            }
            else
            {
                throw new Exception(string.IsNullOrEmpty(req.downloadHandler.error) ? req.error : req.downloadHandler.error);
            }
        }

+       private IEnumerator GetTextureCoroutine(string url, Action<Texture2D> successCallback = null, Action<string> errorCallback = null)
+       {
+           var task = GetTexture(url);
+
+           while (!task.IsCompleted)
+           {
+               // 50ms 待機
+               yield return new WaitForSeconds(0.05f);
+           }
+
+           try
+           {
+               successCallback?.Invoke(task.Result);
+           }
+           catch (Exception ex)
+           {
+               errorCallback?.Invoke(ex.Message);
+           }
+       }
+   }

await はできないので、Task が IsCompleted になるまで待ち続けます。ちなみにエラーになっても IsCompletedtrue になるようです。

エラーが発生している場合は、task.Resultにアクセスするときに Exception が送出されるっぽい

スクリプト全体

(折りたたみ)

using System;
using System.Collections;
using System.Threading.Tasks;
using UnityEngine;
using UnityEngine.Networking;
using UnityEngine.UI;

public class WebRequester : MonoBehaviour
{
    [SerializeField]
    private RawImage _rawImage;

    public async void OnClickGetTexture()
    {
        try
        {
            var texture = await GetTexture("https://picsum.photos/200");
            _rawImage.texture = texture;
        }
        catch (Exception ex)
        {
            Debug.LogError($"[WebRequester:ERROR] {ex.Message}");
        }
    }

    public void OnClickGetTextureCoroutine()
    {
        StartCoroutine(GetTextureCoroutine("https://picsum.photos/200", (texture) =>
        {
            _rawImage.texture = texture;
        }, (error) =>
        {
            Debug.LogError($"[WebRequester:ERROR] {error}");
        }));
    }

    private async Task<Texture2D> GetTexture(string url)
    {
        using var req = UnityWebRequestTexture.GetTexture(url);

        Debug.Log("[WebRequester] Send");
        req.SendWebRequest();
        Debug.Log("[WebRequester] Recv");

        while (!req.isDone)
        {
            // 50ms 待機
            await Task.Delay(50);
        }

        if (req.result == UnityWebRequest.Result.Success)
        {
            var texture = DownloadHandlerTexture.GetContent(req);
            return texture;
        }
        else
        {
            throw new Exception(string.IsNullOrEmpty(req.downloadHandler.error) ? req.error : req.downloadHandler.error);
        }
    }

    private IEnumerator GetTextureCoroutine(string url, Action<Texture2D> successCallback = null, Action<string> errorCallback = null)
    {
        var task = GetTexture(url);

        while (!task.IsCompleted)
        {
            // 50ms 待機
            yield return new WaitForSeconds(0.05f);
        }

        try
        {
            successCallback?.Invoke(task.Result);
        }
        catch (Exception ex)
        {
            errorCallback?.Invoke(ex.Message);
        }
    }
}

シーンにボタンを追加

ボタンを追加したシーン
ボタンを追加したシーン

右のボタンを押したら StartCoroutine を実行するメソッドを動かします!

右のボタンのクリックリスナーの設定

右のボタンのインスペクタ
右のボタンのインスペクタ

動作確認

実行結果
実行結果

両方動いている!

まとめ

これでどっちでもいけるようになった