今回はUnity初心者の方向けの内容で、Rigidbodyの使い方を一通りおさらいしていくという内容です。
Unityでゲームを作っているとRigidbodyコンポーネントをよく使いますが、初心者のうちはゲームオブジェクトが変な動きになったりして困ることがありますよね。かくいう私もUnityでアクションゲームやFPSを作るのが好きで日頃からRigidbodyのお世話になっているのですが、C#スクリプトを書いていると意外とRigidbodyについてきちんと理解していない部分があって「な、なんでこんな挙動になるんだ…」と困惑してしまうことがよくありました。
しかし公式マニュアルをはじめ色々な資料を調べたり、長年実際に使って試行錯誤したりしているうちにRigidbodyについて理解できるようになってきて、今ではそこそこ思い通りに制御できるようになってきました。ここではそんな私が皆さんが苦労しなくても済むようにRigidbodyの使い方についてまとめておきますね。
※なお、この記事では3Dゲーム用のRigidbodyについて解説しています。
Rigidbodyとは?
では初めに「Rigidbodyって何?」という部分について簡単に説明しておきます。
Rigidbodyは一言でいうと「物理演算によってゲームオブジェクトの位置を制御するためのコンポーネント」です。物理演算とは力学的な法則をシミュレーションすることであり、これをゲームに使うと例えば
- 積んである箱をキャラクターが押すと箱が崩れ落ちる
- 敵キャラクターが投げた弾が放物線を描いて地面に着地する
- キャラクターが気絶したときにぐったりさせる
といった挙動を簡単に実現することができます。特にアクションゲームではキャラクターや障害物などに物理的な動きをさせたい場合が多いので、Rigidbodyを活用すると便利です。
Rigidbodyコンポーネントの設定項目について
次にRigidbodyコンポーネントの設定項目について説明します。3Dゲーム用のRigidbodyコンポーネントには次のような設定項目があります。
- 質量
- 抗力
- 回転抗力
- 重力を使用
- Is Kinematic
- 補間
- 衝突判定
- Constraints
それぞれ詳しく見ていきましょう。
質量
物体の質量。単位はKgです。
抗力
Rigidbodyの移動に適用する空気抵抗の大きさ。大きい値を指定するとゲームオブジェクトが動きにくくなります。
回転抗力
Rigidbodyの回転に適用する空気抵抗の大きさ。大きい値を指定するとゲームオブジェクトが回転しにくくなります。
重力を使用
ONにするとRigidbodyに重力が適用されます。重力の影響を無視したい場合はOFFにしましょう。
Is Kinematic
Is KinematicオプションをONにするとRigidbodyが物理演算の影響を受けなくなります。つまり、おおざっぱに言うとRigidbodyがついていないゲームオブジェクトと同じような挙動になります。
一時的に物理演算を無効化したい場合に便利です。
補間
場合によってはRigidbodyの動きがカクカクすることがあるため、それを改善するためのオプションが用意されています。
- なし
- 補間
- 外挿
補間なし
デフォルト設定です。補間を使用しませんが処理が軽いです。
補間
前フレームのTransform に基づいてTransformを補間します。一般的にはこちらを使えばOKでしょう。
外挿
次のフレームのTransformを予測してTransformを補間します。
衝突判定
後で説明するRigidbodyのすり抜け問題を解決するためのオプションです。次の4種類の衝突判定モードから選択することができます。
- 非連続的
- 連続的
- 連続的かつ動的
- Continuous Speculative
非連続的
デフォルトのモードです。最も処理が軽いですが、他のゲームオブジェクトに衝突した場合にすり抜けが発生する場合があります。
連続的
すり抜けを解消するためのモードの一つです。衝突相手がRigidbodyを持たないゲームオブジェクトの場合、すり抜けないようにすることができます。
ただし、もし衝突相手がRigidbodyを持っている場合は依然としてすり抜けが発生する場合があります。
連続的かつ動的
こちらもすり抜けを解消するためのモードですが、こちらは衝突相手がRigidbodyを持っている場合であってもすり抜けないようにすることができます。
ただし衝突相手が高速で回転している場合は依然としてすり抜けが発生する場合があるほか、4つのモードの中で処理が最も重いです。
Continuous Speculative
こちらは上の「連続的かつ動的」モードの特性に加えてオブジェクトの回転も考慮します(このモードは実質「連続的かつ動的」モードの上位互換のようなものです)。これを使えば物理演算によるすり抜けはほぼ解消できるでしょう。
ただし、このモードでは「見かけ上は衝突していないのに衝突したような挙動になる」可能性があります。
Constraints
Rigidbodyに制約を与えます。例えば「位置を固定」のXにチェックを入れると、物理演算によってX方向に移動しなくなるといった具合です。ある方向には動かしたくない・回転させたくないという場合に活用しましょう。
C#スクリプトからRigidbodyを制御する方法
お次はC#スクリプトからRigidbodyを制御する方法について説明していきます。C#スクリプトでRigidbodyを制御するときによく使う関数は次の3つです。
- AddForce
- AddTorque
- AddForceAtPosition
それぞれ詳しく見ていきましょう。
Rigidbodyに力を加える:AddForce関数
まず一つ目はRigidbodyに力を加える「AddForce」関数です。この関数については下記の記事で詳しい話をしているのですが、一応ここでも簡単に説明しておきます。
AddForceを使うとRigidbodyに力を加えて動かすことができるので最も重要な関数です。サンプルスクリプトは次の通り。
using UnityEngine; public class ExampleClass : MonoBehaviour { [SerializeField] Rigidbody rb; [SerializeField] float thrust = 1.0f; void FixedUpdate() { rb.AddForce(transform.forward * thrust, ForceMode.Force); } }
AddForce関数の第一引数には力(ベクトル)、第二引数には後述のForceModeを指定します。このサンプルでは(後で説明する)FixedUpdate関数内でAddForceを呼び出し、ゲームオブジェクト前方に継続的に力を加えるような処理になっています。
ForceModeについて
さてAddForceの第二引数ではForceModeという「力の加え方の方式」を指定します(※省略可能)。3Dの場合は次の4つのモードがあり、指定するモードによって全く違う力の加え方になります。
- Force
- Impulse
- Acceleration
- VelocityChange
Force
Rigidbodyの質量を考慮して力を加えます。継続的に力を加える際に使う最も一般的なモードで、引数を省略するとこのモードが適用されます。
Impulse
Rigidbodyの質量を考慮して瞬発的な力を加えます。1回呼び出すだけで力を加えられるので、例えばジャンプなどの挙動を実現するのに向いています。これもよく使うモードの一つです。
Acceleration
Rigidbodyの質量を無視して加速力を加えます。質量の大きさにかかわらず直接加速力を制御したい場合に使うと良いらしいのですが、個人的にはほとんど使ったことがありません。
VelocityChange
Rigidbodyの質量を無視して速度を変更します。1回の呼び出しで即座に速度が変更されます。
Rigidbodyに回転力を加える:AddTorque
二つ目はRigidbodyに回転力を加える「AddTorque」関数です。Rigidbodyを物理的に回転させたい場合に便利。使い方はAddForceと同じです。
ある一点に力を加える:AddForceAtPosition
三つ目はRigidbodyのある一点に力を加える「AddForceAtPosition」関数です。特定の点に力を加えるという性質上、AddForceとAddTorqueを足したような挙動になります。
使い方はAddForceとほぼ同じですが、第二引数に座標を入力する必要があります。
注意:Rigidbodyに関する処理は原則「FixedUpdate」関数内に書く
さてここで注意点なのですが、Rigidbodyに関する処理は基本的にFixedUpdate関数の中に書くのが無難です。FixedUpdate関数とはフレームに関係なく一定の間隔で実行される関数で、Rigidbodyに継続的に力を加える場合など一般的な物理演算処理を行う場合に正しい結果を得ることができます。
ただ初心者の方からすれば「Update関数じゃダメなの?」と思うかもしれません。実はこれは非常に重要な疑問です。ではUpdate関数とFixedUpdate関数の違いを見てみましょう。
- Update関数:実行間隔が毎回異なる
- FixedUpdate関数:実行間隔が一定
Update関数は1フレームに1回実行される関数です。つまり実行間隔はフレームに依存しており常に変動します(例えばあるフレームの処理がたまたま重かった場合は実行間隔が伸びるなど)。実はUnityの物理演算は時間によって制御されているので、このようなフレーム依存の関数で物理演算処理を行うと正しい結果が得られなくなる可能性があります。
一方でFixedUpdate関数は先ほども書いた通り一定間隔で実行されます。したがって一般的な物理演算の場合はこちらを使うと正しい結果を得られるのです。
ただし、ForceMode.Impulseのように「一瞬だけ力を加える」場合はUpdate関数内で処理を呼び出しても問題ない場合もあります。
Rigidbodyでありがちなトラブルの解決法
最後に、Rigidbodyでありがちなトラブルとその解決方法をご紹介します。ゲーム開発中によく遭遇するRigidbodyのトラブルとしては次の2つがあります。
- 床や壁をすり抜けてしまう
- 壁などに引っかかる
それぞれ対処法を見ていきましょう。
床や壁をすり抜けてしまう場合
まず最もよくありがちなトラブルは、Rigidbodyをつけたゲームオブジェクトが床や壁をすり抜けてしまうことです。このトラブルの主な原因としては次の2つがあります。
- コライダーがないか、コライダーがトリガー扱いになっている
- Rigidbodyが高速で移動している
コライダーがないか、コライダーがトリガー扱いになっている場合
一つ目は単純なミスの場合が多いのですが、衝突するゲームオブジェクト・衝突されるゲームオブジェクトのどちらか(または両方)について
- コライダーがついていない
- コライダーの「トリガーにする」オプションがONになっている
のどちらかが当てはまっているとすり抜けが発生します。この場合はコライダーをきちんと設定するだけで解決します。
Rigidbodyが高速で移動している場合
次に二つ目は、Rigidbodyが高速で移動していて、かつ衝突されるコライダーが小さい(薄い)場合にもすり抜けが発生することがあります。
この場合はRigidbodyの「衝突判定」オプションで「連続的」を設定すれば解決します。
壁などに引っかかる場合
次にRigidbodyを使ってキャラクターを移動させるといった場合に、キャラクターが壁に引っかかって落下しない場合があります。
この場合は、たいていキャラクターについているコライダーと壁のコライダー両方の摩擦係数を0にすると解決します。ぜひ覚えておいてください。
おわりに
以上、UnityのRigidbodyの使い方について一通りご説明しました。RigidbodyはUnityでゲームを作るうえでほぼ必須の機能なので、ぜひこの機会に使い方をマスターしていただければと思います。
この記事がゲーム開発のお役に立てば幸いです。