◆はじめに
UnityでC#を使っている大学生です。スクリプトを書いていて躓いたことを備忘録として残します。
同じ疑問をお持ちになった方、是非お役立てください。
今回は制限時間の実装についてです。
ゲーム制作をしていると何かと制限時間をつけたいと思う機会は多いのではないでしょうか。
・Time.deltaTimeを使う方法
・コルーチンを用いる方法
・TimeSpan構造体とDateTime構造体を使う方法
の以上3通りについて触れていきます。
・欲しい部分だけを表示する方法
も後半に載せておきます。
※ただしこの記事はあくまで"必要最低限の知識"と"方法"に重きを置いています。ご注意ください。
◆事前準備
まずはUnity側の下準備としてCreate Emptyより空のオブジェクトを作成します。
ここにAddComponentから新たなC#スクリプトをアタッチします。(名前は何でも大丈夫です。ここではTimerScriptとします。)
カウントダウン表示用にCanvas配下にUI→TextからTextも作成しておきましょう。(ここではtimerTextとします。)
◆Time.deltaTimeを使う方法
これを使うのが一番シンプルです。個人的に気に入っています。
おおまかな流れとしては
【1】制限時間を決める
【2】制限時間からTime.deltaTimeを引く
【3】float型を文字列に変換して表示
【4】カウントが0以下になった時の処理
になります。
予備知識として、Time.deltaTimeは各フレームにかかった時間を表します。
ゲームはフレームの連続でできているので、各フレームにかかる時間を足し合わせると経過時間と等しくなります。
それを逆に利用して、すでに決められた時間(固定の値)から各フレームにかかった時間(増加していく)を引いていき、カウントダウンとして使います。
ちなみに足し合わせればカウントアップとしても使えます。優秀!
// 制限時間をセット
float timer = 10f;
// Update()の中などに何度も呼ばれるメソッドの中に記述し、Time.deltaTimeを引きつづける
timer -= Time.deltaTime;
(参考)
// 初期化
float timer = 0f;
// カウントアップ
timer += Time.deltaTime;
☆☆☆
では書き方がわかったところで使用例を見てみましょう。
この例は10秒から減っていくタイプの制限時間(タイマー)になります。
using System;
using UnityEngine.UI;
public class TimerScript : MonoBehaviour
{
public float limiTime = 10f;
public Text timerText;
void Update(){
// 制限時間との差を取得しつづける
limitTime -= Time.deltaTime;
// 文字列に変換して表示
timerText.text = limitTime.ToString();
// もし制限時間が0になったら
if(limitTime==0){
// Finishと表示
timerText.text = "Finish";
}
}
}
【1】制限時間を決める
決められた秒数から経過時間を引いていく仕組みなので、まずは制限時間を決めましょう。
Unity側からでも操作可能なようにpublicにしています。
【2】制限時間からTime.deltaTimeを引く
制限時間が決まったら、さっそくTime.deltaTimeを引いていきます。
引き続けてもらう必要があるのでUpdate関数などの中に記述するとよいでしょう。
【3】float型を文字列に変換して表示
カウントダウンの仕組み自体はできたので、今度はそれを表示していきます。
timerTextのtextに制限時間を表示したいのですが、いかんせんlimitTimeはfloat型で文字列ではないのでそのままでは表示できません。解決策としてToString()を使ってあげると簡単に文字列に変換できます。
【4】カウントが0以下になった時の処理
完成しているようにみえて致命的な欠陥が残っています。今のままだとカウントダウンはマイナスの値もとってしまいます。
そこで、if文を使ってカウントが0以下になったときは常にFisishと表示されるように指定します。
float型なので==0で条件式が書けます。地味に助かるところです。
(timerText.text="0"とかけば0が表示されるようになるので柔軟に変えてください。数字はダブルクオーテーションで囲って文字列扱いにすると簡潔に書けます。)
これで表示がマイナスの値になることはなくなりました。
◆コルーチンを用いる方法
これは感覚的に分かりやすい印象があります。気のせいかもしれません。
おおまかな流れとしては
【1】制限時間を決める
【2】コルーチンを利用して1秒に1回カウントを引く
【3】カウントが0以下になった時の処理
になります。
コルーチン
コルーチンとは、実行を停止して Unity へ制御を戻し、その次のフレームで停止したところから続行することができる関数です。
予備知識として、Unityにはコルーチンという機能が存在します。
処理を中断し、任意のタイミングで中断したところから処理を再開してくれるありがたい機能です。
void Start(){
StartCoroutine("コルーチン関数名");
}
//処理1と処理2の間で任意の秒数待つ
IEnmerator コルーチン関数名(){
// 処理1
yield return new WaitforSeconds(止めたい秒数);
// 処理2
}
コルーチン関数の前にはIEnmeratorをつけます。
yield return new WaitforSeconds()も関数内に必ず記述します。
呼び出すときはStartCoroutineを使います。
※これらは同じスクリプト内に書きましょう。ややこしくなります...。
☆☆☆
コルーチンについて軽く触れたところで本題に戻ります。
実際に使用しているケースを眺めてみましょう。10秒からカウントが減っていくタイプの制限時間(タイマー)です。
using UnityEngine.
using UnityEngine.UI;
public class GameManager : MonoBehaviour
{
// 制限時間
public int TIME_LIMIT = 10;
void Start()
{
// コルーチンを呼ぶ
StartCoroutine("Timer");
}
IEnumerator Timer()
{
// 変数へ初期値を代入
int count = TIME_LIMIT;
// カウントが0より大きい時ループ
while (count > 0)
{
// テキストの更新
timerText.text = count.ToString();
// 1秒待機
yield return new WaitForSeconds(1.0f);
// カウントを1減らす
count--;
}
// テキストの表示
timerText.text = "Finish";
}
}
【1】制限時間を決める
決められた秒数から1ずつ引いていく仕組みなので、まずは制限時間を決めましょう。
Unity側からでも操作可能なようにpublicにしています。
【2】コルーチンを利用して1秒に1回カウントを引く
while文の中で1秒ごとに1ずつカウントが減るようにします。まさにカウントダウンそのものです。
Start関数が呼ばれたとき(ゲーム再生開始時点)から実行が始まります。
【3】カウントが0以下になった時の処理
カウントが0になるとwhile文の条件式を満たさなくなるのでループを抜けます。
特に何も書いていない場合テキストは1のまま停止してしまいます。ループを抜けてしまったためテキストが更新されないからです。
よってFinishと表示されるようにします。(先ほどと同様timerText.text="0"でもかまいません)
◆TimeSpan構造体とDateTime構造体を使う方法
おおまかな流れとしては
【1】DateTime型同士を引き算して得られた時間差をTimeSpan型で取得
【2】TimeSpan型を文字列に変換してUnityのTextに表示
【3】カウントが0以下になった時の処理
になります。
注意する点は
DateTime型同士を引き算しても得られる値はDateTime型ではない!
ということです。私はきれいに引っ掛かりました。
// これはダメ
DateTime time = DateTime.Now.AddSeconds(3) - DateTime.Now;
// これはOK
// DateTime.Now.AddSeconds(3)で現在時刻より3秒後 , DateTime.Nowで現在時刻を得られます
TimeSpan timeSpan = DateTime.Now.AddSeconds(3) - DateTime.Now;
TimeSpan 構造体
時間間隔を表します。
DateTime 構造体
通常、日付や時刻として表現される瞬間を表します。
DateTime.Now
コンピューター上の現在の日時を現地時刻で表した DateTime オブジェクトを取得します。
DateTime.AddSeconds(double)
指定された秒数を加算した新しい DateTime を返します。
☆☆☆
では書き方が分かったところで、実際に使用しているケースを眺めてみましょう。
この例は3秒から減っていくタイプの制限時間(タイマー)になります。
using System;
using UnityEngine.UI;
public class TimerScript : MonoBehaviour
{
private DateTime limiTime;
TimeSpan timeSpan;
public Text timerText;
void Start(){
// 現在時刻から3秒後をセット
limitTime = DateTime.Now.AddSeconds(3);
}
void Update(){
// 現在時刻との差を取得しつづける
timeSpan = LimitTime - DateTime.Now;
// 文字列に変換して表示
timerText.text = timeSpan.ToString();
// もし現在時刻がStart時点でセットした3秒後の時刻を過ぎたら
if(limitTime<=DateTime.Now){
// Finishと表示
timerText.text = "Finish";
}
}
}
【1】DateTime型同士を引き算して得られた時間差をTimeSpan型で取得
TimerScriptを開き、Start()の中で現在時刻から3秒後をセットします。
(5秒後、10秒後などに変えると容易に制限時間を変更することができます。Startはゲーム再生開始時にただ1回しか処理が行われないので、limitTimeはStartが実行されたときの時刻の3秒後に固定されます。)
そしてUpdate()で、limitTimeから常に現在時刻を引き続けます。
現在時刻は刻一刻と変化する(増えていく)のに対してlimitTimeは固定されているので、引くことで秒単位で差を得ることができ、実質カウントダウンをしていることになります。
先に述べた通り、時刻同士の引き算で得られた値はTimeSpan型なので、timeSpanでこの差を受け取ってあげます。
【2】TimeSpan型を文字列に変換してUnityのTextに表示
カウントダウンの仕組み自体はできたので、今度はそれを表示していきます。
timerTextのtextに制限時間を表示したいのですが、いかんせんtimeSpanは文字列ではないのでそのままでは表示できません。解決策としてToString()を使ってあげると簡単に文字列に変換できます。
【3】カウントが0以下になった時の処理
一見完成しているように思えますが、今のままだとタイマーはマイナスの値もとってしまいます。
そこでif文を使って3秒後を過ぎたらずっとFinishと表示するように制御します。
(Finishでなくてもかまいません。timerText.text="0"とすれば0で固定することも可能です。)
これで表示がマイナスになることはなくなりました。
◆欲しい部分だけを表示する方法
以上3通りの方法でカウントダウンの仕組みはだいたい完成しました。
ただここで少し問題があり、何も表示について指定しないとhh : mm : ss(hh時間mm分ss秒)がすべて表示されてしまったり意図した表示になっていないことがあります。
数秒間のカウントダウンがしたいと思った時、この使っていない部分省きたいですよね。
そんなときには書式指定子というものが存在します。
// デフォルト(例:00.00.04のような表示)
timerText.text = timeSpan.ToString();
// s秒(例:4のような表示)
timerText.text = timeSpan.ToString("s");
// ss秒mmミリ秒(例:04.37のような表示)
timerText.text = timeSpan.ToString("ss':'ff");
// ss秒mmmミリ秒(例:04.375のような表示)
timerText.text = timeSpan.ToString("ss':'fff");
// DateTimeだと「:」など文字でないものをシングルクォーテーションで囲まなくてよい
// ss秒mmミリ秒(例:04.37のような表示)
timerText.text = dateTime.ToString("ss:ff");
"s"なら1桁の時に10の位を0埋めしません、"ss"だと0埋めされます。
急に出てきたfはミリ秒、小数点以下の秒数を表示したいときに用います。
"f"なら小数点以下1桁、"ff"なら2桁、"fff"なら3桁になります。個数に比例していきます。
(他の書式指定子についてはMicrosoft公式ドキュメント)
[https://docs.microsoft.com/ja-jp/dotnet/standard/base-types/custom-date-and-time-format-strings]
単独でなくても"ss':'f"など秒とミリ秒を同時に表示することもできます。
ただしここで1つ注意があります。
DateTimeなどは、("ss:ff")と一息に書いてよい
TimeSpanの時は、("ss':'ff")と、文字以外のものはシングルクォーテーションで囲む必要がある
これで完璧に制限時間を操れるようになりました!!
◆おわりに
三種類まとめてみました。私自身最初に躓いたのが制限時間でした。
各々使いやすいものを使って実装してください。
長くなってしまいましたがお読みいただきありがとうございました。
参考記事
[https://docs.unity3d.com/ja/2019.4/ScriptReference/index.html]
↧