Menu
CoddyTech

手動で重力を加える

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

CharacterControllerは、スクリプトが落下させた場合にのみ落下し、実際の落下では速度が増していきます。重力は毎秒、落下速度に約9.81単位を加えます。そのため、スクリプトでは垂直速度をフィールドに保持し、毎フレーム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から始まってしまいます。静止状態から落下すると、半秒後のコントローラーの速度は毎秒-4.9単位、1秒後には-9.81単位です。

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

challenge icon

チャレンジ

中級

ダイバーは高い塔の上に立っていて、矢印キーで歩きます(すでに実装済みです)。Diver.cs に重力を追加しましょう。Inspector で gravity を -12、maxFall を 10 に設定します。スクリプトには verticalSpeed フィールドがあります。

  • 毎フレーム、verticalSpeed に1秒あたりの gravity が加算され、歩行と同じ Move でその値だけダイバーが下に移動します。
  • 空気抵抗によって長い落下は遅くなります。ダイバーの落下速度は、1秒あたり maxFall ユニットを超えません。
  • コントローラーが接地していて、verticalSpeed が 0 未満の場合は、verticalSpeed を -1 に設定します。

Play を押し、塔から歩いて降りてから、Check を押してください。

自分で試してみよう

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);
    }
}
quiz icon腕試し

このレッスンには短いクイズがあります。レッスンを始めて解答し、進捗を記録しましょう。

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