timeScaleで変わるのは、どの時間か
ポーズでTime.timeScaleを0にした後、UIのフェードや待機処理まで止まる場合は、使っている時間の基準を確認します。アプリのフレーム更新が続く通常のポーズではUpdateは呼ばれ続けますが、Time.deltaTimeを足して進める処理は進まなくなります。この記事はUnity 2022.3 LTSを基準にしています。
1. 止めたい処理と、動かしたい処理を決める
- ゲーム進行:キャラクターの移動やゲーム内の制限時間などは、ポーズで止める設計ならスケール時間を使います。
- ポーズUI:メニューのフェードや操作説明のアニメーションなどは、ポーズ中も動かす設計なら非スケール時間を使います。
- 操作入力:Updateが続くため、入力やフレーム単位の処理まで自動的に止まるとは限りません。ゲーム操作の可否はポーズ状態で管理します。
timeScaleの変更は後続フレームへ反映されます。設定した直後の1回の値だけで、動作を判断しないようにします。
2. UIの経過時間にはunscaledDeltaTimeを使う
Update内でフェード量などを更新している場合は、進行量をTime.deltaTimeで計算しているか確認します。ポーズ中も進めたい処理だけ、Time.unscaledDeltaTimeを使う設計にします。図の0秒・1秒・2秒はポーズ後の経過時間の例で、各フレームのunscaledDeltaTimeが1秒になるという意味ではありません。
ゲーム全体を一律に非スケール時間へ変えると、ポーズ中もゲームが進む原因になります。UIなど用途を限定してください。またFixedUpdateはtimeScaleが0の間は呼ばれないため、その中でunscaledDeltaTimeを参照するだけではポーズ中の処理を実行できません。
3. Coroutineの待ち時間を選ぶ
WaitForSecondsはスケール時間を使うため、timeScaleが0の間は待ち時間が進みません。ポーズ中にも完了させたいUIの待機には、Coroutine内でWaitForSecondsRealtimeを使う方法があります。例えばyield return new WaitForSecondsRealtime(2f);は非スケール時間で2秒を待つ指定です。
これは別スレッドでUI処理を動かす指定ではありません。再開はフレームの処理に従い、指定した秒数とまったく同じ瞬間の実行を保証する用途にはしません。待っているGameObjectをSetActive(false)にすると、そのCoroutine自体が停止するため、時間の基準とオブジェクトの寿命を分けて確認します。
4. Animatorや独自の停止処理を確認する
UIのAnimatorをポーズ中も進めたい場合はUpdate ModeのUnscaled Time、コードならAnimatorUpdateMode.UnscaledTimeを確認します。Animatorのspeedを0にする処理や、Animator・GameObjectの無効化があれば、それらも別に調べます。
TweenやTimelineなどは、それぞれが持つ時間設定を確認します。timeScaleを変更しただけで音声や入力を含むすべての機能が止まる設計にはせず、停止・継続の対象を明示します。
5. 再開時は、保存した状態を戻す
ポーズ前がスローモーションの0.5だった場合に、再開で常に1へ設定すると元の速度を失います。時間の変更を管理する所有者で元の値を保持し、ポーズ要求の重なりを整理して復元します。Fixed Timestepなども変更する実装なら、それらの値も管理対象にします。
調査では、有効なUIオブジェクトで、スケール時間のタイマーと非スケール時間のタイマーを並べて比較します。ポーズ開始、継続、解除を繰り返し、想定した処理だけが進むか確認してください。
修正後の確認
- ポーズUIが操作でき、ゲーム側の入力は設計どおり止まるか確認する。
- フェード、Animator、待機中のCoroutineをそれぞれ確認する。
- メニューを閉じる・GameObjectを無効にする場合の中断処理を確認する。
- スローモーション中のポーズや複数のポーズ要求から正しく復帰するか、対象Playerで確認する。