Time.deltaTimeとは?

Time.deltaTimeは、前回のフレームから今回のフレームまでに経過した時間を秒単位で表す値です。

なぜこの値が重要かというと、ゲームの処理はフレーム単位で実行されるため、各フレームの間隔は一定とは限らないからです。

例えば60FPSで動作している場合、1フレームあたりのTime.deltaTimeはおよそ0.0167秒(1/60秒)になります。

一方30FPSに下がれば、1フレームあたりおよそ0.0333秒(1/30秒)になります。

Time.deltaTimeは実行環境やフレームレートによって変動するので、これを掛け合わせればフレームレートに左右されない処理になります。

フレームレート非依存な移動・回転

Time.deltaTimeを掛けることで速度を安定させる

移動や回転の処理にTime.deltaTimeを掛けることで、フレームレートが変動しても一定の速度を保てます。

次の例は、毎フレーム5 * Time.deltaTimeだけオブジェクトを前方に移動させるコードです。

using UnityEngine;

public class MoveExample : MonoBehaviour
{
    // 1秒あたりの移動速度
    [SerializeField] private float _speed = 5f;

    private void Update()
    {
        // フレームの経過時間を掛けることで、1秒あたり_speedだけ進む
        transform.Translate(Vector3.forward * _speed * Time.deltaTime);
    }
}

_speedTime.deltaTimeを掛けているため、このオブジェクトは1秒間に必ず5ユニット進みます。

60FPSなら1フレームで約0.083ユニット、30FPSなら1フレームで約0.166ユニット移動し、フレームレートが変わっても1秒後の到達距離は同じになります。

回転も同様で、Time.deltaTimeを掛けておけば1秒あたりの回転角度を一定に保てます。

using UnityEngine;

public class RotateExample : MonoBehaviour
{
    // 1秒あたりの回転角度(度)
    [SerializeField] private float _degreesPerSecond = 90f;

    private void Update()
    {
        // Y軸まわりに1秒あたり90度回転させる
        transform.Rotate(Vector3.up, _degreesPerSecond * Time.deltaTime);
    }
}

このようにTime.deltaTimeを掛けることで、どの環境でも同じ速度・同じ回転で動かせるようになります。

Time.deltaTimeを掛け忘れるとどうなるか

フレームレート依存になり速度がバラつく

Time.deltaTimeを掛け忘れると、オブジェクトの速度がフレームレートに依存してしまいます。

次のコードはTime.deltaTimeを掛けていない悪い例です。

using UnityEngine;

public class BadMoveExample : MonoBehaviour
{
    [SerializeField] private float _speed = 5f;

    private void Update()
    {
        // 経過時間を考慮していないため、フレームレート依存になってしまう
        transform.Translate(Vector3.forward * _speed);
    }
}

このコードは「1フレームごとに5ユニット進む」という意味になります。

そのため60FPSの端末では1秒間に300ユニット、120FPSの端末では1秒間に600ユニット進んでしまい、高フレームレートの端末ほど移動が速くなります。

処理落ちでフレームレートが下がれば、今度は逆に遅くなります。

テスト環境では問題なく見えても、ユーザーの端末によって挙動が変わってしまうため、移動・回転・カウントアップなど時間に関わる処理には必ずTime.deltaTimeを掛けましょう。

Time.deltaTimeとTime.fixedDeltaTimeの違い

fixedDeltaTimeは固定の時間ステップ

Time.deltaTimeがフレーム間隔という可変の値であるのに対し、Time.fixedDeltaTimeは物理演算が実行される固定の時間ステップを表します。

Unityの物理演算は、描画フレームとは独立した一定の間隔で更新されます。

この間隔の初期値は0.02秒(1秒あたり50回)で、Edit > Project Settings > TimeFixed Timestepから変更できます。

描画フレームは端末性能によって変動しますが、fixedDeltaTimeは設定値で固定されているため、物理挙動を安定させられます。

