ちょっとご確認いただきたいのですが、コードを下記のようにすると、コンソールにはどのように表示されるでしょうか?
C#
1// 省略
2
3 void Awake()
4 {
5 this.UpdateAsObservable()
6 .Where(_ => IsRead)
7 .TakeWhile(_ => time > 0)
8 .Subscribe(_ =>
9 {
10 Debug.LogFormat("time: {0}", time);
11 DiscountReadingTime();
12 },
13 () => Debug.LogFormat("Completed! time: {0}", time));
14 }
15
16// 省略
17
18 // Readメソッドから呼ばれるメソッド
19 IEnumerator EndReading()
20 {
21 yield return new WaitForSeconds(time);
22 Debug.Log("Reset!");
23 IsRead = false;
24
25 time = 5;
26 remainingTime.text = time.ToString();
27 }
28
29// 省略
私の予想ですと、うまくいく場合では最後に「Reset!」だけが表示され、ボタンが無反応になる場合では先に「Completed! time:...」が表示されて、その後に「Reset!」が表示されるのではないでしょうか。
ちょっと調べた感じでは、TakeWhile
がfalse
になるとそこでUpdateAsObservable
のストリームは終了してしまい、以降time
やIsRead
を元に戻しても、ストリームがなくなっているので何も起こらない...という気がします。
想像するに、コルーチンによるリセットとTakeWhile
によるストリーム終了判定が拮抗しており、先にリセットされた場合はうまくいくが、先にストリームが終了した場合は失敗するという状況ではないでしょうか?
改善案としては...
案1:
TakeWhile
をやめて、time > 0
でフィルタリングするだけにし、ストリームを止めないようにする。
C#
1// 省略
2 void Awake()
3 {
4 this.UpdateAsObservable()
5 .Where(_ => IsRead && time > 0)
6 .Subscribe(_ => DiscountReadingTime());
7 }
8// 省略
案2:
Awake
は削除、EndReading
は通常のメソッドにし、Read
内でストリーム購読を開始して、ストリーム完了時にEndReading
を実行する。
C#
1public class PlayerControl : MonoBehaviour
2{
3
4 public bool IsRead;
5 public Text remainingTime;
6 public float time = 5.0f;
7
8 // ボタンを押すと呼ばれるメソッド
9 public void Read()
10 {
11 if (IsRead)
12 {
13 return;
14 }
15
16 IsRead = true;
17 this.UpdateAsObservable()
18 .TakeWhile(_ => time > 0)
19 .Subscribe(_ => DiscountReadingTime(), EndReading);
20 }
21
22 // Readメソッドが呼ばれることでUnirxにより走るメソッド
23 void DiscountReadingTime()
24 {
25 time -= Time.deltaTime;
26 remainingTime.text = ((int)time).ToString();
27 }
28
29 // Readメソッドから呼ばれるメソッド
30 void EndReading()
31 {
32 IsRead = false;
33 time = 5;
34
35 if (remainingTime != null)
36 {
37 remainingTime.text = time.ToString();
38 }
39 }
40}
案3:
案2をベースとしつつ、イベントストリームをUpdateAsObservable
から1秒間隔のInterval
に置き換える(参考: UniRxのシンプルなサンプル その9(TimerとInterval 一定時間後に実行) - Qiita)。
time
は初期値5.0f
のまま書き換えないようにし、ストリームに流れてくるプッシュ回数がtime
未満の間テイクしてDiscountReadingTime
を実行する。
DiscountReadingTime
は引数としてプッシュ回数を受け取れるようにし、残り時間に換算してremainingTime
に表示する。
案2と同様、ストリーム完了時にEndReading
を実行する。
C#
1public class PlayerControl : MonoBehaviour
2{
3
4 public bool IsRead;
5 public Text remainingTime;
6 public float time = 5.0f;
7
8 // ボタンを押すと呼ばれるメソッド
9 public void Read()
10 {
11 if (IsRead)
12 {
13 return;
14 }
15
16 IsRead = true;
17 Observable.Interval(TimeSpan.FromSeconds(1))
18 .TakeWhile(i => i < time)
19 .Subscribe(DiscountReadingTime, EndReading);
20 }
21
22 // Readメソッドが呼ばれることでUnirxにより走るメソッド
23 void DiscountReadingTime(long eventIndex)
24 {
25 remainingTime.text = ((int)(time - (eventIndex + 1))).ToString();
26 }
27
28 // Readメソッドから呼ばれるメソッド
29 void EndReading()
30 {
31 IsRead = false;
32
33 if (remainingTime != null)
34 {
35 remainingTime.text = time.ToString();
36 }
37 }
38}
ずらずらと色々書いた後ですみませんが、恥ずかしながら私はUniRxについては名前しか聞いたことがなく(かなり便利で強力だという評判は目にしていたものの、リアクティブプログラミングに馴染みがなく手出ししていなかったのです)、ご質問者さんの方がお詳しいかと思います。
もし「こんな書き方だとこんな不具合があるよ」とか「こう修正するともっと効率的だよ」とかご指摘ありましたらぜひお願いします。
UniRxは多機能で十分理解するのに時間を要しそうですが、面白そうですね。もっと積極的に取り入れてみようかと思います。
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
2018/01/27 02:41