← logs に戻る
2026/07/01 · 5 min read ⚙️ intermediate

UniTask ではじめる Unity の async/await

コルーチンや Task のつらみを避けつつ、Unity で async/await を自然に書くための UniTask 入門。基本の書き方・実行タイミング・キャンセルまで。

Unity で「数秒待つ」「ロードを待つ」といった非同期処理は、長らくコルーチンの担当でした。UniTask は、それを C# の async/await でそのまま書けるようにするライブラリです。この記事では、なぜ UniTask なのか・基本の書き方・実行タイミング・キャンセルまでを一気に押さえます。

なぜ UniTask なのか

Unity 標準の非同期の選択肢は「コルーチン」か「Task」ですが、どちらも Unity では相性が悪い部分があります。

  • コルーチンIEnumerator を返すだけなので、戻り値を返せないtry/catchyield をまたげないawait できない
  • 標準の Taskasync/await が使えるものの、Taskクラス(ヒープ確保)でありフレーム毎に回すとGCを生む。さらにシーン遷移やオブジェクト破棄で自動的に止まらない

UniTask(Cysharp 製)は、この隙間を埋めます。

UniTaskstruct(値型) として実装され、待機の一般パスでヒープ確保ゼロ。Unity の PlayerLoop 上で回るので、スレッドを渡り歩かず待機後もそのまま Unity API を触れる

🧭 純粋な C# の Task はクラスで、await の継続はスレッドプールに載ることがある。UniTask はメインスレッドの PlayerLoop に閉じているので、この違いを意識せず書ける。

基本の書き方

戻り値がなければ UniTask、あれば UniTask<T> を返します。await の使い勝手は素の C# と同じです。

using Cysharp.Threading.Tasks;
using UnityEngine;

public class Loader : MonoBehaviour
{
    async UniTask StartAsync()
    {
        Debug.Log("開始");
        await UniTask.Delay(1000);          // 1秒待つ(コルーチンの WaitForSeconds 相当)
        int value = await FetchAsync();      // 戻り値を受け取れる
        Debug.Log($"取得: {value}");
    }

    async UniTask<int> FetchAsync()
    {
        await UniTask.Delay(500);
        return 42;                           // コルーチンでは返せなかった戻り値
    }
}

AsyncOperation を返す Unity の API は、そのまま await できます。

await SceneManager.LoadSceneAsync("Main");   // ロード完了まで待つ

コルーチンとの対応

コルーチンで書いていた待機は、ほぼ1対1で置き換えられます。

コルーチンUniTask
yield return new WaitForSeconds(1f)await UniTask.Delay(1000)
yield return nullawait UniTask.Yield()
yield return new WaitForFixedUpdate()await UniTask.WaitForFixedUpdate()
yield return new WaitUntil(() => flag)await UniTask.WaitUntil(() => flag)
yield return op(AsyncOperation)await op

複数の非同期を束ねる操作も揃っています。

// 全部の完了を待つ(Task.WhenAll 相当)
await UniTask.WhenAll(LoadA(), LoadB(), LoadC());

// どれか1つ完了したら進む
int winIndex = await UniTask.WhenAny(LoadA(), LoadB());

実行タイミング(PlayerLoop)

コルーチンの yield に更新タイミングがあったように、UniTask もどのタイミングで再開するかPlayerLoopTiming で指定できます。

await UniTask.Yield(PlayerLoopTiming.FixedUpdate);   // 次の FixedUpdate で再開
await UniTask.NextFrame();                            // 次フレームまで待つ
await UniTask.DelayFrame(3);                          // 3フレーム待つ

💡 物理演算に触る処理は FixedUpdate タイミングで再開させると、フレーム落ち時の挙動が安定する。

キャンセルを必ず考える

UniTask を使ううえで最重要なのがキャンセルです。オブジェクトが破棄されても走り続ける非同期は、破棄済みオブジェクトへのアクセスで落ちる原因になります。CancellationToken を渡して止めましょう。

async UniTask RunAsync()
{
    var ct = this.GetCancellationTokenOnDestroy();   // このオブジェクト破棄で自動キャンセル
    while (true)
    {
        await UniTask.Delay(1000, cancellationToken: ct);
        Debug.Log("生きてる");
    }
    // 破棄されると Delay が OperationCanceledException を投げてループを抜ける
}

⚠️ 標準の Task はシーン遷移やオブジェクト破棄を知らないので、放置すると裏で走り続ける。UniTask ではトークンを渡すのを習慣にする

🧭 CancellationToken の考え方は純粋な C# と同じ。違いは「破棄と連動したトークンを Unity 側が用意してくれる」点。

なお、戻り値も完了も待たない「撃ちっぱなし」にしたいときは Forget() を付けます。

RunAsync().Forget();   // await しない意思表示(警告も消える)

まとめ

  • UniTask は Unity で async/await を自然に書くためのライブラリ。コルーチンの「戻り値なし・try/catch 不可・await 不可」を解消する。
  • UniTaskstruct でヒープ確保ゼロ、PlayerLoop 上で回るので待機後もそのまま Unity API を触れる。
  • 待機は UniTask.Delay / Yield / WaitUntil などでコルーチンと1対1に置き換わる。AsyncOperation は直接 await できる。
  • 再開タイミングは PlayerLoopTiming で制御する。
  • キャンセルは必須GetCancellationTokenOnDestroy() のトークンを渡して、破棄後に走り続けないようにする。

次にやること: UniTask.WhenAll を使ったロード画面の並列化や、IUniTaskAsyncEnumerable によるイベントストリーム(UniRx 的な書き方)に踏み込むと、非同期の幅が一気に広がる。

← logs 一覧 tags →