第11回は、前回書き換えたPlayerMoveを詳しく読み解くコード解説回です。Character Controller方式とRigidbody方式の違いを比べ、なぜUpdateとFixedUpdateへ処理を分けたのかを理解します。
コードをコピーして動かすだけでなく、入力、移動方向、接地判定、ジャンプ、物理移動がどの順番でつながっているかを説明できるようになります。
Character ControllerとRigidbodyの違い
| 比べる項目 | Character Controller | Rigidbody |
|---|---|---|
| 移動方法 | controller.Move()で指定量を動かす | linearVelocityやAddForce()で動かす |
| 重力 | 自分で計算して追加する | Use GravityでUnityが計算する |
| ジャンプ | 上方向の移動速度を自分で管理する | 上方向へ力を加えられる |
| 衝突 | キャラクター向けの簡単な当たり判定 | 質量や反発を含む物理演算 |
| 必要なCollider | Component自体がカプセル形を持つ | Capsule Colliderなどを別に付ける |
| 処理を書く場所 | 主にUpdate() | 入力はUpdate()、物理移動はFixedUpdate() |
Character Controllerは操作しやすく、FPSの基本移動に向いています。Rigidbodyは重力、落下、押される動き、力を使うジャンプなど、物理演算と組み合わせやすい方法です。
ゲームの作り方によって選びます。今回は物理の力でジャンプすることを学ぶため、Rigidbodyへ変更しました。
Componentの役割がどう変わったか
Character Controller方式では、1つのComponentが移動とカプセル形の衝突をまとめて担当していました。
Rigidbody方式では役割を分けます。
変数を役割ごとに分けて読もう
public float moveSpeed = 4f;
public float jumpForce = 6f;
public float turnSpeed = 10f;
public Animator animator;
public Transform modelRoot;
public Transform groundCheck;
public float groundDistance = 0.25f;
public LayerMask groundLayer;
Rigidbody rb;
Transform cameraTransform;
Vector3 move;
bool jumpRequested;
bool isGrounded;
moveは進む方向、jumpRequestedは「次の物理更新でジャンプする」という予約、isGroundedは床に立っているかを保存します。
StartでRigidbodyとCameraを覚えよう
void Start()
{
rb = GetComponent<Rigidbody>();
cameraTransform = Camera.main.transform;
}
GetComponent<Rigidbody>()は、Playerに付いているRigidbodyを探してrbへ保存します。以後は毎回探さず、rbという短い名前で使えます。
Camera.main.transformはMain Cameraの向きです。カメラの前後左右を基準にしてWASD移動を作るために保存します。
Updateではキー入力を読む
float horizontal = 0f;
float vertical = 0f;
if (Keyboard.current.aKey.isPressed)
{
horizontal -= 1f;
}
if (Keyboard.current.dKey.isPressed)
{
horizontal += 1f;
}
if (Keyboard.current.sKey.isPressed)
{
vertical -= 1f;
}
if (Keyboard.current.wKey.isPressed)
{
vertical += 1f;
}
horizontalは左右、verticalは前後です。AとSはマイナス方向、DとWはプラス方向へ値を変えます。
キー入力は画面の更新に合わせて変化するため、Update()で読みます。スペースキーの「今押された」という入力もUpdateで調べます。
Cameraを基準に移動方向を作る
Vector3 forward = cameraTransform.forward;
Vector3 right = cameraTransform.right;
forward.y = 0f;
right.y = 0f;
forward.Normalize();
right.Normalize();
move =
forward * vertical +
right * horizontal;
move = Vector3.ClampMagnitude(move, 1f);
カメラのforwardをWとS、rightをAとDへ使います。これにより、カメラが向いている方向を前として歩けます。
カメラが上下を向いていてもPlayerが空中へ進まないように、Yを0にします。最後にClampMagnitudeで長さを1以下にし、斜め移動だけ速くなることを防ぎます。
CheckSphereで床を調べる
isGrounded = Physics.CheckSphere(
groundCheck.position,
groundDistance,
groundLayer,
QueryTriggerInteraction.Ignore);
groundCheck.positionが球の中心です。groundDistanceが球の半径です。groundLayerに選んだ床だけを調べます。ジャンプを予約する理由
if (Keyboard.current.spaceKey.wasPressedThisFrame &&
isGrounded)
{
jumpRequested = true;
}
スペースキーを押した瞬間、さらに床に立っているときだけjumpRequestedをtrueにします。空中ではisGroundedがfalseなので二段ジャンプできません。
入力を読むUpdateと、物理を動かすFixedUpdateは呼ばれるタイミングが違います。Updateで見つけたジャンプ入力を失わないように、boolへ保存してFixedUpdateへ渡します。
Update
└─ Spaceが押された
└─ jumpRequested = true
FixedUpdate
└─ jumpRequestedを確認
└─ AddForceでジャンプ
└─ jumpRequested = false
FixedUpdateでRigidbodyを動かす
Vector3 currentVelocity =
rb.linearVelocity;
rb.linearVelocity =
new Vector3(
move.x * moveSpeed,
currentVelocity.y,
move.z * moveSpeed);
XとZにはWASDから作った移動速度を入れます。Yには現在のcurrentVelocity.yを残します。
Yを毎回0にすると、ジャンプの上向き速度と重力による落下速度が消されます。現在のY速度を残すことで、横移動しながら自然に上昇・落下できます。
Character Controllerではcontroller.Move(move * speed * Time.deltaTime)のように移動量を渡しました。Rigidbodyでは1秒あたりの速度をlinearVelocityへ設定するため、ここではTime.deltaTimeを掛けません。
AddForceでジャンプする
if (jumpRequested)
{
rb.AddForce(
Vector3.up * jumpForce,
ForceMode.Impulse);
jumpRequested = false;
}
Vector3.upは真上、jumpForceは力の大きさです。ForceMode.Impulseは一瞬の衝撃として力を加えるため、ジャンプに向いています。
力を加えた後でfalseへ戻すため、1回のキー入力につきAddForceは1回だけ実行されます。
よくある違いと間違いを確認しよう
ジャンプアニメーションを調整しよう
次回はisGroundedをAnimatorへ渡す流れを確認し、Jump、Midair、Landの切り替えを調整します。

コメント