Version: 2021.2
言語: 日本語
ScriptableObject
重要なクラス - Mathf

Important Classes - Time and frame rate management

Time クラス

Unity の Time クラスは、プロジェクトで時間関連の値を扱う重要な基本プロパティを多数提供します。

Time スクリプトリファレンス のページでは各メンバーを詳しく説明しています。

ここでは、一般的で典型的な使用例をいくつか紹介します。

Time.time は、プロジェクトが再生を開始してからの時間を返します。

Time.deltaTime は、最後のフレームが完了してからの経過時間を返します。

Time.timeScale は、時間が経過する速度を表します。この値を読み取ったり、設定して時間が経過する速さを制御し、スローモーションのような効果を生み出すことができます。

時間関連のプロパティの全リストについては、Time のスクリプトリファレンスページ を参照してください。

Frame rate management

The Update function allows you to monitor inputs and other events regularly from a script and take appropriate action. For example, you might move a character when the “forward” key is pressed. An important thing to remember when handling time-based actions like this is that the game’s frame rate is not constant and neither is the length of time between Update function calls.

この例として、フレームごとに、徐々に前方にオブジェクトを動かす作業を考えて見ましょう。最初は、フレームごとに一定の距離ずつオブジェクトを移動しているように見えるかもしれません。

//C# script example
using UnityEngine;
using System.Collections;

public class ExampleScript : MonoBehaviour {
    public float distancePerFrame;
    
    void Update() {
        transform.Translate(0, 0, distancePerFrame); // this is incorrect
    }
}

しかし、フレーム時間が一定でないので、オブジェクトは不規則な速度で移動するように見えます。フレーム時間が 10 ミリ秒なら、オブジェクトは 1 秒に distancePerFrame の距離を 100 回前進します。しかし、フレーム時間が 25 ミリ秒に増加した場合 (例えば CPU 負荷のため) 1 秒に 40 回しか前進できず、進む距離が少なくなります。その解決方法は、フレーム時間に応じた移動の距離をスケーリングすることです。移動の距離は Time.deltaTime プロパティから読み取り可能です。

//C# サンプルスクリプト
using UnityEngine;
using System.Collections;

public class ExampleScript : MonoBehaviour {
    public float distancePerSecond;
    
    void Update() {
        transform.Translate(0, 0, distancePerSecond * Time.deltaTime);
    }
}

Note that the movement is now given as distancePerSecond rather than distancePerFrame. As the frame rate changes, the size of the movement step will change accordingly and so the object’s speed will be constant.

ターゲットプラットフォームによっては、Application.targetFrameRate または QualitySettings.vSyncCount のいずれかを使用して、アプリケーションのフレームレートを設定することができます。詳細については、Application.targetFrameRate API documentation を参照してください。

固定時間ステップ (fixedDeltaTime)

更新とは異なり、Unity の物理システムは、シミュレーションの正確さと一貫性にとって重要な固定時間ステップ (fixed timestep) に有効です。物理計算の更新開始時に、Unity は現在の時刻に追いつくのに必要な物理計算の更新を行います。

ゲームやアプリケーションが固定時間ステップの値よりも高いフレームレートで動作している場合は、各フレームが 1 つの固定時間ステップの持続時間よりも短いことを意味します。そのため、Unity はフレームごとに 1 回または 0 の固定物理演算更新を行います。例えば、固定時間ステップ値が 0.02 の場合、1 秒間に 50 回の固定の更新が行われます。ゲームやアプリケーションが毎秒約 60 フレームで動作する場合、10 フレームに 1 回程度の割合で、固定更新が行われないフレームが発生します。

ゲームやアプリケーションが固定時間ステップの値よりも低いフレームレートで動作している場合は、各フレームが 1 つの固定時間ステップの持続時間よりも長いことを意味します。そのようにして、物理シミュレーションが最後のフレームからの経過時間に “追いつく” ようにします。例えば、固定時間ステップ値が 0.01 の場合、1 秒間に 100 回の固定の更新が行われます。ゲームやアプリケーションが毎秒約 25 フレームで動作する場合、フレームごとに 4 回程度の割合で固定更新を行い、物理シミュレーション時間を現在のフレーム時間に合わせて最新に保ちます。

