「経過秒数を小数点含めて保持しポーズ時にDisposeで破棄、再開時に経過秒数で初期化しConnect」とおっしゃる方針でも行けるかもしれませんが、ちょっと複雑になってしまいそうな気配がしますね...
Timer
の代わりにコルーチンで時間を計るというのはどうでしょうかね?
試しにCreateCountDownObservable
が作るストリームをコルーチンベースに変更してみました。
C#
1using System;
2using System.Collections;
3using UniRx;
4using UnityEngine;
5
6public class TimerManager : MonoBehaviour
7{
8 private IConnectableObservable<int> gameStartCountDownObservable;
9
10 private IConnectableObservable<int> inPlayCountDownObservable;
11
12 // 試合開始前のカウントダウン(Observable)
13 public IObservable<int> GameStartCountDownObservable => gameStartCountDownObservable.AsObservable();
14
15 // 試合中のカウントダウン(Observable)
16 public IObservable<int> InPlayCountDownObservable => inPlayCountDownObservable.AsObservable();
17
18 // カウントダウン秒数
19 public int CountDownTime { get; } = 2;
20
21 // プレイ秒数
22 public int PlayTime { get; } = 60;
23
24 private void Awake()
25 {
26 // 試合前のカウントダウンタイマ
27 // CountDownTime秒タイマのストリームをPublishでHot変換(まだConnectはしない)
28 gameStartCountDownObservable = CreateCountDownObservable(CountDownTime).Publish();
29
30 // 試合中のタイマ
31 // PlayTime秒タイマのストリームをPublishでHot変換(まだConnectはしない)
32 inPlayCountDownObservable = CreateCountDownObservable(PlayTime).Publish();
33
34 // 3秒タイマのOnCompleteで60秒タイマをConnectする(60秒タイマの起動)
35 // おそらく第二引数がコンプリート時のコールバック処理
36 gameStartCountDownObservable.Subscribe(_ => { ; }, () => inPlayCountDownObservable.Connect());
37 }
38
39 // CountTimeだけカウントダウンするストリーム
40 private IObservable<int> CreateCountDownObservable(int countTime)
41 {
42 return Observable.FromCoroutine<int>(observer => CountDownCoroutine(observer, countTime));
43 }
44
45 private float timeScale = 1.0f;
46
47 // 時間経過速度の倍率
48 public float TimeScale
49 {
50 get => timeScale;
51 set => timeScale = Mathf.Max(value, 0.0f);
52 }
53
54 private IEnumerator CountDownCoroutine(IObserver<int> observer, int countTime, float interval = 1.0f)
55 {
56 var phase = interval;
57 while (true)
58 {
59 yield return null;
60
61 // 経過時間を積算していく
62 // このとき、経過時間にタイムスケールを乗じることで
63 // 時間経過の速さをコントロールする
64 phase += Time.deltaTime * timeScale;
65
66 // phaseがintervalを超えるたびに値を発行する
67 if (phase >= interval)
68 {
69 var invocationCount = (int)(phase / interval);
70 phase %= interval;
71 for (var i = 0; i < invocationCount; i++)
72 {
73 if (countTime > 0)
74 {
75 observer.OnNext(countTime);
76 }
77 else
78 {
79 observer.OnCompleted();
80 yield break;
81 }
82
83 countTime--;
84 }
85 }
86 }
87 }
88
89 // カウントダウンを開始
90 public void StartCountDown()
91 {
92 // ゲーム開始前のカウントダウンのタイマ起動
93 gameStartCountDownObservable.Connect();
94 }
95}
上記のように改変したTimerManager
を、別途実験用に用意した下記スクリプトから利用したところ...
C#
1using System.Collections;
2using System.Diagnostics;
3using UniRx;
4using UnityEngine;
5using Debug = UnityEngine.Debug;
6
7public class TimerTest : MonoBehaviour
8{
9 [SerializeField] private TimerManager timerManager;
10
11 private bool isCounting = true;
12
13 private IEnumerator Start()
14 {
15 // スペースキーを押して実験開始
16 Debug.Log("Hit space to start countdown.");
17 var waitUntilSpace = new WaitUntil(() => Input.GetKeyDown(KeyCode.Space));
18 yield return waitUntilSpace;
19
20 // 参考情報として経過時間を測定する
21 var stopwatch = new Stopwatch();
22 stopwatch.Start();
23
24 // Connect
25 timerManager.StartCountDown();
26
27 // カウントダウンの購読
28 timerManager.GameStartCountDownObservable
29 .Subscribe(
30 time =>
31 {
32 Debug.Log($"GameStartCountDown: {time}, Elapsed: {stopwatch.ElapsedMilliseconds * 0.001:F3}");
33 },
34 () =>
35 {
36 // カウントダウンが完了したとき
37 // ゲーム開始
38 Debug.Log($"Start game! Elapsed: {stopwatch.ElapsedMilliseconds * 0.001:F3}");
39 })
40 .AddTo(this);
41 timerManager.InPlayCountDownObservable
42 .Subscribe(
43 time => { Debug.Log($"InPlayCountDown: {time}, Elapsed: {stopwatch.ElapsedMilliseconds * 0.001:F3}"); },
44 () =>
45 {
46 // カウントダウンが完了したとき
47 // ゲーム終了
48 Debug.Log($"Time is up! Elapsed: {stopwatch.ElapsedMilliseconds * 0.001:F3}");
49 stopwatch.Stop();
50 })
51 .AddTo(this);
52
53 // スペースキーを押したらタイムスケールを1.0または0.0に切り替えることで
54 // カウントを一時停止、または再開する
55 // なお、停止中は参考用のストップウォッチも一時停止することにした
56 while (true)
57 {
58 yield return null;
59 yield return waitUntilSpace;
60
61 isCounting = !isCounting;
62 if (isCounting)
63 {
64 timerManager.TimeScale = 1.0f;
65 stopwatch.Start();
66 Debug.Log($"Resumed. Elapsed: {stopwatch.ElapsedMilliseconds * 0.001:F3}");
67 }
68 else
69 {
70 timerManager.TimeScale = 0.0f;
71 stopwatch.Stop();
72 Debug.Log($"Paused. Elapsed: {stopwatch.ElapsedMilliseconds * 0.001:F3}");
73 }
74 }
75 }
76}
下図のような挙動になりました。

バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
2022/05/08 14:23 編集