Unityに久しぶりに触ることになり、それがきっかけでReactive Extentionsに再び興味が高まりました。
試すにあたり、まずUnity, ReactiveExtensions, C#, Coroutine について過去記事を振り返ってみます。
「Unity 5 / C#」 http://crossframe.iiv.jp/201611241241/
Windowsストアアプリの開発や、BizSparkに加入していたことで ”crossframe”BlogにMicrosoftテクノロジーを研究していた時期があります。(当時はhttp://xfra.meというドメイン) そのときC#を活用する環境しとしてUnityにをとりあげました。「C#を一段と魅力的にしている」環境という感想からもどちらかといえばC#きっかけで始めた感があります。
「Reactive Extensions」 http://crossframe.iiv.jp/201604021188/
「C# LINQのクエリ式」 http://crossframe.iiv.jp/20130503247/
当時C#のLINQに惹かれていて、Reactive Extentionsも取り上げていたみたいです。(すっかり忘れしたいました。こういうときBlogに書いておいてよかったと思えます)
「Co-Routine / Scheme」 http://bitlife.me/archives/312
「Co-Routine / Python」 http://bitlife.me/archives/265
“crossframe”BlogはMicrosoftテクノロジー専用だったので。それ以外は”bitlife”Blogに記事を書いていました。(たぶん)
ここに今回試すコルーチンを取り上げています。
Schemeのcall/cc : call-with-current-continuation がわかりやすい説明です。
今回Reactive Extensions for Unity(以下UniRx)を試すために、参考書として「UniRx/UniTask完全理解」(アスキードワンゴ)を購入しました。
一番興味深いのは、時間軸のプログラミングがシンプルに記述できることです。ネットでよくある事例としてダブルクリックとシングルクリックの判定の仕方とかありますが、こういった制御がとても得意です。ということからもゲーム開発環境であるUnityととても相性がいいのは、必然と言えます。またUnityのStart(init),Updateという関数で起動されるプログラム構造(Processingなどもそうですが)は、コルーチンがとても有効にはたらく特徴があります。
試したかったことは同時性の判定で、それぞれ別のタイミングで動いていイベントが同時刻の場合のみ特定の処理をすることです。
環境: Unity2020.3.20f1 / Mac
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 |
using System.Collections; using System.Collections.Generic; using UnityEngine; using System; using UniRx; public class NewBehaviourScript1 : MonoBehaviour { void Start() { IObservable<int> clk = Observable .EveryUpdate().Where(_ => Input.GetMouseButtonDown(0)).Select(_ => 3); IObservable<int> co1 = Observable .FromCoroutine<int>(observer => Coroutine1(observer)); IObservable<int> co2 = Observable .FromCoroutine<int>(observer => Coroutine2(observer)); co1.Merge(co2).Merge(clk).TimeInterval().Buffer(2,1).Subscribe(t => { if(t[0].Value != 3 && t[1].Value == 3) { if (t[1].Interval < TimeSpan.FromSeconds(0.2f)) { Debug.Log("Hit! : " + t[0].Value + " / " + t[1].Interval); }; } if(t[1].Value != 3) { Debug.Log(t[1].Interval + " ( " + t[1].Value + " )"); } }); } private IEnumerator Coroutine1(IObserver<int> observer) { for (int i = 0; i < 30;i++) { observer.OnNext(1); yield return new WaitForSeconds(2); } observer.OnNext(0); observer.OnCompleted(); } private IEnumerator Coroutine2(IObserver<int> observer) { for (int i = 0; i < 20; i++) { observer.OnNext(2); yield return new WaitForSeconds(3); } observer.OnNext(0); observer.OnCompleted(); } // Update is called once per frame void Update() { } } |
2つのコルーチンはそれぞれ2秒、3秒間隔でメッセージを送信し、マウスクリックをするタイミングがこれらのメッセージと同時の場合(0.2秒以内)、どちらと同時かLogに出力します。
メッセージの識別として数値(1,2,3)を使っています。Merge, TimeInterval, Bufferこれらのメソッドチェーンが肝になります。前回値の取り出し方、取り出したあとの比較、これらが簡単にかける仕組みになっています
学習コストが高いといわれるUniRxですが、実現したいことに絞って調査すれば有効に使えると思います。
しかしながらC#のポテンシャルの高さを改めて感じます。Unityがクロスプラットホームであることから、C#もクロスプラットホームで使えることになります。もちろんUnity以外でも使える環境はあるのですが、Unityという”かなり”実用的な環境であることの意義はとても大きいと感じました。