Time ウィンドウで固定時間ステップの値を変更でき、Time.fixedDeltaTime プロパティを使用して、スクリプトからそれを読み取ることができます。時間ステップの値が低いと、より頻繁に物理計算の更新が行われより正確なシミュレーションが可能ですが、CPU への付加が高くなります。物理エンジンに特に高精度を必要とする高い要求をしない限り、大抵、デフォルトの固定時間ステップを変更する必要はありません。

Unity の時間のロジック

以下のフローチャートは、Unity が 1 つのフレーム内の時間をカウントするために使用するロジックと、timedeltaTimefixedDeltaTimemaximumDeltaTime プロパティがどのように相互に関係しているかを示しています。

最大許容時間ステップ (maximumDeltaTime)

非常に遅いフレームが発生すると、Maximum Allowed Timestep (最大許容時間ステップ、Time ウィンドウ内) は、非常に大きな deltaTime 値に起因する望ましくない状態を避けるために、以下のフレームの deltaTime の値を制限します。この値は、スクリプトを通して Time.maximumDeltaTime としてアクセスすることもできます。

例えば、アプリケーションが maxDeltaTime の値 0.333 で 60Hz で実行されている場合に、1 つの非常に遅いフレーム (2 秒) に遭遇した場合、以下の表のような動作になります。

フレーム unscaledTime time deltaTime smoothDeltaTime
1 0.000 3.000 0.014 0.014
2 0.014 3.015 0.014 0.014
3 0.028 3.028 0.014 0.014
4 (a) 0.042 3.043 0.014 0.014
5 2.062 (b) 3.376 (c) 0.333 (d) 0.078
6 2.082 3.396 0.020 0.066
7 2.096 3.410 0.014 0.056
8 2.110 3.424 0.014 0.048

上のデータは、最大許容時間ステップの値 (maximumDeltaTime) の影響を示しています。1 から 8 の番号がつけられた 8 つのフレームを示しています。この例のフレーム 4 (a) では速度の減退が発生し、他のフレームのように約 25 ミリ秒ではなく、フレームが完了するのに 2 秒かかる原因になっています。この影響は次のフレームの値に反映されています。一時停止は unscaledTime 値では約 2 の増分として確認できますが (b)、Time.time 値は 0.333 (c) だけ増加しています。これはこの例が実行されていたときの最大許容時間ステップの値だからです。このフレームの deltaTime 値も、最大許容時間ステップ (d) に固定されています。

最大許容時間ステップの値は、物理の時間管理にも影響します。固定時間ステップ は、リアルタイムで物理シミュレーションを正確に保ちます。しかし、ゲームで物理演算を多用する場合、場合によっては問題を引き起こす可能性があり、ゲーム時のフレームレートも低くなってしまいます (例えば、多くのオブジェクトが動いている場合など)。主フレームの更新処理は、通常の物理演算更新の間に “詰め込む” 必要があります。そのため、実行する処理が大量にある場合は、いくつかの物理演算更新を 1 つのフレーム内で行い、現際の時間に物理シミュレーションを間に合わせることが必要な場合があります。フレームタイム、オブジェクトの位置、他のプロパティーがフレームの開始時に凍結され、頻繁に更新される物理演算との同期状態ではなくなります。

CPU パワーが限られているということは、物理システムの処理能力にも限界があるということです。そこで Unity には、物理の時間を効果的に遅くして、フレーム処理が遅れた場合に追いつけるようにするオプションがあります。最大許容時間ステップは、フレーム更新中に Unity が物理演算と FixedUpdate の呼び出しの処理に費やす時間を制限します。

