今回はUnityでゲームのプレイ時間を表示する機能、制限時間を設けて持ち時間が減っていく機能を作成します。
時間経過で特定のイベントを発生させたり、レースゲーム等では必須の機能ですね。
時間表示用のUIを作成する
まずは、時間表示をするUI部分を作成していきます。
上のように弾数等、装備スロットを表示しているStatusUIに新しく、Timerという名前のPanelを作成します。
Timerパネルは上のような位置に設定します。
Anchor Presetsは横はStretchで縦がCustumになります。
Timerの子要素にTextを作成し、設定を上のようにします。
これでUI部分は完成しました。
経過時間の計算と表示をするスクリプトTimerScriptを作成する
Add ComponentでNew Scriptを選び新しいスクリプトTimerScriptをTimerの子要素のTextに設定します。作成します。
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 | using UnityEngine; using System.Collections; using UnityEngine.UI; public class TimerScript : MonoBehaviour { [SerializeField] private int minute; [SerializeField] private float seconds; // 前のUpdateの時の秒数 private float oldSeconds; // タイマー表示用テキスト private Text timerText; void Start () { minute = 0; seconds = 0f; oldSeconds = 0f; timerText = GetComponentInChildren<Text> (); } void Update () { seconds += Time.deltaTime; if(seconds >= 60f) { minute++; seconds = seconds - 60; } // 値が変わった時だけテキストUIを更新 if((int)seconds != (int)oldSeconds) { timerText.text = minute.ToString("00") + ":" + ((int) seconds).ToString ("00"); } oldSeconds = seconds; } } |
minuteは分、secondsは秒を表します。
timerTextフィールドは計算した結果を表示するTextになります。
Startメソッド内でminute、secondsを0で初期化します。
secondsにTime.deltaTimeの値を足していきます。
Time.deltaTimeは最後のフレームを完了するのに要した時間なので、Updateメソッドが呼ばれる度にsecondsに値を足していけば時間を計測出来ます。
60秒を超えたらminuteに1を足し、seconds = seconds – 60で秒数から60を引いて秒数に再度代入します。
細かい秒数を捨てないようにします。
oldSecondsは、同じ秒数だった時にTextを更新しないようにする為に用意しています。
secondsとoldSecondsはfloatなのでintにキャストして比較をしています。
テキストのアップデートを常に行っていると処理速度に影響を与える為、最小限の更新に留めておきます。
分と秒を表示する箇所は
1 2 3 | timerText.text = minute.ToString("00") + ":" + ((int) seconds).ToString ("00"); |
のようにしてますが、
minite.ToString(“00”)としている部分は分の値が一桁の場合に1と表示するのではなく01と表示する為の処理です。
1 2 3 | ((int) seconds).ToString ("00") |
の部分ではsecondsをint値にキャストし、その上でToString(“00”)で二桁で秒数を表示します。
それではUnityの実行ボタンを押して確認してみましょう。
上が実行結果です。
1分に切り替わる部分を切り取ってみました。
ゲームの中断をした時はしっかりと時間の経過を止めて、再開した時は時間経過が進むようにもなっています。
時間をカウントダウン形式にして制限時間を計りたい場合
経過時間を計測するのではなく、制限時間を設定して時間を減らしていき残り時間が0秒になった時になんらかの処理をしたい事もあります。
そんな時は、Time.deltaTimeを足していた個所を制限時間から引いていけばいい事になります。
数値を減らしていく場合は、分と秒を一旦足して、全体の秒数を求めてからTime.deltaTimeを引き、その後、再度分と秒を計算するというやり方にしてみます。
制限時間が来たら、コンソールに『制限時間終了』という文字を表示するCountDownTimerスクリプトを作成します。
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 | using UnityEngine; using System.Collections; using UnityEngine.UI; public class CountDownTimer : MonoBehaviour { // トータル制限時間 private float totalTime; // 制限時間(分) [SerializeField] private int minute; // 制限時間(秒) [SerializeField] private float seconds; // 前回Update時の秒数 private float oldSeconds; private Text timerText; void Start () { totalTime = minute * 60 + seconds; oldSeconds = 0f; timerText = GetComponentInChildren<Text>(); } void Update () { // 制限時間が0秒以下なら何もしない if (totalTime <= 0f) { return; } // 一旦トータルの制限時間を計測; totalTime = minute * 60 + seconds; totalTime -= Time.deltaTime; // 再設定 minute = (int) totalTime / 60; seconds = totalTime - minute * 60; // タイマー表示用UIテキストに時間を表示する if((int)seconds != (int)oldSeconds) { timerText.text = minute.ToString("00") + ":" + ((int) seconds).ToString("00"); } oldSeconds = seconds; // 制限時間以下になったらコンソールに『制限時間終了』という文字列を表示する if(totalTime <= 0f) { Debug.Log("制限時間終了"); } } } |
制限時間を計算する時も経過時間とそんなに変わる事はないと思います。
制限時間がなくなったら、今回の場合はコンソールに『制限時間終了』という文字を表示していますが、
通常ならばゲーム全体を管理するスクリプト等に、ゲームオーバー処理をさせる為に作成したGameOver関数を呼び出すとか、
他のスクリプトに設定されている何らかの処理を呼び出す、というような感じで使用する事になると思います。
Time.timeを使った時間計測
Updateメソッド内でTime.deltaTimeを足していく事で時間計測する事が出来ましたが、Unity起動からの実行時間Time.timeを使っても時間計測をする事が出来ます。
Timerに新しいスクリプトTimerTimeを作成し取りつけます。
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 | using UnityEngine; using System.Collections; using UnityEngine.UI; public class TimerTime : MonoBehaviour { private Text timerText; private int minute; private float seconds; private float oldSeconds; // 最初の時間 private float startTime; void Start () { timerText = GetComponentInChildren <Text> (); oldSeconds = 0; startTime = Time.time; } void Update () { // Time.timeでの時間計測 seconds = Time.time - startTime; minute = (int)seconds / 60; if((int)seconds != (int)oldSeconds) { timerText.text = minute.ToString("00") + ":" + ((int)(seconds % 60)).ToString("00"); } oldSeconds = seconds; // マウスの左ボタン押しで一時停止 if(Input.GetMouseButtonDown(0)) { Time.timeScale = Mathf.Approximately(Time.timeScale, 0f) ? 1f : 0f; } } } |
Time.timeはUnity起動後の時間なのでStartメソッドでスタート時のTime.timeの値をセットします。
経過時間はTime.timeからstartTimeを引くと得られますので、それを使って分と秒を計算します。
秒から分を計算するには経過秒数を60で割った商になります。
secondsの値を書き換えてしまうと難しくなるので、時間を表示する時にseconds % 60を使って秒数を表示するようにして、分を考慮した秒数を表示する事が出来ます。
それでは実行して試してみましょう。
↑のようにTime.deltaTime、Time.timeで計算した時間表示が同じになっていますね。
7秒の時にマウスの左クリックをして一時停止した後、再度マウスの左クリックで時間計測を再開しています。
Time.deltaTime、Time.timeどちらでも時間の計測が出来るので、お好みで選択するといいと思います。
DateTime構造体とTimeSpan構造体を使った時間計測
2020/06/04に追記した内容です。
DateTime構造体とTimeSpan構造体を使用して経過時間を計算する場合はTimeSpan構造体のプロパティ値を使えるので独自の分や秒の計算は要りません。
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 | using System; using System.Collections; using System.Collections.Generic; using UnityEngine; using UnityEngine.UI; public class TimerScript2 : MonoBehaviour { private Text timerText; // スタート時のDateTime構造体 private DateTime startDataTime; // 時間経過のTimeSpan構造体 private TimeSpan totalTime; // Start is called before the first frame update void Start() { timerText = GetComponentInChildren<Text>(); // 現在のDateTime構造体を設定 startDataTime = DateTime.Now; } // Update is called once per frame void Update() { // 現在のDateTimeから初期のDetaTimeを計算し経過した時間を計算する totalTime = DateTime.Now - startDataTime; // TimeSpan構造体のプロパティで分と秒をテキストに入れる timerText.text = totalTime.Minutes.ToString("00") + ":" + totalTime.Seconds.ToString("00"); } } |
StartメソッドでDateTime.NowでDateTimeのNowプロパティで現在の日時データであるDateTimeを取得し、startDateTimeに入れます。
Updateメソッドで現在の日時データ(DateTime.Now)からスタート時の日時データ(startDateTime)を引いて時間間隔のTimeSpan構造体を計算します。
TimeSpan構造体のMinutesやSecondsプロパティで分と秒を取得し、それをToString(“00”)を使って必ず二けたになるように整形しテキストに出力します。
Time.timeScaleを0にしても時間を進める
Time.timeScaleでゲームを止めている状態でも時間計測を続けたい事もあります。
Time.deltaTimeはTime.timeScaleが0だと取得出来ないので、Time.realtimeSinceStartupを使ってゲーム開始からの時間を取得しタイマーに使います。
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 | using UnityEngine; using System.Collections; using UnityEngine.UI; public class RealTimeTimer : MonoBehaviour { private Text timerText; private int minute; private float seconds; private float oldSeconds; // 最初の時間 private float startTime; void Start () { timerText = GetComponentInChildren <Text> (); oldSeconds = 0; startTime = Time.realtimeSinceStartup; } void Update () { // Time.timeでの時間計測 seconds = Time.realtimeSinceStartup - startTime; // 止まっているかどうかをコンソールに表示 Debug.Log("Time.timeScale: " + Time.timeScale); minute = (int)seconds / 60; if ((int)seconds != (int)oldSeconds) { timerText.text = minute.ToString ("00") + ":" + ((int)(seconds % 60)).ToString ("00"); } oldSeconds = seconds; // マウスの左ボタン押しで一時停止 if (Input.GetMouseButtonDown (0)) { Time.timeScale = Mathf.Approximately(Time.timeScale, 0f) ? 1f : 0f; } } } |
Time.timeを使ったサンプルの処理をそのままTime.realtimeSinceStartupに変えただけです。
Time.timeScaleを0にしても時間計測がされているのがわかると思います。