14 / 22 · 9 分钟
跳跃,以及让它正确的落地检测
简短回答
怎么让角色跳跃,并阻止它在半空中再次起跳?
跳跃本身只有一行代码;难的是判断脚什么时候踩到了东西。用角色正下方的一个小球去和 Ground 层做检测,只有检测返回 true 时才允许起跳。绝不要去判断竖直速度是否为零——在抛物线最高点它同样是零。
让角色跳起来只有一行代码。让它只在踩着东西的时候才跳,才是吃掉你一整个下午的部分 —— 原因在于 Unity 不会告诉你脚底碰到了什么,得你自己去问。
动态图解
球体位于脚下,每帧都给出答案。旁边的两个时间窗口,正是让操作感觉公平而不苛刻的关键。
跳跃本身
The one line, for each of the two movement systems
// CharacterController: you own gravity, so you own the jump too.
verticalSpeed = Mathf.Sqrt(jumpHeight * -2f * gravity);
// Rigidbody: hand it an impulse and let physics do the arc.
rb.AddForce(Vector3.up * jumpForce, ForceMode.Impulse);接地检测
错误的做法是判断竖直方向的速度是否接近零。每次跳到最高点时它都接近零,于是角色恰好在顶点获得第二次跳跃 —— 这就是没人想要的经典漂浮感二段跳。
- 1
新建 Ground 图层:,然后把地板和每一个平台都放到该图层上。
- 2
给角色创建一个空的子物体,命名为 GroundCheck,放到脚底正下方 —— 偏下几厘米,不要正好贴着脚。
- 3
每帧在那个点检测一个小球是否与 Ground 图层重叠。所谓站在东西上,就是这个小球碰到了东西。
Jump.cs — the whole thing
using UnityEngine;
[RequireComponent(typeof(CharacterController))]
public class Jump : MonoBehaviour
{
public Transform groundCheck;
public LayerMask groundLayer;
public float checkRadius = 0.25f;
public float jumpHeight = 2f;
public float gravity = -20f;
// A jump the player asked for a moment too early still counts.
public float bufferTime = 0.12f;
// So does one asked for a moment after walking off an edge.
public float coyoteTime = 0.12f;
CharacterController cc;
float verticalSpeed, lastGrounded, lastPressed;
void Awake() => cc = GetComponent<CharacterController>();
void Update()
{
bool grounded = Physics.CheckSphere(
groundCheck.position, checkRadius, groundLayer, QueryTriggerInteraction.Ignore);
if (grounded) lastGrounded = Time.time;
if (Input.GetButtonDown("Jump")) lastPressed = Time.time;
if (grounded && verticalSpeed < 0f) verticalSpeed = -2f;
if (Time.time - lastPressed < bufferTime && Time.time - lastGrounded < coyoteTime)
{
verticalSpeed = Mathf.Sqrt(jumpHeight * -2f * gravity);
lastPressed = lastGrounded = -99f; // spend both, so it fires once
}
verticalSpeed += gravity * Time.deltaTime;
cc.Move(Vector3.up * verticalSpeed * Time.deltaTime);
}
// Draw the check in the Scene view, so you can see what it is testing.
void OnDrawGizmosSelected()
{
if (groundCheck == null) return;
Gizmos.color = Color.green;
Gizmos.DrawWireSphere(groundCheck.position, checkRadius);
}
}这两个计时器不是可有可无的点缀
Coyote time —— 玩家在走出平台边缘两帧后才按 Jump,说明他就是想跳。没有它,游戏就像在故意无视你。
Jump buffer —— 玩家在落地前一刻按 Jump,是想一落地就跳。没有它,这次按键被白白丢掉,玩家就会骂手感。
两者都在 0.1 秒左右。谁都感觉不到它们,但操作是紧实还是稀烂,差别一大半就在这两者身上。
做出这个的那一课
移动、跳跃并落到方块上
这篇指南单独成篇,是一份可以直接照着做的配方。在课程里,同样的东西会作为贯穿全部五个章的那个项目的一部分来搭建 —— 角色与战斗,第 02 课。