Menu
CoddyTech

手动实现重力

本课属于 Coddy Unity 学习旅程的“打造真正的游戏”单元。第 39 课,共 60 课。

只有当你的脚本让 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 出现了两次,分别用于不同的计算:gravity * Time.deltaTime 是这一帧增加的速度,而速度乘以 Time.deltaTime 得到的是这一帧移动的距离。速度必须保存在字段中,因为它需要从一帧延续到下一帧;局部变量每帧都会重新从 0 开始。从静止状态下落,半秒后控制器的速度为每秒 -4.9 个单位,一秒后则为 -9.81 个单位。

重置和下落同样重要。在地面上,Move 会让控制器停下,但不会清除字段中的值:如果不重置,玩家站立时这个值会不断增大,下一次踏出悬崖时就会以全速下落。重置时会设为一个较小的负数,而不是 0,这样每次 Move 仍会将角色压向地面:在帧率较高时,一帧重力产生的下压力可能小于 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 在线编辑器