用射线检测地面
本课属于 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,而且可以直接在那里施加一次性冲量,就像“使用冲量跳跃”中那样。
挑战
中等让青蛙只能从地面起跳。
- 添加
bool IsGrounded(),使其返回Physics.Raycast(transform.position, Vector3.down, 0.85f):青蛙的半径为 0.75,再加上 0.1。 - 在
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 在线编辑器