可变跳跃高度
这篇记录可变跳跃高度(variable jump height)的实现——按得越久跳得越高,松开就剪短上升。
需求:为什么需要可变跳跃高度
如果每次跳跃都是固定高度,玩家就没法做”小跳”。平台跳跃里经常要贴着一个矮平台边缘小跳一下,或者大跳跨过宽的沟,两种操作在同一个跳跃键上。固定高度 = 玩家要么每次都跳满,要么根本做不到小跳。
可变的做法是让按住时间参与决定:长按跳跃键 = 跳满;轻轻点一下 = 小跳。这样”跳多高”是玩家的操作精度,而不是游戏写死的。
原理:跳跃的物理,以及”控制高度”的两个思路
一次跳跃可以拆成两部分:给一个向上的初速度 v0,然后重力 g 每帧把速度磨掉一点。
- 从起跳到顶点的时间:
t = v0 / g - 最高点高度:
h = v0² / (2g)
所以”控制高度”本质上只有两个旋钮:
- 改初速度 v0:松开时直接把向上的速度砍掉一半(
velocity.y *= 0.5),上升瞬间变慢、立刻到顶。这是一次性的瞬间削减,手感直接、干脆。 - 改重力 g:上升期间只要松开,就把重力加大,让 v0 被更快地磨光,顶点自然变低。这是持续的加速度变化,手感更像”浮起来一段、然后被拽回去”。
两种都行。我用的方案 2(切重力),因为它在 Rigidbody2D 里实现最干净,而且可以顺手再做一个下落增重,两件事共用同一套 gravityScale 机制。
实现
相关参数:
public float jumpForce = 10f; // 起跳初速度
public float baseGravity = 4f; // 平时基础重力(对物理默认重力的倍率)
public float jumpCutMultiplier = 2.5f; // 上升时松开跳:重力 ×2.5,剪短上升
public float fallMutiplier = 2.5f; // 下落时 重力 ×2.5,下坠更快
private bool jumpHeld; // 跳跃键是否还按着
baseGravity 这个数值要澄清一下:它是 gravityScale,也就是对 Physics2D 默认重力的倍率,不是加速度本身。Unity 2D 的默认重力加速度是 -9.81,所以实际加速度 = -9.81 × baseGravity。
在 Update 里记录按键的”按住”状态:
jumpHeld = Input.GetButton("Jump");
核心在 FixedUpdate,每个物理帧根据当前状态决定重力:
void FixedUpdate()
{
if (isDashing)
{
rb.gravityScale = dashGravity; // 冲刺:重力最小,优先级最高
}
else if (rb.velocity.y < 0f)
{
rb.gravityScale = baseGravity * fallMutiplier; // 下落:加大重力,落地干脆
}
else if (rb.velocity.y > 0f && !jumpHeld)
{
rb.gravityScale = baseGravity * jumpCutMultiplier; // 上升中松手:猛增重力,剪短上升
}
else
{
rb.gravityScale = baseGravity; // 其余时间(按住上升 / 顶点附近)正常
}
}
三个分支正好对应上升期的两种状态和下落期:
- 上升 + 按住(落到 else):重力保持小值,向上速度磨得慢,跳得高。这就是”长按空格才能到最高”。
- 上升 + 松开(第三个分支):重力 ×2.5,向上的速度被快速磨光,顶点降低,很快就掉头。这就是”松开就快速往下掉”。
- 下落中(第二个分支):重力 ×2.5,下坠更快,落地更干脆,不会飘。
用前面那条公式算一下两种情况的差别。jumpForce = 10,默认重力加速度按 9.81 算:
- 长按:
g = 9.81 × 4 ≈ 39.2,h = 10² / (2 × 39.2) ≈ 1.27 - 松开:
g = 39.2 × 2.5 ≈ 98.1,h = 10² / (2 × 98.1) ≈ 0.51
同样的初速度,只因为松没松手,跳跃高度从 1.27 变成 0.51,接近砍半。小跳和大跳的差别就是这个。
几个容易搞错的地方
1. gravityScale 是倍率,不是加速度。 一开始容易把 baseGravity 当成”多少米/秒²”直接写。Unity 2D 的默认重力是 -9.81,想得到 4 的加速度,得写 4 / 9.81 ≈ 0.4 这样的倍率——或者反过来,像我这样把倍率设成整数、让调参的人对着手感试。关键是心里要清楚 gravityScale 是乘在全局重力上的。
2. 想改”持续效果”,要动 gravityScale,不是改一次 velocity。 rb.velocity.y *= 0.5 只对那一帧生效,下一帧重力会重新改变速度。要得到一个”持续的、每帧都自动作用的力”,用 gravityScale(Rigidbody2D 每物理帧自动把全局重力 × gravityScale 加进去)最省事。
3. 分支顺序别乱。 下落判定(velocity.y < 0)必须在”上升 + 松手”之前。如果反了,下落时松手会被误判进上升分支。我这版两个倍率恰好都是 2.5,顺序错了也看不出来,但概念上是两种不同的效果,分开写、顺序写对,以后调其中一个不影响另一个。
4. 和土狼时间、缓冲、冲刺的关系。 土狼和缓冲控制的是”能不能起跳”的时机,可变跳跃控制的是”起跳之后飞多高”,两者是正交的——所以这套重力逻辑和之前的宽容机制能共存。冲刺则不同:冲刺期间 isDashing 分支在最前,重力被 dashGravity 完全接管,可变跳跃在冲刺中不生效,这是有意的(冲刺要保证直线不飘)。
小结
可变跳跃高度 = 把”跳跃高度”从固定值变成玩家的操作精度:按住跳满、松开剪短。物理上就是控制”向上的初速度被重力磨掉的速度”,实现上选”切重力”而不是”切速度”,因为 gravityScale 是每物理帧自动生效的持续力,最干净。配合一个下落增重,整个跳跃的起落手感就齐了。