Unityで物理演算の動きに挑戦していましたが、オブジェクトのTransformを使ったUpdateでの処理とはかなり考え方が違うところがあったのでメモを残そうと思います。
移動の関数の使い方だけではなくオブジェクトの構造などもそのままではうまくいかないようです…。
少しずつ覚えていこうと思います。
もくじ
Transformでの移動処理との違い
フレームの処理
まず一番の違いは、1つのフレーム内での処理順がUpdateのものとは異なることです。
物理演算の移動系の処理を行ったとしても、実際に移動が行われるのはFixedUpdateがすべて終わった後です。
衝突など物理演算ならではの動きをする場合、処理後の座標や回転などの値は次のフレームになるまで分かりません。
そのため、Script Execution Orderを設定してスクリプトの実行順を変えたとしてもオブジェクトの移動処理の結果を同じフレーム内で使うことはできません。
親子関係の問題
Rigidbodyのついたオブジェクト同士を親子にするのは基本的に非推奨とのことです。
子のIsKinematicがtrueであれば親に追従して移動させることはできますが、IsKinematicだったとしても移動・回転の関数を使うと親に追従しなくなってしまいます。
(そもそもTransformにはあるlocalPositionとかlocalRotationがRigidbodyにはなくローカル座標で動かすことができません…)
Rigidbodyの機能
上記2つの問題があるため、RigidbodyでTransformのようなオブジェクトの位置同期をするのは難しいです。
代わりにRigidbodyにはJoint(ジョイント)という機能があります。Rigidbodyのついたオブジェクト同士を結合して1つのオブジェクトのように動かす機能です。
結合方法にも「単純にくっつける」「関節のように一ヶ所でくっつける」「ばねや振り子のようにぶらぶらさせる」などいくつかの方法があります。
Rigidbodyではこちらの機能を使ってオブジェクト同士の位置関係を制御することになります。
RigidBodyの移動と回転
まずJointの前に移動や回転に関わる処理がどのようになっているか見ていこうと思います。
座標で移動
①Rigidbody.position = Vector3 移動座標
いわゆるワープ移動で、離れた座標に移動するのに使えます。Transform.positionのRigidbody版みたいなものかなと思います。
②Rigidbody.MovePosition(Vector3 移動座標)
移動時に補間がかかるとのことで、継続して移動するオブジェクトにはこのMovePositionを使うのが良いようです。(※MovePositionは主にIsKinematicで使う用なのか、IsKinematicがfalseの場合は補間がかからず毎フレームでワープ移動するとのことです)
方向で移動
どちらもIsKinematicでは使えないので注意です。
③Rigidbody.AddForce(Vector3 移動方向)
オブジェクトに押す力を加えます。力を加えるので、オブジェクトが動き出すのにも止まるのにも重力と質量に従った慣性がかかります。
④Rigidbody.Velocity = Vector3 移動方向
オブジェクトの移動方向と速度を直接変更します。慣性をかけずに移動させたりオブジェクトを急停止したりするのに使えます。
Quaternionの指定で回転
⑤Rigidbody.rotation = Quaternion 目的の向き
回転させるというより向きをその角度に変えるという感じです。Transform.rotationのRigidbody版かなと思います。
⑥Rigidbody.MoveRotation(Quaternion 目的の向き)
②の回転版と考えれば良さそうです。
トルク(回転力)で回転
移動と同じくIsKinematicでは使えないので注意です。
⑦Rigidbody.AddTorque(Vector3 回転力)
回転力を加えるという感じで、慣性をかけて回転できますが特定の角度になった時にピタッと止めるのは難しいです。やりたい場合は、指定した値を越えたときに⑧を使って止めることになるかと思います。
⑧Rigidbody.angularVelocity = Vector3 回転力
④の回転版という感じで、慣性をつけずに回転の速さや停止を指定します。
ちなみにトルクって何?っていう感じですが、計算としては
回転の軸(y軸回転ならVector3.up) * 回転の強さ(float) * 回転方向(floatで正なら右回転、負なら左回転)
という感じにすれば良いようです。
RigidbodyのJoint
JointはRigidbodyのついたオブジェクト同士を結合するコンポーネントです。
またJointの種類によっては何もない空間の座標にくっつけることもできます。
使い方とポイント
Rigidbodyのついたオブジェクト2つを結合したい場合に使えます。2つを親子のようなものと考えた時の子の方のオブジェクトにJointのコンポーネントをつけます。(ヒエラルキーで親子関係にはしません)
JointのConnected Bodyに親側のオブジェクトを指定すれば結合されます。
注意点がいくつかあります。
- 親子どちらかがIsKinematicでは使えない
- Rigidbody.MoveRotationなどで値を指定して子を個別に動かすことはできない
- Rigidbodyの結合によってMassやCenter Of Massも結合される
- 子は完全に親の相対的位置にあるのではなく強い力によってズレることがある
子を個別に動かしたい場合は、各Jointの設定の範囲内でAddForceなどで動かすことになります。
MassやCenter Of Massは初期設定では親に追加されるような感じで自動で計算されます。自分で指定することもできますが、親が子の影響を全く受けないようにするのは難しいのかな…と思います(極力小さい値にすることはできます)。
子のCenter Of Massを親の位置と同じにするには、Automatic Center Of Massのチェックを外してローカル座標で相対的に親の位置を指定します。

