【ゲーム制作】deltaTimeとは?|意味と使い方を初心者向けにわかりやすく解説

サムネイル画像 ゲーム制作

今回は、ゲーム制作でよく出てくる「deltaTimeとは何か」について解説していきます。

「deltaTimeって何?」
「移動処理に掛けているのをよく見るけど、なぜ必要なの?」
「フレームレートが変わるとゲーム速度が変わるのはどう防ぐの?」

こんな疑問はありませんか?

ゲーム制作を始めると、キャラクターの移動やアニメーション更新で deltaTime という言葉がよく出てきます。最初は少し難しく見えますが、考え方はそこまで複雑ではありません。

この記事を読み終えると、あなたはdeltaTimeの意味・必要な理由・使い方・初心者がハマりやすいミスをしっかり理解できるようになると思いますので、ぜひ最後まで読んでいただけると嬉しいです。

deltaTimeとは?

deltaTimeとは、前のフレームから今のフレームまでに経過した時間のことです。

ゲームは1回だけ処理して終わるのではなく、毎フレーム少しずつ更新されています。deltaTimeは、その「1フレームぶんの時間」を表しています。

たとえば、60fpsなら1フレームはおおよそ 1.0 ÷ 60 = 0.0167秒 です。30fpsならおおよそ 0.0333秒 になります。

つまり、deltaTimeは「今回はどれだけ時間が進んだか」をゲーム側に教えてくれる値です。

なぜdeltaTimeが必要?

deltaTimeが必要な理由は、PCによってフレームレートが違うからです。

もし「1フレームごとに5ピクセル進む」とだけ書いてしまうと、60fpsの環境では1秒間に60回進むので速くなり、30fpsの環境では1秒間に30回しか進まないので遅くなります。

つまり、フレーム数ベースで動かすと、ゲーム速度が環境依存になってしまうわけです。

そこで、「1秒あたり何ピクセル進むか」を基準にして、そこにdeltaTimeを掛けることで、フレームレートが違っても同じ速度で動かせます。

DirectXで仕組みから学ぶか、Unityのようなエンジンで進めるか迷っている方は、DirectXとUnityの違いを解説した記事も参考になります。

deltaTimeを使わない場合の問題

まずは、deltaTimeを使わない移動処理を見てみましょう。

▼main.cpp

#include <iostream>

int main() {
    float x = 0.0f;

    for (int i = 0; i < 5; i++) {
        x += 5.0f;
        std::cout << "x: " << x << "\n";
    }

    return 0;
}

実行結果

x: 5
x: 10
x: 15
x: 20
x: 25

これ自体は動いていますが、この書き方だと「1フレームで5進む」だけなので、フレームレートによって実際の速さが変わってしまいます。

deltaTimeを使った基本の移動

deltaTimeを使う時は、基本的に次の形で考えます。

移動量 = 速度 × deltaTime

たとえば、「1秒で120ピクセル進む」キャラクターなら、1フレームの移動量は 120 × deltaTime になります。

▼main.cpp

#include <iostream>

int main() {
    float x = 0.0f;
    float speed = 120.0f;
    float deltaTime = 0.0167f; // 約60fps

    for (int i = 0; i < 5; i++) {
        x += speed * deltaTime;
        std::cout << "x: " << x << "\n";
    }

    return 0;
}

実行結果

x: 2.004
x: 4.008
x: 6.012
x: 8.016
x: 10.02

このように、1フレームごとの移動量は小さく見えますが、1秒あたりではちゃんと120ピクセル進む計算になります。

deltaTimeはどういう原理で効いているの?

deltaTimeが効く原理は、かなりシンプルです。

ゲームでは本来、「1秒あたりどれだけ動くか」で速度を考えたいです。ですが実際の更新処理はフレームごとに行われるので、そのままだと「1回更新するごとにどれだけ動くか」に変換しなければいけません。

そこで、1秒あたりの速度に、今回進んだ時間であるdeltaTimeを掛けます。

たとえば、速度が 120 px/s で、deltaTimeが 0.0167秒 なら、

120 × 0.0167 = 約2.0

となるので、そのフレームでは約2ピクセル進めばいいわけです。

フレームレートが落ちてdeltaTimeが 0.0333秒 になったなら、

120 × 0.0333 = 約4.0

となって、1フレームあたりの移動量は増えます。これによって、更新回数が減っても1秒単位で見た時の移動速度はだいたい同じになります。

つまりdeltaTimeは、「フレーム単位の処理を、時間単位の処理に変換するための値」と考えるとかなりわかりやすいです。

実際のdeltaTimeの取り方

