65
54

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

CA Tech LoungeAdvent Calendar 2023

Day 11

【ゲームプログラミング】「速度」を使う時は必ず`deltaTime`を使ってください

Last updated at Posted at 2023-11-22

Unityを念頭に置いた記述をしますが、どのエンジン、言語にも通ずる一般的・汎用的なお話をします

はじめに

みなさんは、ゲーム上で「 移動 」を扱う際、どのように実装していますか?
UnityならAddForce()という力学的な力を加える関数で動かすことも時々しますが、普通は 座標に足し算 (引き算) をして移動させ ますよね?

移動.cs
position.x += something_x;
position.y += something_y;

みたいな感じに。ここ問題提起。
somethingには何を入れますか?

ちょっとゲームプログラミングやってきた方は、このsomethingが大きいと移動が速くなることに気づき、something速度に相当 するものだと気づいてor知っているでしょう。

だから

移動.cs
position.x += speed_x;
position.y += speed_y;

と書くと。

これ、不安定です

「速さ」を足し算しない理由

説明が二通りできます。

プログラム上の説明(簡単な方)

ほとんどの(アクション性のある)ゲームは、「フレーム」で構成される「ループ」にて動作していることと思います。

もし、 足し算をそのまま一定の「速さ」にすると、毎フレームその一定値の分だけ足される わけです。

逆に言うと、 ゲーム上の速さがFPSに依存する ことになります。
次のh3項目と重複しますが、この場合(数式に免疫のない方は見なくともいいです)

\begin{align}
速度 &= 一秒あたりの移動量\\
    &= フレーム当たりの速さ \times 一秒当たりのフレーム\\
    &= フレーム当たりの速さ \times FPS
\end{align}

ということです。
要するに、 重いときは移動が遅く、軽いときは移動が早く見えるようになります。
もちろんこっちがいい場合もあります。後述参照。

この場合だと、良からぬ点が二つあります:

  • FPSに波がある場合、ゲームのスピード感が頻繁に変わり強い違和感を生じさせる
    極端な例ですが、1秒ごとにFPS=60とFPS=30に切り替わるようなシステムだったとして(そんなことある?)、この場合1秒ごとにゲーム全体のスピード感が2倍ないし半分になるわけです。嫌ですよね。
  • FPSを変更した場合、全てのスピードの設定を再び調整しないといけない。
    ゲームプログラミングは調整性が肝要ですからね。

物理学上の説明(ちょっとややこしい方)

「速さ」の公式は小学校でやりましたか?
人によっては、このような図を習った方もいますね。
kihaji
引用:WAKARA・マスログ「は・じ・きを覚えさせることの最大の問題点【速さ・時間・距離の公式】[youtube公開]」

ここで一度僕が好きな代ゼミ亀田先生の30秒動画を見てください:https://www.youtube.com/watch?v=axBVaj42f0A
ご視聴ありがとうございます。

要するにこういうことです。

移動した距離 = 速さ \times 時間

ここからがサビです。
座標にdを足し算する ということは、dだけ動く ということですよね。すなわちd「移動した距離」 に該当するわけです。
その d に「速さ」を当てはめるのは、まあおかしいわけですよね。
だから時間を掛け算しなさい という話になるわけです。

ゲームだから実際の物理学と違ってもいいだろ!という指摘もあるでしょう。実際そうです。
しかし、現実に揃えるメリットは明確に存在します。

こうすることで、 「速さ」「一秒あたりの移動量」に統一できます。

常人の皆様は「秒」を用いる時間帯系の中で生活されているかと思われますので、「フレーム当たり速さ」なんというヘンな感じなパラメーターではなく、秒あたりのパラメーターで 直観的に 調整できるわけですね。

deltaTimeを使ってください

上記の問題を解決するには、「はじき」の公式にもある通り、「速さ」に「時間」を掛け算する といいわけですね。 その「時間」にdeltaTimeを割り当てます。

deltaTimeって?

Unity特有のの変数名をそのままみんな知ってるよねって面で喋っていてごめんなさい。
deltaTimeは、「前フレームと今フレームの時間間隔」 です。

例えばフレーム19からフレーム20に移るのに0.02秒かかったら、フレーム20の時点でdeltaTime=0.02が入るわけです。

Unityの場合はTime.deltaTimeでアクセスできます。

他の場合は、

  • フレーム→フレームの時間間隔を測定するタイマーを実装するか、
  • FPSの起伏があまりない場合、FPSの逆数を掛け算 することによって似たことができます。
    FPS「一秒に処理されるフレーム数」の逆数が「1フレームにかかる秒」ですからね。

なんで掛け算するの?

フレームとフレームの間で移動した量を足し算するわけなので、その間の 時間deltaTimeですよね?
それを掛け算すると、 フレームとフレームの間の 移動した距離 になるからです。

時間積分(時間で累積)をするものは全てdeltaTime

速度→距離

以外にも、

時間当たりダメージ量→受けたダメージ

時間当たりポイント量→獲得ポイント

加速度→速度 (加速度から初めて移動量を出す場合、二回deltaTimeの掛け算をする!!)

力積→運動量

など、「時間当たり」から、まとまった「量」を導くときは、全てdeltaTimeにしましょう。

あまりピンと来ない方は、「単位」から考えてみるアプローチもオススメです: https://qiita.com/toRisouP/items/930100e25e666494fcd6

「フレーム当たり速さ」の方がいい場合もある

例えば格闘ゲームなどの公平性が重要とされるEスポーツ性のあるゲームは、こちらが好まれます。
格ゲーは「nフレーム発生の攻撃」とか言いますけど、もし環境によってタイミングがフワフワしてたら、ガチ勢プレイヤーは困りますからね。

あとは、かなりカクカクなゲームな場合もこちらがいい場合があります。
deltaTimeが大きくなるわけなので、逆に「ワープした」という違和感を与えてしまいますので、いっそのことフレームごとの方が納得感が出ます。

さいごに

deltaTimeを掛け算しましょう

コメントで皆様から有益な示唆を受けているので、ぜひご覧ください。

いいね頂けると泣いて喜びます><

この記事を読んでいる方は次の記事を読んでいるかもしれません:

65
54
10

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
65
54

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?