また子側のFreeze Rotationなどが親に反映されるので要注意です。
Joint全般の難しさとして、結合といってもTransformでの親子のように子が親の相対的座標にあるわけではなく、強い力をかけるとズレが生じるという点があります。
親側のオブジェクトが壁や床に強くぶつかった時など、一瞬ですがわずかにズレが発生します。
親に強い回転をかけた時なども遠心力で離れる(?)ような現象が発生します。
特にJointを複数つなげて使用した場合に起こりやすく、親から1つ目のJointはほぼズレが発生しませんが2つ目以降のJointではそれなりに分かります。
子のMassを小さくすることで緩和はできますが完全ではないです。
Fixed Joint
単純に結合させるジョイントです。Transformの親子関係の代わりはこれを使うことになります。
かかった力の強さによって子オブジェクトが結合から外れて飛ぶようにすることもできるので、振り払ったり物を投げたりする動作にも使えるようです。
Hinge Joint
指定した軸でオブジェクトを固定して動かせるジョイントです。
ドアの蝶番や扇風機の羽根みたいな動きを作ることができます。空間に固定して動かすこともできます。
回転の軸が1軸しか設定できないので、2軸以上にしたい場合はHinge Jointを二重にします。(上述のズレには注意です!)
Hinge Joint個別の記事もまとめたのでよろしければそちらも見てみてください!
Spring Joint
離れた2つのRigidbodyをバネみたいな動きでつなぐジョイントです。
紐やバネでぶら下がるパーツみたいなものが作れそうです。
こちらはまだ試していないので、使ってみることがあったら追記しようと思います。
Character Joint
軸ごとの角度制限が可能な球体関節のジョイントとのことです。
ソーセージのつなぎ目とか人体の関節みたいな動きを作ることができるようです。
こちらも未使用なので使ったら追記しようと思います。
Configurable Joint
好みのジョイントの設定を細かく設定できます。
設定項目がとても多く、2軸で動く関節やスライド運動などを作ることもできます。
スライド運動を作る方法について別の記事にまとめたのでよろしければ見てみてください!
Transformを使えるケース
Jointは便利ですが、必ずしも全部のオブジェクトをRigidbody+Jointで制御する必要はないのかなと思います。
- 子オブジェクトの動きはローカル座標内で完結していて物理演算オブジェクトの影響を受けない・与えない
- 子オブジェクトの動きに物理演算での動きは必要ない
- 子オブジェクトに接触判定は必要ない、または接触判定は必要だがIsTriggerで大丈夫
以上のような場合であれば通常の子オブジェクトにしてTransform.localPositionやTransform.localRotationで動かしてしまっても大丈夫なんじゃないかなと思います。
子オブジェクトの動き自体はFPSに合わせた方が良いと思うので、Time.DeltaTimeで調整するかFixedUpdateで処理すればOKかと思います。
すべての動きを物理演算だけにするのはさすがに難しい(MassやGravityなどが複雑に影響してしまい逆にシンプルな挙動を作ることができない)ので、上手に使い分けるのが大事なのかなと思います。
まとめ
Transformでの親子関係をそのままRigidbodyに持っていって使いたい!と思ってしまうと沼にはまる気がします…。
代替の機能はあるけれど根本的に違うものと思って色々動きのテストをしてみるのがオススメです。