Unity平台游戏多跳机制问题:走下平台后跳跃次数未正确递减
平台游戏多跳机制修复:走出平台后跳跃次数正确递减的方案
问题核心
多跳功能(双跳/三跳)正常,但玩家未触发跳跃、水平走出平台时,可用跳跃次数未按预期递减:比如maxJumps=2时,走出平台后仍能跳2次,期望仅保留1次跳跃机会。要求保留碰撞法线y分量>0.5的判断逻辑,用于区分地面与墙面。
问题根源分析
- 原代码中
OnCollisionExit2D里的col.contacts在碰撞退出时已无有效数据,无法通过法线判断是否离开地面 - 拆分脚部碰撞类后的逻辑存在漏洞:仅通过
keyX和keyJump判断,未验证是否离开的是地面(法线y>0.5),且可能因碰撞边缘多次触发导致jumpCount错误递减
修复方案一:基于原代码的最小修改
核心思路:在OnCollisionStay2D中记录是否处于地面状态,在FixedUpdate中检测离开地面的瞬间(从地面变为非地面),且此时未触发跳跃,才递减jumpCount
using UnityEngine; using UnityEngine.InputSystem; using System.Collections; public class PlayerBehavior : MonoBehaviour { [Header("References")] public Rigidbody2D rb; public Transform respawn; public SpriteRenderer spriteRend; public Animator animator; [Header("Movement")] public float moveSpeed = 10f; public float jumpHeight = 15f; public int jumpCount, maxJumps = 2; [Header("Input")] private float keyX = 0; private bool keyJump = false; public InputAction playerMovement, playerJump; [Header("Effects")] public float fadeDuration = 0.5f; // 记录上一帧是否在地面 private bool wasGrounded = false; // 当前是否在地面 private bool isGrounded = false; private void OnEnable() { playerMovement.Enable(); playerJump.Enable(); } private void OnDisable() { playerMovement.Disable(); playerJump.Disable(); } private void Start() { spriteRend = GetComponent<SpriteRenderer>(); spriteRend.flipX = false; rb = GetComponent<Rigidbody2D>(); rb.gravityScale = 6f; jumpCount = maxJumps; animator = GetComponent<Animator>(); wasGrounded = false; isGrounded = false; } private void Update() { keyX = playerMovement.ReadValue<float>(); rb.linearVelocity = new Vector2(keyX * moveSpeed, rb.linearVelocity.y); animator.SetFloat("X Velocity", Mathf.Abs(rb.linearVelocity.x)); animator.SetFloat("Y Velocity", rb.linearVelocity.y); if (keyX > 0) spriteRend.flipX = false; else if (keyX < 0) spriteRend.flipX = true; keyJump = playerJump.triggered; if (keyJump && jumpCount > 0) { rb.linearVelocity = new Vector2(rb.linearVelocity.x, jumpHeight); jumpCount--; animator.SetBool("isJumping", true); } else { animator.SetBool("isJumping", false); } } private void FixedUpdate() { // 检测从地面变为非地面的瞬间 if (wasGrounded && !isGrounded) { // 未触发跳跃且有水平移动(确认是走出平台而非主动跳跃) if (!keyJump && Mathf.Abs(keyX) > 0.1f) { // 确保jumpCount不会减到负数 if (jumpCount > 0) { jumpCount--; } } } // 更新上一帧地面状态 wasGrounded = isGrounded; // 重置当前地面状态,等待OnCollisionStay更新 isGrounded = false; } private void OnCollisionStay2D(Collision2D col) { if (col.gameObject.CompareTag("Surface")) { foreach (ContactPoint2D contact in col.contacts) { // 仅当接触地面时标记为在地面 if (contact.normal.y > 0.5f) { isGrounded = true; break; } } } } private void OnCollisionEnter2D(Collision2D col) { if (col.gameObject.CompareTag("Surface")) { foreach (ContactPoint2D contact in col.contacts) { if (contact.normal.y > 0.5f) { jumpCount = maxJumps; break; } } } if (col.gameObject.CompareTag("Hazard")) { StartCoroutine(DeathEffect()); } } private IEnumerator DeathEffect() { SpriteRenderer sr = GetComponent<SpriteRenderer>(); Color originalColor = sr.color; float t = 0; while (t < fadeDuration) { t += Time.deltaTime; sr.color = new Color( originalColor.r, originalColor.g, originalColor.b, 1 - (t / fadeDuration) ); yield return null; } sr.color = new Color(originalColor.r, originalColor.g, originalColor.b, 1); transform.position = respawn.transform.position; } }
修复方案二:优化脚部碰撞类实现
如果坚持使用脚部碰撞检测,需在脚部类中保留地面接触的法线判断,并在玩家类中正确处理状态变化:
脚部碰撞类(PlayerFeetBehavior)
using UnityEngine; public class PlayerFeetBehavior : MonoBehaviour { private PlayerBehavior playerBehavior; // 记录脚部是否接触地面 public bool IsGrounded { get; private set; } private void Start() { playerBehavior = GetComponentInParent<PlayerBehavior>(); IsGrounded = false; } private void OnCollisionStay2D(Collision2D col) { if (col.gameObject.CompareTag("Surface")) { foreach (ContactPoint2D contact in col.contacts) { if (contact.normal.y > 0.5f) { IsGrounded = true; return; } } } IsGrounded = false; } private void OnCollisionExit2D(Collision2D col) { if (col.gameObject.CompareTag("Surface")) { IsGrounded = false; } } }
玩家行为类(PlayerBehavior)修改关键部分
using UnityEngine; using UnityEngine.InputSystem; using System.Collections; public class PlayerBehavior : MonoBehaviour { // 新增脚部引用 public PlayerFeetBehavior feet; private bool wasGrounded = false; private void Start() { // ... 原有代码 ... wasGrounded = false; // 确保脚部引用已赋值 if (feet == null) { feet = GetComponentInChildren<PlayerFeetBehavior>(); } } private void FixedUpdate() { if (wasGrounded && !feet.IsGrounded) { if (!keyJump && Mathf.Abs(keyX) > 0.1f) { if (jumpCount > 0) { jumpCount--; } } } wasGrounded = feet.IsGrounded; } private void OnCollisionEnter2D(Collision2D col) { if (col.gameObject.CompareTag("Surface")) { foreach (ContactPoint2D contact in col.contacts) { if (contact.normal.y > 0.5f) { jumpCount = maxJumps; break; } } } if (col.gameObject.CompareTag("Hazard")) { StartCoroutine(DeathEffect()); } } // ... 原有其他代码保持不变 ... }
关键修改说明
- 使用
OnCollisionStay2D而非OnCollisionExit2D获取有效碰撞法线,确保正确判断地面接触 - 通过
wasGrounded和isGrounded的状态变化,精准捕捉离开地面的瞬间 - 增加
Mathf.Abs(keyX) > 0.1f的判断,确保仅在水平移动走出平台时才递减跳跃次数,排除主动跳跃的情况 - 对
jumpCount做下限保护,避免减到负数
内容的提问来源于stack exchange,提问作者ScienceCode Creations
相关产品推荐
相关产品推荐

