Unity 2D场景重置后跳跃角色无法移动及卡墙角问题求助
Unity 2D角色移动与场景重置问题修复
问题1:跳跃状态下触发场景重置后角色无法移动
问题原因
- 场景重置时,若玩家按住跳跃键,
jumpHeld会持续保持true,FixedUpdate中会不断执行Jump()方法,强制将角色Y轴速度设为跳跃力,导致角色始终处于上升/跳跃状态,无法落地,自然无法进行移动操作。 - 原跳跃逻辑未限制单次跳跃,按住跳跃键会持续触发跳跃,进一步加剧角色失控问题。
修复方案
- 将跳跃输入从
Input.GetButton("Jump")改为Input.GetButtonDown("Jump"),确保仅在按键按下的瞬间触发一次跳跃。 - 添加跳跃状态锁(
canJump),防止连续触发跳跃。 - 在
Start()方法中重置跳跃相关状态,避免场景重置后残留输入状态影响。
修改后的核心代码片段:
// 新增跳跃状态锁 private bool canJump = true; void Start() { rb = GetComponent<Rigidbody2D>(); anim = GetComponent<Animator>(); // 重置跳跃状态,避免场景重置后残留输入影响 canJump = true; jumpHeld = false; } void Update() { Move(); // 改为GetButtonDown,仅在按键按下瞬间触发跳跃输入 jumpHeld = Input.GetButtonDown("Jump"); CheckGround(); UpdateAnimations(); } void FixedUpdate() { // 增加canJump判断,防止连续跳跃 if (jumpHeld && canJump && (isGrounded || coyoteTimeCounter > 0)) { Jump(); } } void Jump() { rb.velocity = new Vector2(rb.velocity.x, jumpForce); isGrounded = false; coyoteTimeCounter = 0f; // 跳跃后锁定,直到落地解锁 canJump = false; }
问题2:角色卡在墙壁与地面夹角时无法跳跃
问题原因
- 角色卡在夹角时,地面检测的BoxCast可能同时碰撞到墙壁,返回的碰撞法线为墙壁的法线(Y轴分量远小于0.7f),导致
isGrounded被判定为false,无法触发跳跃。 - 原地面检测仅依赖单一BoxCast的法线判断,容错性不足。
修复方案
改用Physics2D.OverlapBox检测地面碰撞,只要检测范围内存在地面层碰撞体,即判定为接地,无需依赖法线(适合大部分2D平台场景),提升夹角场景的检测容错性。
修改后的CheckGround方法:
void CheckGround() { Vector2 boxSize = new Vector2(checkWidth, fixedCheckHeight); // 使用OverlapBox检测范围内是否有地面碰撞体 bool hasGroundContact = Physics2D.OverlapBox(groundCheck.position, boxSize, 0f, groundLayer); if (hasGroundContact) { isGrounded = true; coyoteTimeCounter = coyoteTime; canJump = true; // 同步解锁跳跃 } else { isGrounded = false; coyoteTimeCounter -= Time.deltaTime; } }
额外优化(可选)
调整groundCheck的位置,使其更靠近角色底部,减少检测到墙壁的概率;或者缩小检测宽度,避免检测范围覆盖到墙壁。
完整修复后的PlayerMovementSteamDeck脚本:
using System.Collections; using System.Collections.Generic; using UnityEngine; public class PlayerMovementSteamDeck : MonoBehaviour { [Header("Movement Settings")] public float moveSpeed = 5f; public float jumpForce = 10f; [Header("Ground Check Settings")] public Transform groundCheck; public float checkWidth = 0.5f; private const float fixedCheckHeight = 0.2f; public LayerMask groundLayer; public float coyoteTime = 0.2f; private float coyoteTimeCounter; private bool isGrounded; private bool jumpHeld; // 新增跳跃状态锁 private bool canJump = true; private Rigidbody2D rb; private Animator anim; void Start() { rb = GetComponent<Rigidbody2D>(); anim = GetComponent<Animator>(); // 重置跳跃状态,避免场景重置后残留输入影响 canJump = true; jumpHeld = false; } void Update() { Move(); // 改为GetButtonDown,仅在按键按下瞬间触发跳跃输入 jumpHeld = Input.GetButtonDown("Jump"); CheckGround(); UpdateAnimations(); } void FixedUpdate() { // 增加canJump判断,防止连续跳跃 if (jumpHeld && canJump && (isGrounded || coyoteTimeCounter > 0)) { Jump(); } } void Move() { float moveInput = Input.GetAxisRaw("Horizontal"); bool aPressed = Input.GetKey(KeyCode.A); bool dPressed = Input.GetKey(KeyCode.D); bool leftArrowPressed = Input.GetKey(KeyCode.LeftArrow); bool rightArrowPressed = Input.GetKey(KeyCode.RightArrow); if ((aPressed && dPressed) || (leftArrowPressed && rightArrowPressed)) { moveInput = 0; } else if (aPressed || leftArrowPressed) { moveInput = -1; } else if (dPressed || rightArrowPressed) { moveInput = 1; } if (moveInput == 0) { rb.velocity = new Vector2(0, rb.velocity.y); } else { rb.velocity = new Vector2(moveInput * moveSpeed, rb.velocity.y); transform.localScale = new Vector3(Mathf.Sign(moveInput), 1, 1); } } void Jump() { rb.velocity = new Vector2(rb.velocity.x, jumpForce); isGrounded = false; coyoteTimeCounter = 0f; // 跳跃后锁定,直到落地解锁 canJump = false; } void CheckGround() { Vector2 boxSize = new Vector2(checkWidth, fixedCheckHeight); // 使用OverlapBox检测地面,提升夹角场景的检测容错性 bool hasGroundContact = Physics2D.OverlapBox(groundCheck.position, boxSize, 0f, groundLayer); if (hasGroundContact) { isGrounded = true; coyoteTimeCounter = coyoteTime; canJump = true; } else { isGrounded = false; coyoteTimeCounter -= Time.deltaTime; } } void UpdateAnimations() { anim.SetFloat("Speed", Mathf.Abs(rb.velocity.x)); anim.SetBool("isJumping", !isGrounded && coyoteTimeCounter <= 0); } private void OnDrawGizmosSelected() { if (groundCheck != null) { Gizmos.color = Color.red; Vector2 boxSize = new Vector2(checkWidth, fixedCheckHeight); Gizmos.DrawWireCube(groundCheck.position, boxSize); } } }
KillZone脚本无需修改,保持原逻辑即可。
内容的提问来源于stack exchange,提问作者ImpureHatred
相关产品推荐
相关产品推荐

