遊テックプログラミング教室

プログラミングノートNOTE

壁へのめりこみの対処 ~Rigidbody.MovePositionを使った移動~

Rigidbodyで移動・回転・ジャンプするスクリプト

transform.Translate ではなく、Rigidbody.MovePosition を使用して移動させると、物理エンジンとの整合性が保たれ、壁にめりこむ不具合が解消される場合があります。

なお、Rigidbody.MovePosition は物理エンジンと連携して動作するため、原則として FixedUpdate で使用することが推奨されています。

Update と FixedUpdate の違い

Update メソッド

呼び出し頻度: フレームごとに一度呼び出され、フレームレートに依存します。
用途: プレイヤーの入力処理、アニメーション更新、UI更新など、フレームごとに実行する処理に使用します。

FixedUpdate メソッド

呼び出し頻度: 一定の時間間隔(デフォルトでは0.02秒)で呼び出され、物理演算の更新と同期しています。そのため、フレームレートに依存しません。
用途: 物理演算、Rigidbody の移動や力の加算など、物理ベースの処理に使用します。物理エンジンの更新と一致しているため、安定した動作が可能です。

以下のコードは、上下キーで前後に移動し、左右キーで回転、スペースキーでジャンプするサンプルコードです。プレイヤーにアタッチしてください。Rigidbodyコンポーネントも必要です。

using UnityEngine;

public class Player : MonoBehaviour
{
    public float moveSpeed = 5f;      // 移動速度
    public float turnSpeed = 100f;    // 回転速度
    public float jumpForce = 5f;      // ジャンプの力
    private Rigidbody rb;
    private bool isGrounded;          // 地面に接しているかどうかを判定するフラグ

    void Start()
    {
        rb = GetComponent<Rigidbody>();
    }

    void Update()
    {
        // ジャンプ処理 (Updateでキー入力を確認)
        if (Input.GetKeyDown(KeyCode.Space) && isGrounded)
        {
            rb.AddForce(Vector3.up * jumpForce, ForceMode.Impulse);
            isGrounded = false;  // ジャンプ中は地面にいないのでフラグをfalseに
        }
    }

    void FixedUpdate()
    {
        // 前後の移動(上下キー)
        float moveInput = Input.GetAxis("Vertical"); // 上下キーの入力(-1 ~ 1)
        Vector3 moveDirection = transform.forward * moveInput * moveSpeed * Time.fixedDeltaTime;

        // 左右の回転(左右キー)
        float turnInput = Input.GetAxis("Horizontal"); // 左右キーの入力(-1 ~ 1)
        Quaternion turnRotation = Quaternion.Euler(0f, turnInput * turnSpeed * Time.fixedDeltaTime, 0f);

        // Rigidbodyを使った移動と回転
        rb.MovePosition(rb.position + moveDirection);   // 前後の移動
        rb.MoveRotation(rb.rotation * turnRotation);    // 左右の回転
    }

    // 地面との接触を確認
    private void OnCollisionEnter(Collision collision)
    {
        // 地面に触れているか確認(タグを使うのがおすすめ)
        if (collision.gameObject.CompareTag("Ground"))
        {
            isGrounded = true;  // 地面に触れていればフラグをtrueに
        }
    }
}