1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

UniRxのFromCoroutineValueは同一フレームで実行される

Posted at

起こった問題

UniRxのFromCoroutineValueはコルーチンから戻り値を受け取ってRxのストリームに流せるので便利なのですが、同一フレームで実行されるという罠がありました。FromCoroutineValueを使わずにFromCoroutineを使うことで解決できました。

普通のコルーチン

public class CoroutineTest : MonoBehaviour
{
    void Start()
    {
        StartCoroutine(MyCoroutine());
    }

    IEnumerator<int> MyCoroutine()
    {
        for(int i = 0; i < 10; i++)
        {
            Debug.Log(Time.frameCount); // コルーチン内で何フレーム目か表示
            int someValue = 10;
            yield return someValue;
        }
    }
}

Unityではyield return以降は次フレームで実行される(Unityのyield returnの仕様と言うよりStartCoroutineの仕様?)ので適当なGameObjectに貼り付けて実行すると以下のような結果になります。
aaa.png

FromCoroutineValue

コルーチン内でなにか処理を行ってその結果someValueを返したいといったシチュエーションは結構あると思うのですが、通常のコルーチンでは厄介です。そんなとき便利なのがUniRxのFromCoroutineValueなのですがこれには同一フレームで実行されるという罠があります。

void Start()
{
    Observable.FromCoroutineValue<int>(MyCoroutine)
        .Subscribe(v => {});
}

これを実行すると以下のようなログが出ます。
bbb.png
yield returnで次フレームに移っておらず同一フレームで実行されてしまっています。ですので無限ストリームが作りたいからといって以下のようなコードを書くと永遠に次フレームに移れないのでUnity Editorがフリーズします(悲しい世界)

public class CoroutineTest : MonoBehaviour
{
    void Start()
    {
        Observable.FromCoroutineValue<int>(MyEndlessCoroutine)
            .Subscribe(v =>
            {
                Debug.Log(v);
            });
    }

    IEnumerator<int> MyEndlessCoroutine()
    {
        while (true)
        {
            yield return Random.Range(0, 10);
        }
    }
}

じゃあどうするか

FromCoroutineを使ってIObserver経由で渡します。どういうわけか、FromCoroutineではyield returnで次フレームに移る仕様のようです。先程の無限ストリームは以下のように実現できます。

public class CoroutineTest : MonoBehaviour
{
    void Start()
    {
        Observable.FromCoroutine<int>(observer => MyEndlessCoroutine(observer))
            .Subscribe(v => 
            {
                Debug.Log(v);
            });
    }

    IEnumerator MyEndlessCoroutine(IObserver<int> observer)
    {
        while(true)
        {
            yield return null;
            observer.OnNext(Random.Range(0, 10));
        }
    }
}
1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?