【Unity】Time.deltaTimeの正しい使い方わかってる？適当に掛ければいいてもんじゃない！

# Time.deltaTime、正しく使えていますか？

Unityには`Time.deltaTime`というプロパティが存在します。

このプロパティを計算式に掛けることで、「スペックによらずある処理の速度が一定になる」と思っている人もいるでしょう。
実際にWebを検索すると、「フレームレートが一定でないので、`Time.deltaTime`を掛けないといけない」という説明が多く出てきます。

このプロパティは本当にそういう性質のものなのか。
なぜ掛けることで処理が安定するのか。
その意味をしっかり把握して、必要な場所で正しく使うようにしましょう。

# そもそもTime.deltaTimeの意味とは

`Time.deltaTime`は単に **直前のフレームと今のフレーム間で経過した時間[秒]** を返すプロパティです。
これ自身に処理を安定化させる機能はありません。単純に秒数を返すだけです。

# ではどういう場面でTime.deltaTimeを使えばよいのか

適当に計算式に混ぜ込めば処理がなめらかになるといった理解は**間違い**です。

答えを言ってしまうと、「**右辺に`Time.deltaTime`を掛けた時に、左辺と同じ単位になったら使ってもよい**」です。

##### 「単位」とは

ここでいう単位とは、「物理量」を示す単位のことです。「秒[s]」「距離[m]」「速さ[m/s]」といったものです。
単位には大事な性質があります。

* 異なる単位同士は足せない（ 距離 + 秒、みたいな計算はできない ）
* 異なる単位同士は掛け算できる、その場合は別の単位となる（距離 = 速度✕時間、みたいな）
* 単位を持たない場合は[1]と表記する（無次元）


# Time.deltaTimeを使った例

### 例1: 速度と移動量

「はやさ = きょり / じかん」という式は小学生のころに習ったはずです。
その式を思い出し、それが成り立つように式を組み立てるだけです。

「なぜ速度に`Time.deltaTime`を掛けないといけないのか」の答えは「実際にそのフレームで進んだ"距離"を求める必要があるため」です。


```cs
public class Sample : MonoBehaviour
{
    /// <summary>
    /// 速度[m/s]
    /// </summary>
    private Vector3 _velocity = new Vector3(1, 0, 0);

    void Update()
    {
        // position[m] = position[m] + (velocity[m/s] * time[s])
        // [m] = [m] + [(m/s * s)]
        // [m] = [m] + [m]  ←両辺で単位が同じになった
        transform.position = transform.position + (_velocity * Time.deltaTime);
    }
}
```

### 例2: 速度と速度

続いて、左辺が速度で、この速度を制御したい場合です。この場合、右辺で扱うパラメータが最初から速度であるならば`Time.deltaTime`を掛けてはいけません。
これも単位を計算すればすぐわかります。

```cs
public class Sample : MonoBehaviour
{
    /// <summary>
    /// 速度[m/s]
    /// </summary>
    private Vector3 _velocity = new Vector3(1, 0, 0);

    private Rigidbody _rigidbody;

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

    void Update()
    {
        // [m/s] = [m/s] * [s]
        // [m/s] = [m] ←？？？？？？？？
        _rigidbody.velocity = _velocity * Time.deltaTime; // 間違い

        // この場合は普通に代入するだけでよい
        _rigidbody.velocity = _velocity;   
    }
}
```

### 例3: 移動量と加速度

ちょっと複雑な例ですが、「加速度」を使って「移動量」を計算する場合です。
この場合も、それぞれの単位をしっかり考えれば難しくはありません。

```cs
public class Sample : MonoBehaviour
{
    /// <summary>
    /// 加速度[m/s/s]
    /// </summary>
    [SerializeField]
    private Vector3 _acceleration = new Vector3(1, 0, 0);

    /// <summary>
    /// 現在の速度[m/s]
    /// </summary>
    private Vector3 _currentVelocity = Vector3.zero;

    void Update()
    {
        // 現在の速度を計算する
        // [m/s] = [m/s] + [m/s/s] * [s]
        // [m/s] = [m/s] + [m/s] 
        _currentVelocity = _currentVelocity + _acceleration * Time.deltaTime;
        
        // 速度から移動量をもとめる
        // [m] = [m] + [m/s] * [s]
        // [m] = [m] + [m]
        transform.position = transform.position + _currentVelocity * Time.deltaTime;
    }
}
```

![1.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/47146/82712c75-4cd8-d692-4c92-23e0ec52d9a3.gif)

（それぞれパラメータを変えて動作させた結果）

### 例4: Lerpで使う例

`Time.deltaTime`をよく`Mathf.Lerp`や`Vector3.Lerp`と組み合わせることがあります。

この場合は、「**パラメータの変化速度[1/s]**に対して`Time.deltaTime`を掛けている」思えばよいです。

```cs
public class Sample : MonoBehaviour
{
    private Vector3 _startPosition = Vector3.zero;

    private Vector3 _goalPosition = new Vector3(10, 0, 0);

    /// <summary>
    /// パラメータの変化量[1/s]
    /// 10.0f、の場合は、1.0/10.0 = 0.1[s]で遷移が完了する、という意味になる
    /// </summary>
    private float speed = 10.0f;

    // 現在の変化量
    private float _progress = 0.0f;

    void Update()
    {
        // Vector3.Lerpの第三引数に与えられるパラメータは「全体の変化の割合量[1]」
        // [1] = [1/s] * [s]
        _progress = _progress + speed * Time.deltaTime;
        transform.position = Vector3.Lerp(_startPosition, _goalPosition, _progress);
    }
}
```

# まとめ

`Time.deltaTime`は処理を安定化させる魔法のパラメータではありません。
単に経過時間を返すだけのプロパティです。

そもそも「`Time.deltaTime`を掛けると処理がなめらかになる。なぜならばフレームレートが一定でないから」という説明自体が間違いだらけです。
「**経過時間のパラメータが必要な計算式を（意識せずとも）実装しており、その経過時間の項に`Time.deltaTime`がぴったり収まる**」というだけに過ぎません。

自分が今、なんの式を書いており、どのような計算が行われているのか、をしっかり意識して書けば難しい話ではありません。
その指標となるのが「単位」であり、これを意識すれば`Time.deltaTime`で迷うことはないでしょう。