【UniRx】UniRxとコルーチン

  • 51
    Like
  • 0
    Comment

この記事はちょっと古いのでこちらの新しい記事を参照ください
UniRx入門 その5 -コルーチンとの併用-


UniRxはコルーチンと組み合わせることでより便利になります。
今回はその方法を紹介します。

コルーチンをObservableに変換する

Observable.FromCoroutineを使うことでコルーチンをObservableに変換することができます。
FromCoroutineについては@neueccさん本人が詳しく説明されていますので、こちらを参考にされるのが一番良いかと思います。

Unityのコルーチンの分解、或いはUniRxのMainThreadDispatcherについて

FromCoroutineは特にコルーチンから戻り値のあるObservableを作る方法の汎用性が高く、ファクトリーメソッドやオペレータを組み合わせるよりもより簡潔に処理を記述できる場合があります。

例)一定条件の時のみカウントダウンするタイマをコルーチンから作る

戻り値のあるObservableをコルーチンから作る
private void Start()
{
    //
    //playerの初期化とかここでやってる
    //

    //プレイヤが生存している時間を30秒間カウントダウンする
    //タイマの現在のカウント[秒]が通知される
    Observable.FromCoroutine<int>(observer => CountDownCoroutine(observer, 30, player))
        .Subscribe(count => Debug.Log(count));
}

/// <summary>
/// プレイヤが生きている間だけカウントダウンするタイマー
/// プレイヤが死んでいる場合はカウントは停止する
/// </summary>
IEnumerator CountDownCoroutine(IObserver<int> observer,int startTime,Player player)
{
    var currentTime = startTime;
    while (currentTime > 0)
    {
        if (player.IsAlive)
        {
            observer.OnNext(currentTime--);
        }
        yield return new WaitForSeconds(1);
    }
    observer.OnCompleted();
}

Observableをコルーチンに変換する

Observableをコルーチンに変換する場合はStartAsCoroutineを使います。

StartAsCoroutineの例

private void Start()
{
    StartCoroutine(CoroutineA());
}

IEnumerator CoroutineA()
{

    //コルーチンの実行結果を保存する変数
    var result = 0;

    //Observable.Rangeをコルーチンに変換する
    yield return Observable.Range(0, 10).StartAsCoroutine(c => result = c);

    Debug.Log("Result:"+result);
}
実行結果
Result:9

StartAsCoroutineは以下の特徴があります。

  • OnCompletedが発行されるまでの間yield return nullを繰り返し続ける
  • StartAsCoroutineの引数に渡した関数はOnCompleted発行時に1回だけ実行され最後のOnNext値が渡される

StartAsCoroutineの使いドコロ

StartAsCoroutineを上手く使うと、非同期処理が入り組んだ処理を同期処理っぽく扱えるようになります。
いわゆるTaskのawait処理っぽいものを実現することができるようになります。



private IEnumerator HeavyTaskCoroutine()
{
    //実行結果
    bool result = false;

    //非同期処理の待ち受け
    //Observable.Startは別スレッドで処理を実行する
    yield return Observable.Start(()=>HeavyTask()).StartAsCoroutine(x => result = x);

    //実行結果を判定する
    if (result)
    {
        Debug.Log("Success");
    }
    else
    {
        Debug.Log("Failure");
    }
}

/// <summary>
/// 実行に時間がかかる重い処理
/// </summary>
/// <returns>成否</returns>
bool HeavyTask()
{
    //重い処理をする
    Thread.Sleep(3000);

    //実行の成否を返す(擬似的にランダムにtrue/falseを返す)
    var random = new System.Random();
    return random.Next() % 2 == 0;
}

まとめ

UniRxはコルーチンと組み合わせることでより真価を発揮します。
全てをRxのストリームで無理に書くのではなく、FromCoroutineとStartAsCoroutineを上手に利用して読みやすく扱いやすいコードを書きましょう。