🌊

Unityでフレームレートを理解する

2024/10/18に公開

フレームレートとは?

Unityを使ったゲーム開発において、フレームレートの理解は非常に重要です。

フレームレートは、オブジェクトの移動やアニメーションの滑らかさ、さらにはユーザー体験全体に大きな影響を与えます。本授業では、A地点からB地点へのオブジェクトの移動を通じて、フレームレートと時間の関係性を深く理解します。

筆者は、端的にパラパラ漫画の仕組みをベースに理解しています。
一枚一枚ノートの端を捲るたびに、処理が全て走るという事です。

本記事においては、フレームレートを意図的に変更することで、時間制御とオブジェクトの挙動がどのように連動しているかを体験的に学べます。
これからUnityを触る初学の皆様、なんとなくUnityをやってきた振り返り組の皆様、参考になれば幸いです。

Webプログラミングでは理解しにくいフレームレートのハードルの高さ

Webプログラミングでは、ブラウザがレンダリングやタイミングの多くを自動的に管理してくれるため、開発者がハードウェアのフレームレートについて深く考える機会は少ないかもしれません。
しかし、ゲーム開発やリアルタイムアプリケーションでは、フレームレートが直接的にシステムのパフォーマンスやユーザー体験に影響を及ぼします。

フレームレートの概念を正しく理解していないと、異なるデバイスや環境で予期せぬ動作を引き起こす可能性があります。
例えば、高フレームレートの環境では動きが速くなりすぎたり、低フレームレートではカクついたりすることがあります。

これは、時間経過を適切に計算し、フレームレートに依存しないコードを書くことが求められるためです。

Webプログラミングからゲーム開発に移行する際、このハードウェアレベルでのフレームレートの理解は高いハードルとなり得ます。

本授業(記事)では、そのハードルを克服するために、実際のプログラミングとデバッグを通じてフレームレートの影響を視覚的かつ定量的に理解します。

これにより、フレームレートがゲーム内の挙動にどのように影響するかを深く学び、より堅牢で一貫性のあるアプリケーションを開発できるようになります。

実装スクリプト

それでは先に、コピペでも良いので、以下のスクリプトをUnityで作成してください。
内容の説明を後に行います。

スクリプト:MoveObject.cs

using UnityEngine;

public class MoveObject : MonoBehaviour
{
    public Vector3 targetPosition = new Vector3(10, 0, 0);  // 目標位置
    public float speed = 5f;  // 移動速度
    public int frameRate = 1;  // フレームレート(インスペクターで変更可能)
    private Vector3 startPosition;  // 開始位置

    void Start()
    {
        // フレームレートを設定
        Application.targetFrameRate = frameRate;

        // 現在の位置を開始位置として保持
        startPosition = transform.position;
    }

    void Update()
    {
        // 前回の位置を保存
        Vector3 previousPosition = transform.position;

        // オブジェクトを目標位置に向けて移動
        transform.position = Vector3.MoveTowards(transform.position, targetPosition, speed * Time.deltaTime);

        // 移動量を計算
        float movementAmount = Vector3.Distance(transform.position, previousPosition);

        // 計算式とともにデバッグウィンドウに表示
        Debug.Log($"Movement per frame: {movementAmount} units = speed ({speed}) * Time.deltaTime ({Time.deltaTime})");
    }

    void OnDrawGizmos()
    {
        // マーカーとして赤い球を目標位置に描画
        Gizmos.color = Color.red;
        Gizmos.DrawSphere(targetPosition, 0.5f);
    }
}

手順

  1. シーンの準備

    • オブジェクトの作成
      • ヒエラルキーで 3D Object > CubeSphere を選択し、移動させたいオブジェクトを作成します。
      • オブジェクトをシーン内の任意の位置に配置します。この位置が開始位置となります。
  2. スクリプトの設定

    • スクリプトの作成

      • Assets フォルダ内で右クリックし、Create > C# Script を選択して MoveObject スクリプトを作成します。
      • 上記のコードをスクリプトに貼り付けます。
    • スクリプトのアタッチ

      • 作成した MoveObject スクリプトをオブジェクトにドラッグ&ドロップしてアタッチします。
    • インスペクターでの設定

      • オブジェクトを選択し、インスペクターで以下を設定します。
        • Target Position:目標位置(デフォルトは (10, 0, 0))。
        • Speed:移動速度(デフォルトは 5)。
        • Frame Rate:フレームレート(初期値は 1)。

