お米 is ライス

C#やらUnityやらを勉強していて、これはメモっといたほうがええやろ、ということを書くつもりです

UniRxでUniTaskをSubscribeしたい

あるイベントが発行されたときに、非同期的な処理を開始したかった(例:マウスがクリックされた時一定時間毎フレーム何か処理をするみたいな)

パターン①

    UniTask _task;
    private void Start()
    {
        _task = UniTask.Defer(() => HogeTask());
        this.UpdateAsObservable()
              .Where(u => Input.GetMouseButtonDown(0))
              .Subscribe(async u => await _task);
    }

    private async UniTask HogeTask()
    {
        for(var i = 0;i < 10;i++)
        {
            Debug.Log($"Hoge {i}");
            await UniTask.Delay(100);
        }
    }

パターン②

    private void Start()
    {
        System.Action<Unit> action = async u => await HogeTask("action1");
        action += async u => await HogeTask("action2");
        this.UpdateAsObservable().Where(u => Input.GetMouseButtonDown(1)).Subscribe(action);
    }

    private async UniTask HogeTask(string text)
    {
        for(var i = 0;i < 10;i++)
        {
            Debug.Log($"{text} {i}");
            await UniTask.Delay(100);
        }
    }

ちょっと説明

UniTaskは生成された瞬間から処理が開始されるみたいな感じになっている
なので、UniTaskを変数に保持しておこうと思ってあらかじめ生成しておきたくてもその時点から処理が始まってしまう

パターン①

そこでUniTaskでは「awaitするまで生成しない」ようにできる関数が用意されていて(UniTask.Defer)、そいつにUniTask(本命)を生成する関数を渡して返されたUniTask(義理)を使えば、UniTask(義理)をawaitした瞬間からUniTask(本命)が開始されてくれる

UniRxのあるイベントが発行された時にUniTask(本命)を開始したい場合は上に書いたソースコードのようにSubscribeにUniTask(義理)をawaitするラムダ式を渡してやればよい(ラムダ式にasyncつけれるの初めて知った)
上の例だと左クリックされた瞬間から「Hoge {i}」とログが出され始める

パターン②

遅延生成の肝はUniTaskを生成する関数を保持しておくことなことに気づいたので、
任意の数のUniTaskを任意のタイミングで開始したい場合、②のようにすればいい感じになった

補足

変数に保持しておかなくてもアクセスできる場所に本命のUniTask関数がある場合、Subscribeの中身は「async u => await _task」ではなく「u => HogeTask()」でもいい
外部から渡されたUniTaskを変数に保持しておいて任意のタイミングで開始したい場合に上のコードが必要になってくると思う

追記

めっちゃおなかすいた