Menu
CoddyTech

手動で重力を加える

CoddyのUnityジャーニー「本格的なゲームを作る」セクションの一部。レッスン 39/60。

CharacterController は、スクリプトが落下させた場合にのみ落下し、実際の落下では速度が増していきます。重力によって、落下速度は毎秒約9.81単位ずつ、1秒ごとに増加します。そのため、スクリプトでは垂直速度をフィールドに保持し、毎フレーム gravity * Time.deltaTime を加算して、歩行時と同じ Move でその速度分だけ移動します。

public float gravity = -9.81f;
float verticalSpeed;

void Update()
{
    if (controller.isGrounded && verticalSpeed < 0f)
    {
        verticalSpeed = -2f;   // 接地中:落下を止め、下向きの弱い力を維持する
    }
    verticalSpeed += gravity * Time.deltaTime;

    Vector3 move = new Vector3(Input.GetAxisRaw("Horizontal"), 0f, Input.GetAxisRaw("Vertical")) * speed;
    move.y = verticalSpeed;
    controller.Move(move * Time.deltaTime);
}

Time.deltaTime は2つの役割で使われます。gravity * Time.deltaTime はこのフレームで増加する速度で、速度に Time.deltaTime を掛けた値はこのフレームで移動する距離です。速度はフレームをまたいで保持する必要があるため、フィールドにします。ローカル変数では毎フレーム0から始まってしまいます。静止状態から落下すると、コントローラーの速度は0.5秒後に毎秒-4.9単位、1秒後に-9.81単位になります。

落下と同じくらい、リセットも重要です。床の上では Move によってコントローラーの動きは止まりますが、フィールドの値は止まりません。リセットしないと、プレイヤーが立っている間も値が増え続け、次に崖から踏み外したときに最高速度で落下します。0ではなく小さな負の数にリセットするのは、Move のたびに床へ押し付けるためです。フレームレートが高い場合、1フレーム分の重力による押し付けは Min Move Distance より短くなることがあり、その場合 Move は何もせず、isGrounded は false になります。isGrounded は Move の前に読み取られるため、前のフレームの Move の状態を示します。

challenge icon

チャレンジ

中級

ダイバーに重力を加えて、落下速度が上がるようにしましょう。

  1. Update() の先頭で、controller.isGrounded かつ verticalSpeed < 0f のとき、verticalSpeed = -1f; を設定します。
  2. gravity * Time.deltaTime を加算し、Mathf.Max を使って -maxFall 以上に保ちます。
  3. controller.Move の行の前に、move.y = verticalSpeed; を設定します。

「プレイ」を押して塔から歩いて落ちましょう。落下速度が毎秒10ユニットまで上がります。その後、「確認」を押します。

自分で試してみよう

using UnityEngine;

public class Diver : MonoBehaviour
{
    public float speed = 4f;
    public float gravity = -12f;
    public float maxFall = 10f;

    CharacterController controller;
    float verticalSpeed;

    void Start()
    {
        controller = GetComponent<CharacterController>();
    }

    void Update()
    {
        // 重力: 地面にいるときはverticalSpeedをリセットし、重力を加え、落下速度を制限して、move.yに設定する

        Vector3 move = new Vector3(Input.GetAxisRaw("Horizontal"), 0f, Input.GetAxisRaw("Vertical")) * speed;
        controller.Move(move * Time.deltaTime);
    }
}

本格的なゲームを作るのすべてのレッスン

自分で練習してみよう: Unityプレイグラウンド