質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.48%
RxJava

RxJavaは、リアクティブプログラミングができるReactiveXのJava向けの実装。軽量であり、その他のライブラリへの依存がなく、RxJavaのjarをパスに通せば使用できます。バージョン2.3からはAndroidもサポートしています。

RxJS

RxJSは、Observablesを用いたリアクティブプログラミングのJavaScript向けの実装です。イベント駆動処理も含めた非同期処理を高い可読性を持って容易にコーディングできます。

Rx.NET

Rx.NETは、リアクティブプログラミングが可能なライブラリの一つ。.NET Framework向けReactive Extensionsライブラリです。マイクロソフト社が初めてRxライブラリとして提供し、ここから多くの言語にRxが移植されるようになりました。

RxSwift

RxSwiftは、Reactive ExtensionsのSwift向けの実装です。iOS開発に用いられ、リアクティブプログラミングを可能にします。

Unity

Unityは、Unity Technologiesが開発・販売している、IDEを内蔵するゲームエンジンです。主にC#を用いたプログラミングでコンテンツの開発が可能です。

Q&A

解決済

1回答

805閲覧

[Rx] 効率の良いストリームの合成方法

IShix

総合スコア1724

RxJava

RxJavaは、リアクティブプログラミングができるReactiveXのJava向けの実装。軽量であり、その他のライブラリへの依存がなく、RxJavaのjarをパスに通せば使用できます。バージョン2.3からはAndroidもサポートしています。

RxJS

RxJSは、Observablesを用いたリアクティブプログラミングのJavaScript向けの実装です。イベント駆動処理も含めた非同期処理を高い可読性を持って容易にコーディングできます。

Rx.NET

Rx.NETは、リアクティブプログラミングが可能なライブラリの一つ。.NET Framework向けReactive Extensionsライブラリです。マイクロソフト社が初めてRxライブラリとして提供し、ここから多くの言語にRxが移植されるようになりました。

RxSwift

RxSwiftは、Reactive ExtensionsのSwift向けの実装です。iOS開発に用いられ、リアクティブプログラミングを可能にします。

Unity

Unityは、Unity Technologiesが開発・販売している、IDEを内蔵するゲームエンジンです。主にC#を用いたプログラミングでコンテンツの開発が可能です。

0グッド

1クリップ

投稿2017/12/23 06:27

編集2018/01/05 12:06

ご質問させていただきます。よろしくお願いいたします。
Unity用のRxプラグインを使用させていただき開発しております。その中でストリームの合成がすごく難しくコードが複雑になってしまいこれで正しいのか分からないのでご質問させていただきたいです。

###環境
・Unity 2017.2.0p4
UniRx 5.5.0

###実装したい処理

  1. (初期化) Vector2のデータをランダムに1つ生成
  2. (初期化) ランダムに1度00秒遅延させる
  3. (開始) UpdateAsObservableでループ開始
  4. (判定) 1で生成したデータとオブジェクトの距離が一定の距離になるまで処理を続ける
  5. (OnNext) Subscribeで1で生成したデータに徐々に近づける
  6. (Oncompleted) 完了後1から繰り返し

###実現したいこと
コードが複雑になっているので簡潔な方法がありましたらご教授いただきたいです。プロパティまたはフィールド(クロージャ)などの外部からの参照無しで実装したいです。言語は問いません。大変お手数ですがよろしくお願いいたします。

###現状のコード

C#

1static readonly float WALK_SPEED = .7f; 2static readonly float RAND_INTERVAL_MIN = 4; 3static readonly float RAND_INTERVAL_MAX = 10; 4static readonly float DISTANCE = .05f; 5static readonly Vector2 RAND_POS_LEFT_TOP = new Vector2(-1, 3); 6static readonly Vector2 RAND_POS_RIGHT_BOTTOM = new Vector2(-2, 4); 7Transform MyTransform; 8 9void Start () 10{ 11 MyTransform = gameObject.transform; 12 13 Observable.Empty<Vector2>().StartWith(() => GetRandPos()) 14 .SelectMany(pos => 15 Observable.Timer(TimeSpan.FromSeconds(UnityEngine.Random.Range(RAND_INTERVAL_MIN, RAND_INTERVAL_MAX))) 16 .Select(_ => pos) 17 ) 18 .SelectMany(pos => this.UpdateAsObservable().Select(_ => pos)) 19 .TakeWhile(pos => Vector2.Distance(pos, MyTransform.position) >= DISTANCE) 20 .Select(pos => GetNextPos(pos, MyTransform.position)) 21 .RepeatUntilDestroy(this) 22 .Subscribe(pos => MyTransform.position = pos); 23} 24 25Vector2 GetNextPos(Vector2 target,Vector2 me) 26{ 27 var rad = Mathf.Atan2(target.y - me.y, target.x - me.x); 28 29 me.x += WalkSpeed * Time.deltaTime * Mathf.Cos(rad); 30 me.y += WalkSpeed * Time.deltaTime * Mathf.Sin(rad); 31 return me; 32} 33 34Vector2 GetRandPos() 35{ 36 var x = UnityEngine.Random.Range( 37 RAND_POS_LEFT_TOP.x, 38 RAND_POS_RIGHT_BOTTOM.x 39 ); 40 41 var y = UnityEngine.Random.Range( 42 RAND_POS_LEFT_TOP.y, 43 RAND_POS_RIGHT_BOTTOM.y 44 ); 45 46 return new Vector2(x, y); 47}

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答1

