はじめに
UniTask にはあらゆるものを UniTask に変換する .ToUniTask
というメソッドがあります
同じものを変換するときでもオーバーロードがあったりでややこしいので備忘録としてまとめてみました
GetAwaiter
や WithCancellation
なども書いておきます(実装を書くのは省きます)
環境
以下の環境で動作しています
- Unity 2019.4.15f1
- UniTask 2.0.37
目次
準備
var token = this.GetCancellationTokenOnDestroy();
var uniTask = UniTask.Run(() => "hoge");
こんな感じで変数token
は何かしらのCancellationToken
が、
変数uniTask
には何かしらのUniTaskが入っていると考えてください
CancellationToken
CancellationToken
を UniTask に変換できます
ToUniTask
// 実装
public static (UniTask, CancellationTokenRegistration) ToUniTask(this CancellationToken cancellationToken)
// 使用例
var (task,registration) = token.ToUniTask();
registration.Dispose(); // CancellationTokenRegistrationから破棄もできる
await task;
CancellationToken がキャンセル状態になるまで await される UniTask が返ってきます
返り値は (UniTask, CancellationTokenRegistration)
というタプルです
WaitUntilCanceled
UniTask型ではありませんが、awaitableなCancellationTokenの型が返ってきます
// 実装
public static CancellationTokenAwaitable WaitUntilCanceled(this CancellationToken cancellationToken)
// 使用例
await token.WaitUntilCanceled(); // 直接awaitできる。ToUniTaskはできない
逆変換
UniTask を CancellationToken に変換できます
// 実装
public static CancellationToken ToCancellationToken<T>(this UniTask<T> task)
public static CancellationToken ToCancellationToken<T>(this UniTask<T> task, CancellationToken linkToken)
// 使用例
var ct = uniTask.ToCancellationToken();
var ct2 = uniTask.ToCancellationToken(linkToken);
コルーチン
private IEnumerator HogeCoroutine(){
yield break;
}
ToUniTask , GetAwaiter , WithCancellation
UniTask では StartCoroutine
を使わずにコルーチンを起動することができます
ただし、UniTaskはコルーチンの yield return new WaitForEndOfFrame()
を再現できないので注意が必要です。
// 実装
public static UniTask ToUniTask(this IEnumerator enumerator, PlayerLoopTiming timing = PlayerLoopTiming.Update, CancellationToken cancellationToken = default(CancellationToken))
// 使用例
await HogeCoroutine(); // コルーチン起動
await HogeCoroutine().ToUniTask(PlayerLoopTiming.FixedUpdate, token);
await HogeCoroutine().WithCancellation(token);
下記は MonoBehaviour
を引数に渡すことで内部でStartCoroutine
が走ります。なので yield return new WaitForEndOfFrame()
も正常動作しますし、寿命もゲームオブジェクトと結びついてくれます。
// 実装
public static UniTask ToUniTask(this IEnumerator enumerator, MonoBehaviour coroutineRunner)
// 使用例
await HogeCoroutine().ToUniTask(this);
逆変換
UniTask を IEnumerator に変換できます
// 実装
public static IEnumerator ToCoroutine<T>(this UniTask<T> task, Action<T> resultHandler = null, Action<Exception> exceptionHandler = null)
public static IEnumerator ToCoroutine(this UniTask task, Action<Exception> exceptionHandler = null)
// 使用例
StartCoroutine(uniTask.ToCoroutine(Debug.Log,Debug.LogError));
StartCoroutine(uniTask.ToCoroutine(Debug.LogError));
エラーハンドリングや結果を得るためのデリゲートを渡せるオーバーロードが用意されてます
AsyncOperation
AsyncOperation
を UniTask に変換できます
UniTask には 様々な AsyncOperation が awaitable になっています。
- AsyncOperation
- ResourceRequest
- AssetBundleRequest
- AssetBundleCreateRequest
- UnityWebRequest
シグネチャは拡張メソッドのthisキーワードが付いた引数の型の違いしかなく、使い方は全部一緒なので省きます。
// 実装
public static UniTask ToUniTask(this AsyncOperation asyncOperation, IProgress<float> progress = null, PlayerLoopTiming timing = PlayerLoopTiming.Update, CancellationToken cancellationToken = default(CancellationToken))
// 使用例
await SceneManager.LoadSceneAsync("Game");
await SceneManager.LoadSceneAsync("Game").ToUniTask(cancellationToken : token);
await SceneManager.LoadSceneAsync("Game").WithCancellation(token);
await SceneManager.LoadSceneAsync("HogeScene")
.ToUniTask(Progress.Create<float>(n => {
Debug.Log($"現在{n * 100}%");
}), PlayerLoopTiming.Update, token);
SceneManager.LoadSceneAsync
の例です。
.ToUniTaskに Progress
を入れて進行状況を取ることができます。
AsyncLazy
UniTask に用意されている AsyncLazy型
です。ToUniTask ではなく .Taskプロパティ
から取得できます
.Taskプロパティ
var lazy = UniTask.Lazy(() => uniTask);
var task = lazy.Task; // UniTaskに変換
逆変換
UniTask から AsyncLazy に変換できます
uniTask.ToAsyncLazy(); // AsyncLazyに変換
IObservable
IObservable
を UniTask に変換できます。
ToUniTask
// 実装
public static UniTask<T> ToUniTask<T>(this IObservable<T> source, bool useFirstValue = false, CancellationToken cancellationToken = default)
// 使用例
await subject.ToUniTask(cancellationToken:token); // OnCompletedが発行されるまで待つ
await subject.ToUniTask(true,token); //次に発行される最初のメッセージを待つ
第一引数をtrue
にすると、次に発行される最初のメッセージだけを待機できます。
逆変換
UniTask を IObservable に変換できます。
uniTask.ToObservable();
JobHandle
ジョブの実行が終了するまで待機できます
// 実装
public static UniTask ToUniTask(this JobHandle jobHandle, PlayerLoopTiming waitTiming)
public static async UniTask WaitAsync(this JobHandle jobHandle, PlayerLoopTiming waitTiming, CancellationToken cancellationToken = default)
// 使用例
await jobHandle;
// CancellationTokenが指定できない
await jobHandle.ToUniTask(PlayerLoopTiming.FixedUpdate);
// 指定したタイミングに切り替えてjobHandle.Complete();される
await jobHandle.WaitAsync(PlayerLoopTiming.FixedUpdate,token);
実装を見ればどれも違うのがわかりますが、自分はJobHandleに詳しくないので有識者の方、コメントや編集リクエストをお願いします!
AsyncGPUReadbackRequest
AsyncGPUReadbackRequest
を UniTask に変換できます。
ToUniTask
// 実装
public static UniTask<AsyncGPUReadbackRequest> ToUniTask(this AsyncGPUReadbackRequest asyncOperation, PlayerLoopTiming timing = PlayerLoopTiming.Update, CancellationToken cancellationToken = default(CancellationToken))
await AsyncGPUReadback.Request(src);
await AsyncGPUReadback.Request(src).ToUniTask(cancellationToken : token);
await AsyncGPUReadback.Request(src).WithCancellation(token);
逆変換
UniTask を AsyncGPUReadbackRequest に変換できます
var asyncGpuReadbackRequest = AsyncGPUReadback.Request(src);
asyncGpuReadbackRequest.ToUniTask(PlayerLoopTiming.FixedUpdate,token);
おわりに
文章書くの下手すぎて読みにくいかもです。すいません。
間違いがあったら優しく教えてください。
変更履歴
変更1(コルーチン)
IEnumerator.ToUniTask()は何も指定しないと「コルーチンっぽく動く」だけなので yield return new WaitForEndOfFrame(); は正しく動かない。
— とりすーぷ (@toRisouP) January 6, 2021
IEnumerator.ToUniTask(MonoBehaviour)は内部でStartCoroutineするから本物のコルーチンが起動するのでちゃんと動作する。
とりすーぷさんからご指摘をいただきコルーチンの項目を少し書き換えました!ありがとうございます!