今回のテーマ
オブジェクトを動かす方法
概要
オブジェクトを動かす方法はいくつもあるが、それぞれ特徴があり、やりたい事に応じて適切に選ばなくてはならない。特に「自分が操作して動かすオブジェクト」は、快適な操作感を実現するために注意深く作らなければならない。まずは、動かし方によってどのような特徴があるのかを学んでいく。
プロジェクトの準備
教材の UnityPackage をダウンロードして、プロジェクトにインポートします。ProBuilder パッケージを使っているので、入っていない場合は追加でプロジェクトに加えてください。
試してみましょう
/Assets/3-7 Object Movement/Object Movement シーンを開いて、以下を試してみましょう。
- シーンを実行して、WASD でキャラクターを動かし、階段や坂道を昇り降りしたり、ボールを押したりする
- CharacterController オブジェクトに追加された Character Controller Movement コンポーネントの "Move Method" プロパティを Simple Move から Move に変更して、1. を行ってみる
- CharacterController オブジェクトを削除して Rigidbody プレハブをシーンに設置し、実行して 1. を行ってみる
- Rigidbody プレハブはスペースでジャンプできる(Rigidbody Movement コンポーネントの Ground Layer を適切に設定しないと接地判定が行われない事に注意)
- Rigidbody オブジェクトの Rigidbody Movement コンポーネント・Jump Method プロパティを Velocity から変更してジャンプしてみる
- 上昇中に2段ジャンプした時と、下降中に2段ジャンプした場合の挙動の違いを確認しましょう
- Rigidbody オブジェクトを削除して Transform プレハブをシーンに設置し、実行して 1. を行ってみる
- 当たり判定が行われないことを確認しましょう
- Transform オブジェクトに Rigidbody コンポーネントを追加し、実行して 1. を行ってみる
- 当たり判定が行われることを確認しましょう
以降でこれらの挙動の違いについて解説します。
コンポーネント
オブジェクトを動かすためのコンポーネントは主に以下がある。
- Transform
- Rigidbody (2D では Rigidbody2D)
- Character Controller (2D では使えない)
他に Animator Controller, Navmesh Agent でもオブジェクトを動かすことがあるが、これらは「物理エンジンによる動きではない」として今回は扱わない。
基本事項
まず、最も基本的な前提として、以下を承知しておかなければならない。
- GameObject A と GameObject B が Collider コンポーネントによる衝突をするためには、A と B のどちらか(または両方)に Rigidbody コンポーネントが追加されていなければならない
動き方の特徴
各コンポーネントのメソッド・プロパティによって、オブジェクトの挙動は以下のように異なっている。
コンポーネント | メソッド/プロパティ | 重力の影響 | 力学的 | 機能 | Rigidbody を押せるか | 特徴 |
---|---|---|---|---|---|---|
Transform | position ☆ | - | × | 指定した座標に移動する | 押せない | |
Transform | Translate() ☆ | 受けない | × | 指定したベクトルに移動する | 押せない | |
Rigidbody | AddForce()☆ | 受ける | 〇 | 指定したベクトルに力を加える | 押せる | 第2引数で力の加え方を変えられる |
Rigidbody | velocity☆ | - | × | 速度ベクトルを指定したベクトルに変更する | 押せる | 速度ベクトルをいきなり変えるということは力学的ではない |
Rigidbody | MovePosition() | - | △ | 指定した座標に移動する | 押せる | Rigidbody が Kinematic か否かで異なる挙動をする |
Rigidbody | position | - | × | 指定した座標に移動する | - | テレポートするため、移動元→移動先の間の当たり判定は無視される |
Rigidbody | AddRelativeForce() | 受ける | 〇 | 指定したベクトルに力を加える | 押せる | 指定したベクトルをローカル座標系として扱う |
Character Controller | Move()☆ | 受けない | × | 指定したベクトルに移動する | 押せない | 戻り値で「どの面で他のオブジェクトと接触しているか」を取得できる |
Character Controller | SimpleMove()☆ | 受ける | × | 指定したベクトルに移動する | 押せない | Y 軸方向に移動させることはできない。空中で動きを制御することはできない。戻り値で「接地しているか」を取得できる。 |
各コンポーネントの特徴
それぞれの「コンポーネント」としての特徴を挙げていく。
Transform
- 物理・力学とは無関係にオブジェクトを移動させることができる
- Rigidbody がなくてもオブジェクトを移動させられるが、その場合は衝突・接触相手に Rigidbody がないとコライダー同士が衝突しても当たり判定をしないことに注意しなければならない(このミスにより「当たり判定されない」「コライダーをすり抜ける」ことは初心者にありがちです)
Rigidbody
- 物理・力学に従った挙動をさせられる
- Kinematic に設定すると、物理エンジンによる挙動をしない
- 例えば Navmesh Agent によって移動する GameObject に Rigidbody を追加したい場合は、Kinematic にすることで物理エンジンによってではなく Navmesh Agent によって移動させることができる
- Sleep ではなく WakeUp 状態である時は常に物理計算をしているため、無暗に追加すると CPU の負荷を高めてしまう可能性がある
Character Controller
- Rigidbody/Collider なしで衝突判定させることができる(Rigidbody/Collider がなくても他オブジェクトの Collider に衝突する)
- 坂道 (slope) を登ることができる
- Slope Limit により「登れる角度」を制限できる
- 段差 (step) を登ることができる
- Step Offset により「登れる高さ」を制限できる
- 衝突判定のコールバック関数には OnCollisionEnter() ではなく OnControllerColliderHit() を使う
- OnControllerColliderHit は OnCollisionEnter() とは異なり、接触している限り呼ばれ続ける
- Collider とは異なり、Exit や Stay を検出することはできない
IsGrounded プロパティにより接地フラグを取得できるが、挙動がわかりづらく使いづらい
使用例
典型的な使用例を以下に挙げる。
- Transform による移動は、瞬間移動させたい時や当たり判定が必要ない場合に使う。例えば、2D ゲームでの(当たり判定のない)背景のスクロールは Transform.Translate() を使うのが簡単で負荷も少ない。
- Rigidbody による移動は、力学的に正しい挙動をさせたい時に使う。例えば、慣性が働くような動きをさせたい場合は Rigidbody.AddForce() を使うのが簡単である。また、自由落下させたり、衝突を発生させたい場合も Rigidbody を使うのが簡単である。Physics Material を使うことで摩擦係数・反発係数を設定することもできる。
- Character Controller は、力学的な挙動が必要ない場合や、ジャンプをしない場合は使いやすい。坂道や段差を登らせたい場合も、それ用の機能があるので使いやすい。