実際のゲームでは、前フレームの時刻と今の時刻の差を取ってdeltaTimeを求めます。

ここではC++標準の std::chrono を使ったシンプルな例を載せます。

▼main.cpp

#include <iostream>
#include <chrono>
#include <thread>

int main() {
    auto prevTime = std::chrono::steady_clock::now();

    for (int i = 0; i < 3; i++) {
        std::this_thread::sleep_for(std::chrono::milliseconds(16));

        auto currentTime = std::chrono::steady_clock::now();
        std::chrono::duration<float> elapsed = currentTime - prevTime;
        float deltaTime = elapsed.count();

        std::cout << "deltaTime: " << deltaTime << "\n";

        prevTime = currentTime;
    }

    return 0;
}

実行結果

deltaTime: 0.016...
deltaTime: 0.016...
deltaTime: 0.016...

実際の値はPCや実行状況で少し前後しますが、おおよそ1フレームぶんの経過時間が取れているのが分かります。

実践例

では、キャラクターの横移動をdeltaTimeで制御する例を見てみましょう。

▼main.cpp

#include <iostream>

int main() {
    float playerX = 0.0f;
    float moveSpeed = 240.0f;   // 1秒で240進む
    float deltaTime = 0.0167f;  // 約60fps

    for (int i = 0; i < 5; i++) {
        playerX += moveSpeed * deltaTime;
        std::cout << "playerX: " << playerX << "\n";
    }

    return 0;
}

実行結果

playerX: 4.008
playerX: 8.016
playerX: 12.024
playerX: 16.032
playerX: 20.04

この考え方は、移動だけでなく、アニメーション速度、カメラ移動、弾の移動、ノックバック、フェード演出などにもそのまま応用できます。

【重要】私が実際にdeltaTimeで困った体験談

私も自主制作のゲームで、最初はdeltaTimeを使わずに移動処理を書いていました。

自分のPCでは普通に見えていたので問題ないと思っていたのですが、処理の軽い場面ではキャラが妙に速く動き、重い場面では逆に遅くなることがありました。最初は入力処理がおかしいのかと思ってかなり悩みました。

原因は単純で、1フレームごとの固定移動量で動かしていたからでした。そこで、速度を「1秒あたり」で管理して、移動量を speed * deltaTime に直したところ、かなり安定しました。

ただ、その後に今度はdeltaTimeをミリ秒のつもりで使ってしまい、キャラが一瞬で画面外まで飛んだこともあります。これで、deltaTimeは「秒」なのか「ミリ秒」なのかを必ず統一するのがとても大事だと実感しました。

deltaTime使用時のよくある失敗例と対処法

  • 秒とミリ秒を混同する
    0.016 は約16ミリ秒ですが、値そのものは「秒」です。単位を必ずそろえましょう。
  • 速度に毎フレームdeltaTimeを掛けるのではなく、位置に直接固定値を足してしまう
    速度は「1秒あたり」で持ち、更新時に speed * deltaTime を使う形にすると分かりやすいです。
  • deltaTimeが大きく跳ねた時のことを考えていない
    処理落ちしたフレームで極端な値になると、移動量も急に大きくなります。必要なら上限を設けることもあります。

Sleepやフレーム時間との関係

deltaTimeを正しく使うには、そもそもフレーム時間をある程度安定して測れることも大切です。

特にWindowsでは、Sleep の精度が思ったより荒くて、フレーム制御がガタつくことがあります。このあたりは、Sleepの精度が低い理由とtimeBeginPeriodの記事がかなり相性のいい内容です。

注意点

  • deltaTimeは便利ですが、単位の扱いを間違えると一気に挙動が壊れます
  • 1フレームだけ重くなった時、移動量が急に増えることがあります
  • まずは移動処理にだけ入れて、慣れたらアニメーションやカメラにも広げるのがおすすめです

挙動が怪しい時は、deltaTimeの値そのものをログ出力して確認するとかなり原因を追いやすいです。デバッグ方法については、C++でのデバッグのやり方の記事も参考になります。

まとめ

  • deltaTimeは、前フレームから今フレームまでに経過した時間
  • フレームレートが違っても同じ速度で動かすために必要
  • 移動量は 速度 × deltaTime で求める
  • deltaTimeは「フレーム単位の処理」を「時間単位の処理」に変えるための値
  • 秒とミリ秒の混同に注意することが大切

deltaTimeは、ゲーム制作を進めるうえでかなり重要な基本概念です。

最初は少し抽象的に見えるかもしれませんが、「1秒あたりの速度を、1フレームぶんに変換している」と考えるとかなり理解しやすくなります。

ここまで読んでくださり、ありがとうございました。

関連記事