未接地时coyoteTimeCounter仍设为0.5f?空中多次跳跃问题求助
问题:修复Coyote Time导致的空中多次跳跃问题
我参考Coyote Time实现功能,期望角色离开地面后0.5秒内仅能跳跃一次,但当前代码中,即使角色未处于地面状态,if (isGrounded)分支仍会将coyoteTimeCounter设为0.5f,导致角色可在空中多次跳跃。
我的代码如下:
public class jump : MonoBehaviour { [SerializeField] float coyoteTime = 0.5f,coyoteTimeCounter = 0.0f; [SerializeField] float horizontalSpeed = 7.8f; [SerializeField] float horiMove; [SerializeField] float jumpVelocity = 7.5f; Rigidbody2D rb; [SerializeField] bool isGrounded = false; void Start() { rb = GetComponent<Rigidbody2D>(); } void Update() { horiMove = Input.GetAxis("Horizontal") * horizontalSpeed * Time.deltaTime; rb.transform.Translate(new Vector2(horiMove,0f)); if (isGrounded) { coyoteTimeCounter = coyoteTime; } else { coyoteTimeCounter -= Time.deltaTime; } if (Input.GetKeyDown(KeyCode.Space) && coyoteTimeCounter > 0f) { rb.velocity += jumpVelocity * Vector2.up; //jumping coyoteTimeCounter = 0.0f; // equals to 0.0 but quickly changing to 0.5 (coyoteTime) } } private void OnCollisionEnter2D(Collision2D collision) { if (collision.gameObject.tag == "Ground") { isGrounded = true; } } private void OnCollisionStay2D(Collision2D collision) { if(collision.gameObject.tag == "Ground") { isGrounded = true; } } private void OnCollisionExit2D(Collision2D collision) { if(collision.gameObject.tag == "Ground") { isGrounded = false; } } }
问题核心:即使角色未接地,if (isGrounded)分支仍执行,将coyoteTimeCounter重置为0.5f,导致可多次空中跳跃。
修复方案
问题分析
- 碰撞检测误判:当前逻辑只要接触
Ground标签物体就设isGrounded=true,包括侧面碰撞,会导致角色贴墙时错误重置计数器。 - 无跳跃锁定机制:仅重置
coyoteTimeCounter不足以阻止后续跳跃,一旦isGrounded被误设为true,计数器会立刻被重新赋值。
具体修复步骤
- 添加
canJump变量锁定跳跃状态,确保一次Coyote Time窗口内只能跳一次。 - 优化碰撞检测,只承认从下方接触地面的情况(通过碰撞法线Y轴判断)。
- 调整计数器更新逻辑,仅在落地且可跳跃时重置计数器。
修复后的完整代码
public class Jump : MonoBehaviour // 遵循C#大驼峰命名规范 { [SerializeField] float coyoteTime = 0.5f; [SerializeField] float coyoteTimeCounter = 0.0f; [SerializeField] float horizontalSpeed = 7.8f; [SerializeField] float jumpVelocity = 7.5f; Rigidbody2D rb; [SerializeField] bool isGrounded = false; private bool canJump = true; // 新增跳跃锁定标记 void Start() { rb = GetComponent<Rigidbody2D>(); } void Update() { float horiMove = Input.GetAxis("Horizontal") * horizontalSpeed * Time.deltaTime; rb.transform.Translate(new Vector2(horiMove, 0f)); // 仅在落地且可跳跃时重置Coyote Time计数器 if (isGrounded && canJump) { coyoteTimeCounter = coyoteTime; } else if (!isGrounded) { coyoteTimeCounter -= Time.deltaTime; coyoteTimeCounter = Mathf.Max(coyoteTimeCounter, 0f); // 避免计数器为负 } // 跳跃逻辑:满足Coyote Time窗口且未锁定跳跃 if (Input.GetKeyDown(KeyCode.Space) && coyoteTimeCounter > 0f && canJump) { rb.velocity = new Vector2(rb.velocity.x, jumpVelocity); // 直接设置Y轴速度更稳定 coyoteTimeCounter = 0f; canJump = false; // 锁定跳跃,直到落地解锁 } } private void OnCollisionEnter2D(Collision2D collision) { if (collision.gameObject.CompareTag("Ground")) { // 检查碰撞法线是否来自下方(排除侧面贴墙情况) foreach (ContactPoint2D contact in collision.contacts) { if (contact.normal.y > 0.7f) // 法线Y轴大于0.7,判定为落地 { isGrounded = true; canJump = true; // 落地后解锁跳跃 break; } } } } private void OnCollisionStay2D(Collision2D collision) { if (collision.gameObject.CompareTag("Ground")) { bool isTouchingGroundFromBelow = false; foreach (ContactPoint2D contact in collision.contacts) { if (contact.normal.y > 0.7f) { isTouchingGroundFromBelow = true; break; } } isGrounded = isTouchingGroundFromBelow; if (isGrounded) canJump = true; } } private void OnCollisionExit2D(Collision2D collision) { if (collision.gameObject.CompareTag("Ground")) { isGrounded = false; } } }
额外说明
- 类名修改为
Jump提升代码可读性,符合C#命名规范。 - 跳跃时直接设置Y轴速度,避免叠加导致的速度异常。
- 通过碰撞法线判断落地,消除侧面接触地面的误判。
- 用
Mathf.Max确保计数器不会变为负数,逻辑更严谨。
内容的提问来源于stack exchange,提问作者Halil Cinar
相关产品推荐
相关产品推荐

