Menu
CoddyTech

用射线检测地面

本课属于 Coddy Unity 学习旅程的“物理与游戏玩法”单元。第 37 课,共 71 课。

跳跃只能从地面开始。“站在地面上”通过接触法线判断这一点,并由碰撞消息持续更新。射线可以随时就地回答“我正站在某个物体上吗?”。从中心笔直向下发射射线,长度要比中心到底部的距离稍长一些。这个玩家是一个高 2 个单位的胶囊体,因此底部在中心下方 1 个单位处,射线长度为 1.1:

public float jumpForce = 5f;
private Rigidbody rb;

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

void Update()
{
    if (Input.GetKeyDown(KeyCode.Space) && IsGrounded())
    {
        rb.AddForce(Vector3.up * jumpForce, ForceMode.Impulse);
    }
}

bool IsGrounded()
{
    return Physics.Raycast(transform.position, Vector3.down, 1.1f);
}

多出的 0.1 是余量。物体静止在地面上时,可能会稍微悬在地面上方,也可能会稍微陷入地面,因此长度正好为 1 的射线在某些帧中会检测不到地面。长度大得多也不对:如果长度是 3,玩家在空中上升 2 个单位后就能再次跳跃;如果射线长度为 0,它总能检测到地面,因此每次按下按键都会触发跳跃。

射线从玩家自身的碰撞体内部开始,而射线会跳过碰撞体,因此永远不会检测到玩家自身。要点是,在 Update() 中读取按键,因为 GetKeyDown 只会在一帧内为 true,而且可以直接在那里施加一次性冲量,就像“使用冲量跳跃”中那样。

challenge icon

挑战

中等

让青蛙只能从地面起跳。

  1. 添加 bool IsGrounded(),使其返回 Physics.Raycast(transform.position, Vector3.down, 0.85f):青蛙的半径为 0.75,再加上 0.1。
  2. 在 Update() 中,将 && IsGrounded() 添加到针对 Space 的 if 条件语句中。

按“开始”,然后按两次 Space:青蛙只跳一次。接着按“检查”。

亲手试试

using UnityEngine;

public class Hopper : MonoBehaviour
{
    public float jumpForce = 6f;
    private Rigidbody rb;

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

    void Update()
    {
        if (Input.GetKeyDown(KeyCode.Space))
        {
            rb.AddForce(Vector3.up * jumpForce, ForceMode.Impulse);
        }
    }
}

物理与游戏玩法中的所有课

自己练习: Unity 在线编辑器