エフアンダーバー

個人開発の記録

【Unity】 Rigidbodyの移動方法

Rigidbody(剛体)の移動に関してまとめたいい日本語の記事が見つからなかったので書いてみる(検索に引っかかりづらいだけ?)。

物理は詳しくないので基本的なことだけ。

Rigidbodyの移動

Rigidbodyの基本的な移動方法には次の四つがあります。

  • position
  • velocity
  • AddForce
  • MovePosition

以降のサンプルコードでは度々rigidbodyという変数が登場しますが、 これはMonoBehaviourのrigidbodyプロパティではありません(非推奨のプロパティ)。 事前に次のようなコードでrigidbodyを取得してください。

Rigidbody rigidbody = GetComponent<Rigidbody>();

position

positionはRigidbodyの位置を変更します。

rigidbody.position = Vector3.forward * 0.1f;

Rigidbodyをアタッチしたゲームオブジェクトの位置を変更したい場合には、 Transform.positionではなく、Rigidbody.positionを変更します。

この方法による位置の変更は瞬間移動ではなく空間転移です(?!)
要するに、移動元と移動先の間に何があろうと関係ありません。

velocity

velocityはRigidbodyの速度を変更します。

rigidbody.velocity = Vector3.forward * 0.1f;

元々の速度を上書きしていることに注意が必要です。

AddForce

AddForceは力を加えます。加速度を設定すると考えてもいいかもしれません。

rigidbody.AddForce(Vector3.forward * 0.1f);

AddForceは第二引数にForceModeというオプションがあり、それによって力の加え方が変わります(省略した場合はForceMode.Forceです)。

ForceMode 質量 タイプ
Force 考慮 継続的
Impulse 考慮 瞬間的
Acceleration 無視 継続的
VelocityChange 無視 瞬間的

ForceMode.Force

Forceは物理における「力」です。 何かを押し続けるというような継続的な力を表します。 『プレイヤーがキーを押している間』や『ある範囲にいる間』というように特定の期間中、力を与え続ける場合に使います。

AddForceの第一引数は  \mbox{質量} \times \mbox{加速度} あるいは  \mbox{質量} \times \cfrac{\mbox{距離}}{\mbox{時間}^{2}} を表す値と解釈されます。  \mbox{速度変化} = \mbox{加速度} \times \mbox{時間} = \mbox{第一引数} \times \cfrac{\mbox{時間}}{\mbox{質量}} なので次の二行のコードは同じような意味になります *1

rigidbody.AddForce(Vector3.forward * 0.1f, ForceMode.Force);

rigidbody.velocity += (Vector3.forward * 0.1f) * Time.fixedDeltaTime / rigidbody.mass;

ForceMode.Impulse

Impulseは物理における「力積」です。 釘を打つときのような瞬間的な力を表します。 『プレイヤーがキーを押した瞬間』や『物体同士が接触した瞬間』というように何かのイベントの発生に対して、一瞬だけ力を加える場合に使います。

AddForceの第一引数は  \mbox{質量} \times \mbox{速度変化} あるいは  \mbox{質量} \times \cfrac{\mbox{距離}}{\mbox{時間}} を表す値と解釈されます。  \mbox{速度変化} = \mbox{第一引数} \times \cfrac{1}{\mbox{質量}} なので次の二行のコードは同じような意味になります。

rigidbody.AddForce(Vector3.forward * 0.1f, ForceMode.Impulse);

rigidbody.velocity += (Vector3.forward * 0.1f) / rigidbody.mass;

ForceMode.Acceleration

Accelerationは物理における「加速度」です。  \mbox{力} = \mbox{質量} \times \mbox{加速度} なのでForceMode.Forceの質量を無視したバージョンになります。

AddForceの第一引数は  \mbox{加速度} あるいは  \cfrac{\mbox{距離}}{\mbox{時間}^{2}} を表す値と解釈されます。  \mbox{速度変化} = \mbox{加速度} \times \mbox{時間} = \mbox{第一引数} \times \mbox{時間} なので次の二行のコードは同じような意味になります。

rigidbody.AddForce(Vector3.forward * 0.1f, ForceMode.Acceleration);

rigidbody.velocity += (Vector3.forward * 0.1f) * Time.fixedDeltaTime;

ForceMode.VelocityChange

VelocityChangeは物理における「速度変化」です。  \mbox{力積} = \mbox{質量} \times \mbox{速度変化} なのでForceMode.Impulseの質量を無視したバージョンになります。

AddForceの第一引数は  \mbox{速度変化} あるいは  \cfrac{\mbox{距離}}{\mbox{時間}} を表す値と解釈されます。  \mbox{速度変化} = \mbox{第一引数} なので次の二行のコードは同じような意味になります。

rigidbody.AddForce(Vector3.forward * 0.1f, ForceMode.VelocityChange);

rigidbody.velocity += (Vector3.forward * 0.1f);

MovePosition

MovePositionはほぼpositionと同じで、Rigidbodyの位置を変更します。

rigidbody.MovePosition(Vector3.forward * 0.1f);

ただし、isKinematicの値によっては微妙に異なる挙動をします (以下、リファレンスに詳細が書かれていないため、リファレンスの内容と実際の挙動からの推測です)。

まず、isKinematicがfalseのときにはpositionと全く同じ挙動となります。 つまり、次の二行のコードは同じ意味になります。

rigidbody.position = Vector3.forward * 0.1f;

rigidbody.MovePosition(Vector3.forward * 0.1f);

次に、isKinematicがtrueのときにはpositionの空間転移とは異なり、瞬間移動になります。 つまり、移動元と移動先の間を瞬時に通過したとみなされます。 これはRigidbodyの設定によってはpositionと挙動が異なります。 例えば、interpolationを設定していた場合には移動の中間状態が補間されてレンダリングされる可能性があります。 また、collisionDetectionModeを設定していた場合には間にあったオブジェクトに影響を与える可能性があります。

実際、処理がどのように違うかというと、 positionを変更した場合にはそのままpositionの値を変更しているだけなのですが、 MovePositionを呼び出した場合には変位と移動時間から速度を逆算して、 その速度で移動するように設定しているようです。 isKinematicがtrueの状態なので次の更新処理時には速度は再度0に戻ります。

おわりに

案外、物理演算まわりは調べてみると複雑ですね。
特にKinematicが絡んでくるとややこしい。

知っていることを書くだけのつもりが、書いてみると意外と知らないということが多すぎる今日この頃。 結果として、数時間で書くつもりだった記事に一日かかってたり・・・。



執筆時のUnityのバージョン:5.4.0f3

*1:ただし、AddForceの呼び出しではvelocityの値は即座に変化せず、内部で値が保持されたのちに物理演算時に反映されるようです