スクリプト説明

開始位置の設定**

  • Start メソッド内で、オブジェクトの現在の位置を開始位置として保持します。
    startPosition = transform.position;
    
  • オブジェクトの位置をリセットしない:ユーザーのご要望により、オブジェクトの位置を変更せず、そのままの位置から移動を開始します。

オブジェクトの移動**

  • Update メソッドで、オブジェクト自身を目標位置に向けて移動します。
    transform.position = Vector3.MoveTowards(transform.position, targetPosition, speed * Time.deltaTime);
    

移動量の計算とデバッグ表示**

  • 前回の位置と現在の位置を使って、1フレームあたりの移動量を計算します。
    float movementAmount = Vector3.Distance(transform.position, previousPosition);
    
  • デバッグウィンドウに移動量と計算式を表示します。
    Debug.Log($"Movement per frame: {movementAmount} units = speed ({speed}) * Time.deltaTime ({Time.deltaTime})");
    

フレームレートの設定

  • インスペクターで指定した frameRate を使用して、フレームレートを設定します。
    Application.targetFrameRate = frameRate;
    

デバッグ用マーカーの表示

  • OnDrawGizmos メソッドで、目標位置に赤い球体を描画します。
    Gizmos.color = Color.red;
    Gizmos.DrawSphere(targetPosition, 0.5f);
    

実際の挙動からの理解

  1. ゲームを再生し、以下を確認してください。

    • オブジェクトが現在の位置(開始位置)から目標位置に向かって移動します。
    • デバッグウィンドウに、各フレームの移動量とその計算式が表示されます。
  2. デバッグログの例

    Movement per frame: 5 units = speed (5) * Time.deltaTime (1)
    
    • フレームレートが 1 の場合、Time.deltaTime は約 1 秒となり、移動量は 5 ユニットになります。
  3. フレームレートの変更

    • インスペクターで Frame Rate の値を変更し、510 などに設定して再度ゲームを再生します。
    • Time.deltaTime と移動量がそれに応じて変化することを確認してください。

何が起きているか

これは、移動速度を5とした時に、1フレームでのUnit(移動量)でどれだけ移動したかを視覚的に表すためのスクリプトです。

各変数の説明

  • Speed(速度)

    • 単位:ユニット/秒
    • 説明:オブジェクトが1秒間に移動する距離を表します。
    • 例:Speed = 5 の場合、オブジェクトは1秒間に5ユニット移動します。
  • Unit(移動量)

    • 単位:ユニット
    • 説明:1フレームあたりのオブジェクトの移動距離を示します。
    • 例:Movement per frame = 0.5 の場合、オブジェクトはそのフレームで0.5ユニット移動しました。

計算方法

1フレームあたりの移動量(ユニット)は、移動速度(Speed)にそのフレームの経過時間(Time.deltaTime)を掛けたものです。

計算式:

Movement per frame (ユニット)} = Speed (ユニット/秒) x times x Time.deltaTime (秒)

具体例:

  • フレームレート(Frame Rate):10 FPS
  • Time.deltaTime:0.1 秒(1秒 ÷ 10フレーム)
  • Speed:5 ユニット/秒

計算すると、

Movement per frame} = 5 x times 0.1 = 0.5 x ユニット

デバッグログの出力例:

Movement per frame: 0.5 units = speed (5) * Time.deltaTime (0.1)
  • Speed はオブジェクトの 速度 を示し、単位は ユニット/秒 です。
  • Unit(Movement per frame)1フレームあたりの移動距離 を示し、単位は ユニット です。
  • 両者は Time.deltaTime を介して関係していますが、数値としては異なるものです。