0

ベストアンサー

遅延抜きで繰り返し1回部分のストリーム構築を考えてみます。

求めたい座標のストリーム(一回分)をys_
元となる座標のストリームをxsとします。

ys_xsの最新値とys_の最後の値から構築されるのでScan()オペレータを使います
ys_はある条件で終了するのでTakeWhile()オペレータを使います

ys_ = xs.Scan(...).TakeWhile(...)

説明のためys_の初期値y0を与えてys_を構築する関数をfと置きます。
f = y0 => ys_

次に遅延部分を考えます。

Observable.Timer()を使い一つだけ値をランダムな遅延させて流します。
流れてきた値は捨てて、y0を生成して差し替えます。
これにSelectMany(f)をくっつければ遅延してys_を生成して流してくれるはず

Observable.Timer(...).Select(...).SelectMany(f)

後は全体をRepeat()すれば繰り返すはず

ys = Observable.Timer(...).Select(...).SelectMany(f).RepeatUntilDestroy(...)

UniRxではなくRxで、Vector2でなく1次元でとりあえずやってみましたが
あまり簡潔になってないですね。すみません。

C#

1// テスト用にもととなる座標ストリームをでっち上げる 2var xs = Observable.Interval(TimeSpan.FromSeconds(1)) 3 .Select(x => (double)x) 4 .Take(60).Publish().RefCount(); 5 6 7var ys = Observable.Timer(TimeSpan.FromMilliseconds(rand.Next(1500, 4500))) 8 .Select(_ => GetRandPos()) 9 .SelectMany(y0 => 10 xs.Scan(new { Y = y0, Dist = 10.0 }, // 初っ端でTakeWhileに引っかからないように(雑) 11 (acc, x) => 12 { 13 var y = GetNextPos(x, acc.Y); 14 return new { Y = y, Dist = Math.Abs(x - y) }; 15 }) 16 .TakeWhile(acc => acc.Dist > 1.0) 17 .Select(acc => acc.Y)) 18 .Repeat(); 19 20 21xs.Timestamp().Subscribe(x => Console.WriteLine($"x , {x.Timestamp.Ticks} , {x.Value}")); 22ys.Timestamp().Subscribe(y => Console.WriteLine($"y , {y.Timestamp.Ticks} , {y.Value}"));

適当にGetHOGE()を設定して動かしてみました:
イメージ説明
横軸:時間
縦軸:座標


回答ではなく質問です。追記依頼覧が狭いので。
(聞いたところで難しそうなので回答できるかはちょっと...すみません)

  • ある移動体Pの座標(ストリーム)から、ある規則で動く別の移動体Qの座標(ストリーム)を求めたい。
  • Qの座標はPの座標とQの前回座標から求められる。(GetNextPos())
  • Qの初期座標はランダムに与えられる。(GetRandPos())
  • PとQの距離が一定以下になったら、Qの座標はランダムに与え直される。

ということでよろしいでしょうか?

ランダムな遅延が読み解けなかったんですが、
目的のストリームはどちらですか?

1,2,...,5 座標の出力タイミング R リセット 1) |-------(Delay)------ 1 --- 2 --- 3 --- 4 --- R -----(Delay)---- 5 ---> 2) |1 ------(Delay)------ 2 --- 3 --- 4 --- R5 ---(Delay)--->

投稿2017/12/25 07:43

編集2017/12/28 01:26
ozwk

総合スコア13521

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

IShix

2017/12/27 02:41

説明不足ですみません。ご返答ありがとうございます。箇条書きの内容に間違いありません。図にしていただいているものは1)が目的のストリームとなります。
IShix

2017/12/28 10:21

具体的にご教授いただきありがとうございます。 Scanを使用したことがないので、いただいたコードを元に勉強させていただます。Timestampはこういう風に使うんですね。大変勉強になりました。Rx凄く難しいですが便利なので理解を更に深めていきたいと思います。
ozwk

2018/01/05 04:48

今更ですが気になっていたので ラムダ式のアンダースコアは「捨てられる引数」など、 どうでもいい値に使うという暗黙の了解があります(私だけ?) なもので、 .TakeWhile(_ => Vector2.Distance(_, MyTransform.position) >= DISTANCE) のように、ラムダ式の本体側に_が出てくるとびっくりします。
IShix

2018/01/05 12:06

確かにそうですね。ご指摘ありがとうございます。コードも修正しました。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問