フレーム更新の処理が Maximum Allowed Timestep の処理よりも長くかかる場合は、物理エンジンは “時間を停止” し、フレーム処理が追いつけるようにします。フレーム更新が完了すると、物理演算は、停止してからまったく時間が経過していないかのように再開します。結果的には、リジッドボディは通常行うように、リアルタイムで完璧に移動することはできず、少し遅れてしまいます。ただし、物理演算の “時計” は、通常通り移動しているかのようにそれらを追跡します。物理演算時間の遅延は通常は目立たず、ゲームパフォーマンスとの許容範囲の妥協策です。

Time Scale

“bullet-time” のような特殊効果のために、ゲームの時間の経過を遅くして、アニメーションやスクリプトの応答を少ない割合で発生させることは、有効な場合があります。さらに、ゲームを中断するときのように、完全にゲーム時間を凍結したい場合があるかもしれません。Unity は、リアルタイムと関連づけてゲーム時間をどのように進行させるかについて制御する Time Scale プロパティを持っています。スケールを 1.0 に設定すると、ゲームの時間はリアルタイムに一致します。2.0 の値では Unity で 2 倍の速さで時間が経過します (つまり、アクションは、高速化されます)。他方、0.5 の値ではゲームを半分の速度に遅くします。0 の値は時間を完全に「停止」します。タイムスケールは、実際に実行を遅くするわけではなく、単に Time.deltaTimeTime.fixedDeltaTime を通して Update と FixedUpdate 関数に知らせる時間ステップを変えているにすぎません。Update 関数は、ゲーム時間が遅くなるとき通常より頻繁に呼び出される傾向がありますが、単に、各フレームに伝えられる デルタタイム の処理が減らされます。他のスクリプト関数はタイムスケールから影響を受けません。ですから、例えば、ゲームが停止している場合は、正常なインタラクションで GUI を表示できます。

Time ウィンドウは、グローバルにタイムスケールを設定できるプロパティを持っていますが、一般的に、Time.timeScale プロパティを使用してスクリプトから値を設定すると、より便利です。

//C# スクリプトサンプル
using UnityEngine;
using System.Collections;

public class ExampleScript : MonoBehaviour {
    void Pause() {
        Time.timeScale = 0;
    }
    
    void Resume() {
        Time.timeScale = 1;
    }
}

Capture Framerate

A very special case of time management is where you want to record gameplay as a video. Since the task of saving screen images takes considerable time, the usual frame rate of the game will be drastically reduced if you attempt to do this during normal gameplay. This will result in a video that doesn’t reflect the true performance of the game.

Fortunately, Unity provides a Capture Framerate property that lets you get around this problem. When the property’s value is set to anything other than zero, game time will be slowed and the frame updates will be issued at precise regular intervals. The interval between frames is equal to 1 / Time.captureFramerate, so if the value is set to 5.0 then updates occur every fifth of a second. With the demands on frame rate effectively reduced, you have time in the Update function to save screenshots or take other actions:

//C# script example
using UnityEngine;
using System.Collections;

public class ExampleScript : MonoBehaviour {
    // Capture frames as a screenshot sequence. Images are
    // stored as PNG files in a folder - these can be combined into
    // a movie using image utility software (eg, QuickTime Pro).
    // The folder to contain our screenshots.
    // If the folder exists we will append numbers to create an empty folder.
    string folder = "ScreenshotFolder";
    int frameRate = 25;
        
    void Start () {
        // Set the playback frame rate (real time will not relate to game time after this).
        Time.captureFramerate = frameRate;
        
        // Create the folder
        System.IO.Directory.CreateDirectory(folder);
    }
    
    void Update () {
        // Append filename to folder name (format is '0005 shot.png"')
        string name = string.Format("{0}/{1:D04} shot.png", folder, Time.frameCount );
        
        // Capture the screenshot to the specified file.
        Application.CaptureScreenshot(name);
    }
}

一般的にこの技術を使用して動画を記録するのは非常に有効のようですが、著しく遅くなると、ゲームをプレイすることが難しくなります。テストプレーヤーの仕事を困難にしないよう十分なレコーディング時間を可能にするために Time.captureFramerate 値を試してみると良いでしょう。

ScriptableObject
重要なクラス - Mathf