可变跳跃高度

2026-07-10 · 手感

这篇记录可变跳跃高度(variable jump height)的实现——按得越久跳得越高,松开就剪短上升。

需求:为什么需要可变跳跃高度

如果每次跳跃都是固定高度,玩家就没法做”小跳”。平台跳跃里经常要贴着一个矮平台边缘小跳一下,或者大跳跨过宽的沟,两种操作在同一个跳跃键上。固定高度 = 玩家要么每次都跳满,要么根本做不到小跳。

可变的做法是让按住时间参与决定:长按跳跃键 = 跳满;轻轻点一下 = 小跳。这样”跳多高”是玩家的操作精度,而不是游戏写死的。

原理:跳跃的物理,以及”控制高度”的两个思路

一次跳跃可以拆成两部分:给一个向上的初速度 v0,然后重力 g 每帧把速度磨掉一点

所以”控制高度”本质上只有两个旋钮:

  1. 改初速度 v0:松开时直接把向上的速度砍掉一半(velocity.y *= 0.5),上升瞬间变慢、立刻到顶。这是一次性的瞬间削减,手感直接、干脆。
  2. 改重力 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;                     // 其余时间(按住上升 / 顶点附近)正常
    }
}

三个分支正好对应上升期的两种状态和下落期:

用前面那条公式算一下两种情况的差别。jumpForce = 10,默认重力加速度按 9.81 算:

同样的初速度,只因为松没松手,跳跃高度从 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 是每物理帧自动生效的持续力,最干净。配合一个下落增重,整个跳跃的起落手感就齐了。