如何修复Unity中Rigidbody2D墙跳后空中停滞的问题?
墙跳后角色速度停滞问题修复
问题描述
刚接触编程,制作2D角色控制器时遇到墙跳异常:玩家执行墙跳后,Rigidbody2D速度被覆盖导致空中停滞,且isWallJumping设为false时角色直接停止,希望关闭墙跳状态后玩家能保持原有速度。
问题根源
- 移动逻辑错误:原代码中水平移动的力计算为
speed * move - rb.velocity.x,当move=0时会添加反向力抵消当前速度,导致墙跳结束后直接停滞。 - 冗余速度赋值:
stopWallJump函数中保存再赋值速度的操作完全多余,反而可能干扰原有速度状态。 - 墙滑逻辑强制修改速度:
wallSlide里的冗余判断会强制将y轴速度设为-3,破坏墙跳后的速度延续。 - 墙跳期间完全阻塞输入:墙跳时完全禁用水平移动,没有预留微调或惯性衔接的空间。
修复方案
1. 修正水平移动逻辑,保留惯性
将移动逻辑改为基于输入的加速度,仅限制最大速度,而非直接抵消当前速度:
if (!isWallJumping || Mathf.Abs(move) > 0.1f) { Vector2 forceToAdd = new Vector2(speed * move, 0f); rb.AddForce(forceToAdd); // 限制水平最大速度 if (Mathf.Abs(rb.velocity.x) > maxSpeed) { rb.velocity = new Vector2(Mathf.Sign(rb.velocity.x) * maxSpeed, rb.velocity.y); } }
2. 简化墙跳结束逻辑
移除stopWallJump中无用的速度保存与赋值:
private void stopWallJump() { isWallJumping = false; }
3. 修复墙滑速度限制
删除墙滑中强制修改y速度的冗余代码:
private void wallSlide() { isWallSliding = (isWallRight || isWallLeft) && !isGrounded && !isWallJumping; if (isWallSliding) { rb.velocity = new Vector2(rb.velocity.x, Mathf.Clamp(rb.velocity.y, -wallSlideSpeed, maxClimbSpeed)); } }
完整优化后代码(整理版)
public class MovementPlayer : MonoBehaviour { // 公共参数 public float speed; public float jump; public float jump2; public float smashForce; public float leftCastDistance; public float rightCastDistance; public Vector2 rightBoxSize; public Vector2 leftBoxSize; public float castDistance; public LayerMask groundLayer; public Vector2 boxSize; public Vector2 wallJumpForce; public float wallJumpDuration; public float wallSlideSpeed; public float maxClimbSpeed; public float maxSpeed; // 私有变量 private float move; private float jumpnum; private bool isMoving; private bool isSmashing; private bool isWallLeft; private bool isWallRight; private bool isGrounded; private Rigidbody2D rb; public bool isWallJumping; private float wallJumpDirection; private bool isWallSliding; private float wallJumpTimer = 0f; private void OnDrawGizmos() { Gizmos.DrawWireCube(transform.position - transform.up * castDistance, boxSize); Gizmos.DrawWireCube(transform.position + transform.right * rightCastDistance, rightBoxSize); Gizmos.DrawWireCube(transform.position - transform.right * leftCastDistance, leftBoxSize); } void Start() { rb = GetComponent<Rigidbody2D>(); rb.freezeRotation = true; } void Update() { // 墙跳计时器逻辑 if (isWallJumping) { wallJumpTimer -= Time.deltaTime; if (wallJumpTimer <= 0f) stopWallJump(); } // 地面与墙体检测 isGrounded = Physics2D.BoxCast(transform.position, boxSize, 0, -transform.up, castDistance, groundLayer); isWallRight = Physics2D.BoxCast(transform.position, rightBoxSize, 0, transform.right, rightCastDistance, groundLayer); isWallLeft = Physics2D.BoxCast(transform.position, leftBoxSize, 0, -transform.right, leftCastDistance, groundLayer); // 地面状态重置 if (isGrounded) { jumpnum = 0; isSmashing = false; isWallJumping = false; } move = Input.GetAxis("Horizontal"); isMoving = Mathf.Abs(move) > 0.01f; // 水平移动:墙跳时允许微调 if (!isWallJumping || Mathf.Abs(move) > 0.1f) { Vector2 forceToAdd = new Vector2(speed * move, 0f); rb.AddForce(forceToAdd); // 限制水平最大速度 if (Mathf.Abs(rb.velocity.x) > maxSpeed) { rb.velocity = new Vector2(Mathf.Sign(rb.velocity.x) * maxSpeed, rb.velocity.y); } } // 跳跃与墙跳触发 if (Input.GetButtonDown("Jump")) { if (jumpnum < 2) { if (isWallRight || isWallLeft) { wallJump(); } else { // 普通跳跃 rb.velocity = new Vector2(rb.velocity.x, 0f); float currentJump = jumpnum < 1 ? jump : jump2; rb.AddForce(new Vector2(rb.velocity.x, currentJump), ForceMode2D.Impulse); jumpnum++; } } } // 下砸技能 if (!isWallJumping && Input.GetKeyDown(KeyCode.S) && !isGrounded && !isSmashing) { rb.AddForce(new Vector2(0, -smashForce), ForceMode2D.Impulse); isSmashing = true; } wallSlide(); } private void wallJump() { wallJumpDirection = isWallRight ? -1 : 1; isWallJumping = true; rb.AddForce(new Vector2(wallJumpDirection * wallJumpForce.x, wallJumpForce.y), ForceMode2D.Impulse); wallJumpTimer = wallJumpDuration; jumpnum++; } private void stopWallJump() { isWallJumping = false; } private void wallSlide() { isWallSliding = (isWallRight || isWallLeft) && !isGrounded && !isWallJumping; if (isWallSliding) { rb.velocity = new Vector2(rb.velocity.x, Mathf.Clamp(rb.velocity.y, -wallSlideSpeed, maxClimbSpeed)); } } private void FixedUpdate() { // 无输入时的移动阻尼 if (!isMoving && Mathf.Abs(rb.velocity.x) > 0.01f) { float dampeningForce = rb.velocity.x * -0.5f; rb.AddForce(new Vector2(dampeningForce, 0f)); } } }
额外优化建议
- 使用
ForceMode2D.Impulse处理跳跃和下砸,更符合瞬时力的物理特性 - 移除无用变量(如
raycastDistance、playerPos),精简代码结构 - 合并重复的输入判断逻辑,减少冗余代码
- 统一变量命名风格,提高代码可读性
内容的提问来源于stack exchange,提问作者Klushy225
相关产品推荐
相关产品推荐

