Developer

【初心者Unity】Time.deltaTimeとTime.timeの使い分け
2021.09.30
Lv1

【初心者Unity】Time.deltaTimeとTime.timeの使い分け

1.はじめに

以前の記事で、Time.deltaTimeとTime.timeの基本的な使い方について解説しました。

復習になりますが、各変数の役割は以下のようになっています。
Time.deltaTime:前回のフレームからの経過時間(秒)を表す
Time.time:ゲーム開始時から現在までの経過時間(秒)を表す

原義は上記の通りなのですが、それぞれ工夫して用いることで、
Time.deltaTimeを使用してゲーム開始時から現在までの経過時間を取得したり、
Time.timeを使用して前回のフレームからの経過時間を取得することもできます。

では、この2つの変数はどのように使い分ければ良いのでしょうか。
少し検証してみましょう。

※本記事の内容はTime.deltaTimeとTime.timeの基本的な機能を理解していることを前提にしています。
 理解に不安のある方は上記の2記事を先に参照して下さい。

2.Time.deltaTimeを使用した方が良いケース

一時停止をしたい場合
Time.deltaTimeの強みは、カウントするか否かをこちら側で決められる点にあります。
タイマーを一時停止する例を考えてみましょう。
Time.deltaTimeを使用する場合、一時停止中はタイマーに加算しないことで実装できます。

スクリプトで表すと以下のようになります。

// Time.deltaTimeの場合
void Update()
{
    if (一時停止中でない)
    {
        timer += Time.deltaTime; // 経過時間を計算
    }
}

一方、同じことをTime.timeで実現しようとすると少し面倒です。
一時停止中の時間を計算し、合計時間から引くしかないでしょう。

// Time.timeの場合
if (一時停止が開始)
{
    pauseStart = Time.time; // 開始時間を格納
}
if (一時停止が終了)
{
    pauseFinish = Time.time; // 終了時間を格納
}
pauseTime = pauseFinish - pauseStart; // 停止時間を計算
timer = Time.time - pauseTime; // 経過時間を計算

一時停止開始時の経過時間を保存しておいて、一時停止終了時にTime.timeに上書きすれば良いようにも思えますが、それはできません。

if (一時停止が開始)
{
    pauseStart = Time.time;
}
if (一時停止が終了)
{
    Time.time = pauseStart; // ここでコンパイルエラー!!
}

Time.timeは読み取り専用の変数であり、値を代入することはできません。
これはTime.deltaTimeも同様です。

その他、Time.timeScaleという変数の値を0にするという方法も考えられます。
Time.timeScaleは時間の流れる速さを表す変数です。
初期値は1(現実世界の時間と同程度の速さ)ですが、この値を大きくすることで早送り、小さくすることでスローモーションといった動作が実現できます。
その応用的な使い方として、値を0に設定することで時間を止める、というものがあります。

// 左クリック時、Time.timeScaleの値を0⇔1で切り替え
if (Input.GetMouseButtonDown(0))
{
    Time.timeScale = 1 - Time.timeScale;
}

上手く止まりましたが、Time.deltaTimeも一緒に止まっていますね。

また、Time.timeScaleの値はFixedUpdate関数内の処理にも影響を与えます。
以下の例では、SquareをFixedUpdate関数内での処理で動かしています。

この通り、Time.timeScaleを0にすることでTime.timeのカウントを止めることはできますが、他のオブジェクトへも影響が及んでしまいます。
勿論、Time.timeScaleの影響を受けないようにすることはできるのですが、個別の対応が必要であり、考慮漏れがないか確認するのも一苦労です。
以上のことから、単にタイマーを一時停止したいだけであれば、Time.deltaTimeを使用した方が良いといえるのではないでしょうか。
※ただし、タイマー以外のオブジェクトも一時停止したい!というような場合はTime.timeScaleを使用した方がすっきりと記述できます。

時間単位で処理をしたい場合
フレームレートに依らず、処理を時間単位で行いたい場合にはTime.deltaTimeを使用する方が良いでしょう。
詳しい説明は【初心者Unity】Time.deltaTimeの基本的な使い方 に譲り、ここでは割愛させていただきます。
敢えてTime.timeで実装しようとすると、とても面倒臭くなる気がします…。

3.Time.timeを使用した方が良いケース

(長い)時間を計測したい場合
Time.timeの強みは、繰り返し処理を行わずとも経過時間が取得できるという点にあります。
スクリプトの記述を比較してみましょう。
Time.deltaTimeを使用した場合、スクリプトは以下のようになります。

// Time.deltaTimeの場合
void Update()
{
    if (計測中)
    {
        timer += Time.deltaTime; // 経過時間を計算
    }
}

計測中、演算を繰り返すことになります。

一方、Time.timeを使用した場合は以下の通りです。

// Time.timeの場合
void Update()
{
    if (計測開始)
    {
        start = Time.time; // 開始時間を格納
    }
    if(計測終了)
    {
        timer = Time.time - start; // 経過時間を計算
    }
}

演算は計測終了時に一度減算を行っているのみです。

この少ない演算回数は、以下のメリットを持ちます。
1.処理が軽い
当然ですが計算回数が少ない方が処理は軽いです。
とは言え、Update関数内での加算が大きく処理速度に影響する場面はあまり多くないかも知れません。

2.誤差が少ない
コンピュータは小数を扱うことを苦手としています。
そのため小数をオペランドとした演算の結果、わずかに誤差が生じることがあります。
Time.deltaTimeを使用した場合、演算を繰り返す中でその誤差が蓄積していき、やがて大きな誤差になってしまいます。
Time.timeの場合は演算回数が少ないため、誤差の影響を最小限に抑えることができます。

とは言え、正の値の加算ではそれほど深刻な影響が出ることは稀です。
問題になるのは、経過時間が長くなった場合です。
情報落ちといって、絶対値の差が大きく異なる数字同士を演算したときに小さい値の情報が無視されてしまう現象があります。
Time.deltaTimeの値は非常に小さいため、長大な経過時間に加算しても反映されず、タイマーが進まなくなってしまいます。
そのため、長い時間を計測する場合にはTime.timeを使用する方が良いでしょう。

4.まとめ

以上の内容をまとめましょう。

  • 長い時間を計測したい! → Time.time
  • 短い時間を計測したい! → どちらでもOK(Time.deltaTimeの方が記述がシンプルになるかもしれません)
  • 一時停止をしたい! → Time.deltaTime
  • ゲーム開始時からの経過時間を取得したい! → Time.time
  • 時間単位で処理をしたい! → Time.deltaTime

 

連載目次リンク

「初心者のための」Unityゲーム制作 目次
 

Unity実践編 - 目次リンク

実践Unityゲームプログラミング 連載目次