補足情報

  • Time.deltaTime:前のフレームから現在のフレームまでの経過時間(秒)を示します。フレームレートが変わると、この値も変化します。
  • フレームレートの影響:フレームレートが高い(FPSが大きい)と、Time.deltaTime は小さくなり、1フレームあたりの移動量も小さくなります。しかし、移動速度(Speed)が一定であれば、オブジェクトは時間経過に対して一貫した速度で移動します。

Time.deltaTimeとは

  • フレームレートと Time.deltaTime の関係

    • フレームレートが高くなると、Time.deltaTime は小さくなります。
    • 移動速度は speed * Time.deltaTime で計算されるため、フレームレートが変化してもオブジェクトの移動速度は一定になります。
  • 開始位置の変更

    • オブジェクトの開始位置を変更したい場合は、シーンビューでオブジェクトを移動させてください。
  • 移動速度の調整

    • インスペクターで Speed の値を変更して、オブジェクトの移動速度を調整できます。
  • デバッグ情報の活用

    • デバッグログとシーンビュー上のマーカーを活用して、移動の挙動を詳細に確認できます。

実際の数値が1fpsで計算されない問題

  1. Application.targetFrameRate は目標値であり、保証ではない

    • この設定は Unity に「このフレームレートを目指してほしい」と伝えるものです。
    • しかし、実際のフレームレートはデバイスの性能や Unity の内部処理によって変わります。
    • 極端に低いフレームレート(例:1FPS)は、実現が難しい です。
  2. Time.deltaTime は実際のフレーム間の時間を示す

    • Time.deltaTime は、前のフレームから現在のフレームまでの「実際の」経過時間(秒)を示します。
    • フレームレートが目標より高くなると、Time.deltaTime はその分小さくなります。
  3. Unity の内部制約やハードウェアの限界

    • Unity や使用しているデバイスには、最低限必要なフレームレートがあります。
    • 1FPS のような非常に低い値は、システムが正常に動作するために避けられます
  4. VSync(垂直同期)の影響

    • VSync が有効だと、フレームレートがモニターのリフレッシュレートに同期されます。
    • これにより、Application.targetFrameRate の設定が無視されることがあります。

具体的な例

  • 設定したこと: Application.targetFrameRate = 1;
  • 期待した結果: Time.deltaTime が約 1.0 秒になる。
  • 実際の結果: Time.deltaTime が約 0.475 秒になった。

理由:

  • Unity は 1 秒に約 2 回フレームを更新している(約 2FPS)。
  • これは、システムが正常に動作するための最低限のフレームレートである可能性があります。

実際の挙動総評

  • フレームレート設定はあくまで「目標」

    • 設定した値が必ず反映されるわけではありません。
    • 特に極端な値(低すぎるまたは高すぎる)は、システムによって調整されます。
  • Time.deltaTime を信頼する

    • この値は、実際のフレーム間の経過時間を示します。
    • 移動やアニメーションの計算には、必ず Time.deltaTime を使いましょう。
  • 現実的なフレームレートを設定する

    • 5FPS や 10FPS など、もう少し高い値を設定すると、設定が反映されやすくなります。
  • VSync の設定を確認する

    • メニューの「Edit」→「Project Settings」→「Quality」から、VSync を「Don't Sync」に設定すると、Application.targetFrameRate がより効果的になります。
  • 極端に低いフレームレートは実現が難しいため、Time.deltaTime が期待通りにならないことがあります。

  • Unity の内部処理やハードウェアの制約により、フレームレートが自動的に調整されます。

  • 現実的な値を設定し、Time.deltaTime を使って時間に依存した計算を行うことが重要です。

まとめ

  • オブジェクトの現在の位置を開始位置として使用:ユーザーのご要望に合わせて、オブジェクトの位置を変更せず、そのままの位置から移動を開始するようにしました。

  • フレームレートの調整が可能frameRate 変数をインスペクターで変更することで、フレームレートを動的に調整できます。

  • 移動量と計算式のデバッグ表示:各フレームの移動量とその計算式をデバッグウィンドウに表示することで、Time.deltaTime の影響を視覚的に理解できます。

このスクリプトを使用して、フレームレートや Time.deltaTime がオブジェクトの移動にどのように影響するかを詳しく学ぶことができます。ご不明な点や追加のご要望がありましたら、お気軽にお知らせください。

Discussion