【Unity】「Time.deltaTime」を使わないといけない理由を説明する

どうも、個人ゲーム開発者のゆみねこです。

Unityの情報を調べていると、よく「Time.deltaTime」というナゾの物体を目にすると思います。
これが何なのか、ちゃんと説明できますか?

「よく分からないから、スルーでいいや~」
そう思っていませんか?(偉そう)

実は、Time.deltaTimeを使わないと、思わぬトラブルに直面することがあるんです。

この記事では、Time.deltaTimeが何で、どういう時に使えばいいのかを解説します。
これを理解せずして初心者は卒業できないと言ってもいいです。

Time.deltaTimeの使いみち

先にTime.deltaTimeとは何なのかを説明してもいいんですが、

「そもそもどういう時に必要なの?」
「使わないと何がやばいの?」

を先に知りたいと思うので、そちらを解説します。

使わないとどうなるか

Time.deltaTimeを使わない場合、他のプレイ環境で動作スピードが著しく変化します。

例えば、自分のパソコンで遊べば標準速度なのに、友達のパソコンでは爆速になったり、スマホで遊ぶとめっちゃ遅くなったりします。

主にキャラクターの移動や、カウントダウンや敵の出現頻度などのタイミング系が狂います。

動作環境の違いで取り乱すプレイヤーの図(イメージ)

ゲームバランスが大きく変化してしまうので、スコアアタックを搭載したゲームでは公平性が失われます。

ヤバイ!

使うとどうなるか

上述した問題を全部解決できます。

すなわち、どの環境で遊んでも、均一のゲームスピードを提供します。

プレイヤーの公平性が保たれ、彼女ができ、収入がアップします。
やらない手はないですね。

PCとスマホで均一なゲームスピードを提供し、女の子から感謝される図(イメージ)

使い方

「そうは言っても、使うのが難しかったりするんじゃないの?」
とお悩みの方。

ご心配なく。
Time.deltaTimeの使い方は、めっちゃ簡単。
しかも、Unityに最初からついているので、完全無料です。

プレイヤーの移動の場合

最後に、Time.deltaTimeをかけているだけですね。
たったこれだけで、ゲームスピードの安定性が保たれます。

一定間隔でなにかしたい場合

経過時間を計測する時に、Time.deltaTimeを足していくだけです。
int型の変数で1ずつ足していた方は、今すぐ変えましょう。

スポンサードサーチ

Time.deltaTimeの説明

では、使うメリットや使い方が分かったところで、一体なぜこれでゲームスピードが安定するのかを解説します。

全てのゲーム開発で通用する知識なので、Unity以外のツールに移行した場合でも役に立ちますよ!

概要

最後のフレームを完了するのに要した時間(秒)です。

……意味わかりませんね。

これを詳しく解説するには、先にフレームの概念について説明する必要があります。

フレームとは

「計算+描画」の一回分です。
Unityでいうと、1フレームごとにUpdateが1回呼ばれます。

厳密に言うと、LateUpdateやFixedUpdateも絡んでくるので違うのですが、大体この認識でOKです。

描画部分はUnityが自動でやっているので、ぼくらが意識しているのはUpdateの部分ですね。

これ1つが1フレームと覚えてください。

改めて、Time.deltaTimeとは

前のフレームから、今のフレームまでにかかった時間です。

図解すると、以下のようになります。

なんとなく、理解できましたか?
Time.deltaTimeの値は、処理速度によって毎回異なるのがポイントです。

次のフレームまでの時間が安定しないと、どうなりますか?

そうですね。
高性能なパソコンほど、次のフレームまでの時間が短くなり、逆にスマホだと長くなっていきます。

結果として、

パソコン1秒間に60フレーム
スマホ1秒間に30フレーム

なんてことが起こります。

この問題を解決するのが、Time.deltaTimeです。

なぜTime.deltaTimeで解決するのか?

「Time.deltaTimeって、ただのフレーム間の経過時間でしょ? なんでそれを使うとゲームスピードが一定になるの?」

と思った、将来有望なUnity初心者さんたちのために、仕組みを解説します。

たとえば、とあるパソコンAはTime.deltaTimeが0.1秒で、とあるスマホBだと0.5秒だと仮定します。
(実際はこんなに遅くないですが、あくまで例です)。

先ほどのダメな例のScriptだと、どうなるでしょうか?

//  ダメな例
private int count;
void Update(){
    count++:
    if(count >= 60){
         Debug.Log("60フレームおきに処理");
    }
}

パソコンAは次のフレームまで0.1秒、スマホBだと0.5秒でしたね。
つまり、パソコンAだと0.1×60で6秒おき、スマホBだと30秒おきにDebug.Logが呼ばれます。

これが、敵の出現頻度や、カウントダウン処理だったらどうなりますか?
大 ☆ 問 ☆ 題

じゃあ、いい例の書き方だとどうなるでしょうか。

//  いい例
private float count;
void Update(){
    count += Time.deltaTime;
    
    if(count >= 1.0f){
        count = 0.0f;
        Debug.Log("1秒おきに処理");
    }
}

この場合、Time.deltaTime(次のフレームまでの時間)がいくらであろうが、合計1.0(=1秒)の時にDebug.Logが実行されます。

移動の場合も同様で、

[SerializeField]
private float speed;
 
//  いい例
void Update(){
    var x = Input.GetAxis("Horizontal");
    var z = Input.GetAxis("Vertical");
    
    transform.position += new Vector3(x, 0, z) * speed * Time.deltaTime;
}

こう書くことで、Time.deltaTimeの値が大きい(=次のフレームまでが長い)ほど大きく移動することになるので、パソコンAとスマホBで同一の移動速度になるわけです。

先ほどの例で言うと、パソコンAが0.1秒でスマホBは0.5秒ですので、スマホBはパソコンAの5倍おそいことになります。
このScriptだと、スマホBはパソコンAの5倍の速さで動くことで、5倍おそいフレームを補っているわけですね。

大体の場合、既存の値にTime.deltaTimeをかければ解決します。
ただし、値がとても小さいので (0.01秒台) 、今の計算式に付け足す場合は100倍にするなどして、バランスを調整しましょう。

まとめ

どうでしたか? 疲れましたか?

普段はここまで意識せずとも「Time.deltaTimeをかければ解決する」だけで十分です。
何事も考えすぎると疲れますからね。人生と同じです。

ちなみにぼくは、Unityに限らず、個人で活動するゲーム開発者向けの情報を、Twitterで日々発信しています(質問にも答えています)。

記事の更新を見逃したくないという人は、ぜひぼくのTwitterをフォローしてもらえると嬉しいです!

記事をシェアする