Unityのメソッドをawaitできるようにする方法と、UniRxを導入すればすべて解決するよという話です。
Unity2018.2
UniRx6.2.2
C#6
Unityのメソッドをawaitしたい
async/awaitは便利なのでUnityのResource.LoadAsync()
とかUnityで定義されているメソッドもこんな感じでawaitしたいものです。
var request = Resources.LoadAsync<Texture>("texture_example"); await request; Debug.Log(request.asset as Texture2D);
ただ何も対応しないままこう書くと、次のようなコンパイルエラーが出ます。
GetAwaiter()
という拡張メソッドが必要らしいです。
GetAwaiter()を定義する
じゃあGetAwaiter()を定義しましょう。
GetAwaiterはこんな感じで拡張メソッドとして定義します。
using UnityEngine; using System.Threading.Tasks; using System.Runtime.CompilerServices; public static class ResourceRequestExtenion { // Resources.LoadAsyncの戻り値であるResourceRequestにGetAwaiter()を追加する public static TaskAwaiter<Object> GetAwaiter(this ResourceRequest resourceRequest) { var tcs = new TaskCompletionSource<Object>(); resourceRequest.completed += operation => { // ロードが終わった時点でTaskCompletionSource.TrySetResult tcs.TrySetResult(resourceRequest.asset); }; // TaskCompletionSource.Task.GetAwaiter()を返す return tcs.Task.GetAwaiter(); } }
コメントで大体はわかるかと思いますが、本記事の主旨とずれるので詳しい説明はしません。
このようにしてGetAwaiter()
を定義するとResources.LoadAsync()
がawaitできるようになります。
UniRxを使えばすべて解決
このようにUnityのメソッドにGetAwaiter()
を定義していけばawaitはできるわけですが、
実はこれらの拡張メソッドはすでにUniRxに定義されています。
そのためUniRxをusingするだけで普通にawaitできちゃいます。
using UnityEngine; using System.Threading.Tasks; using UniRx; public class Example : MonoBehaviour { private async Task Start() { var request = Resources.LoadAsync<Texture>("texture_example"); await request; Debug.Log(request.asset as Texture2D); } }
UniRxはAssetStoreからダウンロードできます。
GetAwaiter()の詳細
本記事の結論としてはUniRxを入れちゃいましょうって話なのですが、
GetAwaiter()
を自分で書かなければいけないこともあるかと思います。
その場合は下記の記事でわかりやすく説明されているので参照することをお勧めします(丸投げ)。