You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何修复Unity中Rigidbody2D墙跳后空中停滞的问题?

墙跳后角色速度停滞问题修复

问题描述

刚接触编程,制作2D角色控制器时遇到墙跳异常:玩家执行墙跳后,Rigidbody2D速度被覆盖导致空中停滞,且isWallJumping设为false时角色直接停止,希望关闭墙跳状态后玩家能保持原有速度。

问题根源

  1. 移动逻辑错误:原代码中水平移动的力计算为speed * move - rb.velocity.x,当move=0时会添加反向力抵消当前速度,导致墙跳结束后直接停滞。
  2. 冗余速度赋值:stopWallJump函数中保存再赋值速度的操作完全多余,反而可能干扰原有速度状态。
  3. 墙滑逻辑强制修改速度:wallSlide里的冗余判断会强制将y轴速度设为-3,破坏墙跳后的速度延续。
  4. 墙跳期间完全阻塞输入:墙跳时完全禁用水平移动,没有预留微调或惯性衔接的空间。

修复方案

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.19 04:08:08