手動で重力を加える
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 の状態を示します。
チャレンジ
中級ダイバーに重力を加えて、落下速度が上がるようにしましょう。
Update()の先頭で、controller.isGroundedかつverticalSpeed < 0fのとき、verticalSpeed = -1f;を設定します。gravity * Time.deltaTimeを加算し、Mathf.Maxを使って-maxFall以上に保ちます。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プレイグラウンド