[Unity] Rigidbodyの軌道を自前で計算する


Rigidbodyでの物理計算、便利ですよね。
ただ、自分で計算して位置を出しているわけではないのでそれが実際にどう動くのか、数秒後の世界を見ようとしてもすぐには確認できません。

そこで、今回はこちらの記事(Unity: n秒後のRigidbodyの移動先の位置を取得するスクリプト書いた)を参考にさせてもらい、どういう計算がなされているのかを解説しつつメモしておきたいと思います。


実際の動作の動画キャプチャ

[2015.08.04] この動作サンプルをGithubにアップしました。

サンプルコード

上記の記事を元にメソッドを以下のように実装しました。

[2015.08.04 追記]
コメントで指摘をもらいました。(startPosition使ってなかった・・)

Vector3 CalcPositionFromForce(float time, float mass, Vector3 startPosition, Vector3 force, Vector3 gravity) {
    Vector3 speed = (force / mass) * Time.fixedDeltaTime;
    Vector3 position = (speed * time) + (gravity * 0.5f * Mathf.Pow(time, 2));

    return startPosition + position;
}

上記メソッドは、引数で与えられた「時間、質量、初期位置、力、重力」を元に、最初の引数である時間後の位置を計算するものです。

さて、ではなにをどう計算しているのか。それをざっくりと解説したいと思います。

加速度から位置を計算

重力は加速度です。一般的に $9.8 m/s^2$ という値が使われます。
そして加速度から一定時間後の位置を計算するには以下の式を使います。

pos = \frac{1}{2}\alpha t^2

位置は、加速度を1/2にしたものに時間の2乗を掛けたものとして計算することができます。
仮に重力加速度だけが影響し、2秒後の位置を求める場合は以下のようになります。

pos = \frac{1}{2}(9.8)(2^2) = 19.6m

※ 基本的に物理エンジンの単位は「Kg, m, S(キログラム、メートル、秒)」が使われます。

力から加速度を計算

簡単な物理的計算を行う場合、ニュートン力学が比較的手頃でしょう。
ニュートンの運動方程式として以下が有名です。

F = ma

ここで$F$は力(単位はニュートン)、$m$は質量(mass)、$a$は加速度です。
加速度を求めたいので式を変形して、

a = \frac{F}{m}

とします。
つまり、力を質量で割ったものが加速度、ということです。

以上を元にプログラムを見直すとなにをしているかが分かるかと思います。
特に、

Vector3 speed = (force / mass) * Time.fixedDeltaTime;

変数名を見てもらうと、公式をそのまま使っているのが分かります。
ちなみにTime.fixedDeltaTimeを掛けているのは「瞬間的に」力が加わったとみなすためです。

プログラムは連続的な計算ではなく「離散的な」計算を行います。
つまり、線ではなくあくまで計算可能な範囲で分割した「点」をつないで計算を行うのです。

Time.fixedDeltaTimeはまさにこの計算可能な範囲でのひとつの時間の単位となります。
結果として「瞬間的に」力を加えたことをシミュレートしているわけですね。

つまり、ごく短い時間だけ求めた力を物体に与え続けた、としているわけです。
加速度x時間なので、求めているのは「速度」となります。

そして

speed * time

の部分で「速度x時間」を計算し、結果として位置を求めている、というわけです。
あとは上記で計算した重力の部分も足しあわせてあげることで、引数で渡された時間後にどこに物体があるかを導き出しているというわけですね。

ちなみに物理エンジンと言えば、過去に(2Dですが)本やネットの情報を参考に物理エンジンを自作してみたときの記事があるので興味がある人は見てみてください。(ただし学習のために作っただけなので処理が最適化されておらず、だいぶ重いです・・)

自作2D物理エンジンを作った話