Version: 2017.4
イベント関数
ゲームオブジェクトの作成および削除

タイムとフレームレートの管理

Update 関数を使用すると、定期的にスクリプトからの入力やその他のイベントをモニターして、適切な処置を取ることができます。例えば、Forward キーが押されるとキャラクターが動くとします。このような時系列のアクションを取り扱う場合、ゲームのフレームレートは一定ではなく、 Update 関数の呼び出しの時間的な間隔も一定ではないということに気を付けてください。

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

//C# の例
using UnityEngine;
using System.Collections;

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


//JS の例
var distancePerFrame: float;

function Update() {
    transform.Translate(0, 0, distancePerFrame);
}

しかし、フレーム時間が一定でないので、オブジェクトは不規則な速度で移動するように見えます。フレーム時間が 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);
    }
}


//JS の例
var distancePerSecond: float;

function Update() {
    transform.Translate(0, 0, distancePerSecond * Time.deltaTime);
}

ここでの移動は distancePerFrame ではなく distancePerSecond で与えられていることに注意してください。フレームレートの変化に応じて移動距離が変わるため、オブジェクトの速度は一定になります。

固定時間ステップ

メインフレームの更新とは異なり、Unity の物理システムは、固定された時間ステップで更新されます。これはシミュレーションの正確さと一貫性にとって重要です。物理計算の更新開始時に、Unity は最後の物理計算の更新が終了する固定の時間ステップ値を時間に加えてアラームを設定します。物理システムは、アラームが切れるまで計算を実行します。

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

最大許容時間ステップ

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

CPU には十分な処理能力が有りますが、Unity には、フレーム処理が追いつくよう効果的に物理演算の時間を遅らせるオプションがあります。 Maximum Allowed Timestep 設定 (Time Manager の中にあります) は、Unity が処理する物理演算の消費時間に制限をかけ、与えられたフレーム更新の間に FixedUpdate をコールします。フレーム更新の処理が Maximum Allowed Timestep よりも長くなる場合は、物理エンジンは時間を停止し、フレーム処理が追いつくようにします。フレーム更新が完了すると、物理演算は、停止してからまったく時間が経過していないかのように再開します。結果的には、リジッドボディは通常行うように、リアルタイムで完璧に移動することはできず、少し遅れてしまいます。ただし、物理演算「クロック」は、通常通り移動しているかのようにそれらを追跡します。物理演算時間を遅延させることは通常は目立たず、ゲームパフォーマンスとの間で許容できる妥協策です。

タイムスケール

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

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

//C#の例
using UnityEngine;
using System.Collections;

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

//JS の例
function Pause() {
    Time.timeScale = 0;
}

function Resume() {
    Time.timeScale = 1;
}

キャプチャーフレームレート

ゲームをビデオとして記録したいケースは時間管理の非常に特別なケースです。画面を保存する作業にはかなりの時間がかかるため、通常のゲーム中にこれを行おうとすると、ゲームの通常のフレームレートは大幅に削減されます。これではゲームの真の性能を反映しない動画になってしまいます。

Unity はこの問題を回避することができるCapture Framerate プロパティーを提供しています。プロパティーの値が 0 以外の値に設定されている場合、ゲームタイムは遅くなり、フレーム更新は正確な一定の間隔で行われます。フレーム間の間隔は、1/(Time.captureFramerate) に等しく、5.0 に値が設定される場合、更新が 1/5 秒ごとに発生します。フレームレートへの要求を効果的に削減すると、Update 関数の時間で、スクリーンショットを保存したり、他の操作を実行できます。

//C# の例
using UnityEngine;
using System.Collections;

public class ExampleScript : MonoBehaviour {
    //スクリーンショットシーケンスとしてフレームをキャプチャ。     
        //画像はPNGファイルとしてフォルダーに格納。これらの画像は
    // 画像ユーティリティソフトウェア (QuickTime Pro など) で動画と統合されます。
    // スクリーンショットを格納するフォルダー。
    // フォルダーが既存の場合、数を加え空のフォルダーを作ります。
    string folder = "ScreenshotFolder";
    int frameRate = 25;
        
    void Start () {
        // 再生のフレームレートを設定 (これ以降は、実際の時間はゲームの時間と関連しません)
        Time.captureFramerate = frameRate;
        
        // フォルダーを作成
        System.IO.Directory.CreateDirectory(folder);
    }
    
    void Update () {
        // ファイル名をフォルダー名に追加 (形式は "0005 shot.png")
        string name = string.Format("{0}/{1:D04} shot.png", folder, Time.frameCount );
        
        // 指定したファイルにスクリーンショットをキャプチャ
        Application.CaptureScreenshot(name);
    }
}

//JS の例
//スクリーンショットシーケンスとしてフレームをキャプチャ
//画像はPNGファイルとしてフォルダーに格納。これらの画像は
// 画像ユーティリティソフトウェア (QuickTime Pro など) で動画と統合されます。
// スクリーンショットを格納するフォルダー。
// フォルダーが既存の場合、数を加え空のフォルダーを作ります。
var folder = "ScreenshotFolder";
var frameRate = 25;


function Start () {
    // // 再生のフレームレートを設定 (これ以降は、実際の時間はゲームの時間と関連しません)
    Time.captureFramerate = frameRate;

    // フォルダーを作成
    System.IO.Directory.CreateDirectory(folder);
}

function Update () {
    // ファイル名をフォルダー名に追加 (形式は "0005 shot.png")
    var name = String.Format("{0}/{1:D04} shot.png", folder, Time.frameCount );

    // 指定したファイルにスクリーンショットをキャプチャ
    Application.CaptureScreenshot(name);
}

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

イベント関数
ゲームオブジェクトの作成および削除