つまりTime.deltaTimeは「描画の経過時間」、Time.fixedDeltaTimeは「物理更新の固定間隔」と整理できます。

UpdateとFixedUpdateでの使い分け

Updateでは deltaTime、FixedUpdateでは fixedDeltaTime

基本ルールはシンプルで、UpdateではTime.deltaTimeを、FixedUpdateではTime.fixedDeltaTimeを使います。

Updateは描画フレームごとに呼ばれるメソッドで、入力の取得や見た目の更新など、フレーム単位の処理に向いています。

FixedUpdateは物理演算の更新タイミングで呼ばれるメソッドで、Rigidbodyへの力の加算など物理的な処理に向いています。

次の例は、FixedUpdateRigidbodyを移動させるコードです。

using UnityEngine;

public class PhysicsMoveExample : MonoBehaviour
{
    [SerializeField] private float _speed = 5f;

    private Rigidbody _rigidbody;

    private void Awake()
    {
        _rigidbody = GetComponent<Rigidbody>();
    }

    private void FixedUpdate()
    {
        // 物理更新の固定間隔を掛けて、安定した移動を行う
        Vector3 move = Vector3.forward * _speed * Time.fixedDeltaTime;
        _rigidbody.MovePosition(_rigidbody.position + move);
    }
}

呼び出されるメソッドに対応した経過時間を掛ければ、それぞれの更新サイクルに合った処理になります。

よくある罠:FixedUpdateでTime.deltaTimeを使ってしまう

動くが意図が不明瞭でバグの温床になる

FixedUpdate内でうっかりTime.deltaTimeを使ってしまうことがありますが、これは避けるべきです。

実はUnityの仕様として、FixedUpdate内でTime.deltaTimeを参照すると、自動的にTime.fixedDeltaTimeと同じ値が返されます。

そのため次のコードは見た目上は正しく動作してしまいます。

using UnityEngine;

public class TrapExample : MonoBehaviour
{
    [SerializeField] private float _speed = 5f;

    private Rigidbody _rigidbody;

    private void Awake()
    {
        _rigidbody = GetComponent<Rigidbody>();
    }

    private void FixedUpdate()
    {
        // FixedUpdate内ではTime.deltaTimeはfixedDeltaTimeと同じ値を返すため動いてしまう
        // ただし意図が伝わりにくく、バグの温床になる
        Vector3 move = Vector3.forward * _speed * Time.deltaTime;
        _rigidbody.MovePosition(_rigidbody.position + move);
    }
}

動いてしまうがゆえに問題が見えにくいのが厄介な点です。

FixedUpdateなのにdeltaTimeを使っている」というコードは、読み手に「これはフレーム間隔のつもりなのか、それとも勘違いなのか」という疑問を抱かせます。

さらに、後からこのコードをUpdateへ移動させたり、逆にUpdateの処理をFixedUpdateへ移したりすると、今度は値の意味がずれて挙動が変わるおそれがあります。

値としては同じでも、FixedUpdateではTime.fixedDeltaTimeを明示的に使うことで、コードの意図が明確になり保守しやすくなります。

まとめ

今回はTime.deltaTimeTime.fixedDeltaTimeの違い、そして使い分けについて解説しました。

移動や回転など時間に関わる処理ではTime.deltaTimeを掛けることで、フレームレートに左右されない安定した挙動を実現できます。

掛け忘れると高フレームレートの端末で動きが速くなりすぎるなど、環境依存の不具合につながるため注意が必要です。

またUpdateではTime.deltaTimeFixedUpdateではTime.fixedDeltaTimeを使うという基本を押さえておきましょう。

FixedUpdate内のTime.deltaTimefixedDeltaTimeと同じ値を返すため動いてはしまいますが、意図が曖昧になりバグの温床になります。

それぞれの更新サイクルに合った値を明示的に使うことで、読みやすく壊れにくいコードを書けるようになります。