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

Wall Slide执行一次Wall Jump后失效,无法再次触发求助

问题

Wall Slide功能初始运行正常,但执行一次Wall Jump后,Wall Slide彻底失效,且无法再次触发Wall Jump。以下是Player Movement代码:

public class PlayerMovement : MonoBehaviour
{
    [Header("Movement Options")]

    public float speed;

    public float jumpForce;
    public float acceleration;
    private float currentSpeed;

    private float horizontal;
    private bool isFacingRight = true;

    [SerializeField] private bool isWallSliding;
    private float wallSlidingSpeed = 2f;

    private bool isWallJumping;
    private float wallJumpingDirection;
    private float wallJumpingTime = 0.2f;
    private float wallJumpingCounter;
    private float wallJumpingDuration = 0.4f;
    private Vector2 wallJumpingPower = new Vector2(8f, 16f);

    [SerializeField] private Transform wallCheck;
    [SerializeField] private LayerMask wallLayer;

    [Header("Coyote and Buffer")]
    public float coyoteTime;
    private float coyoteCounter;
    public float bufferTime;
    private float buffetCounter;


    [Header("Components")]

    public Rigidbody2D rb;
    public Transform groundCheck;
    public LayerMask groundLayer;

    void Start()
    {
        currentSpeed = 0;
    }

    void Update()
    {
        Debug.Log($"isWallJumping: {isWallJumping}, isWallSliding: {isWallSliding}, isWalled: {isWalled()}, isGrounded: {isGrounded()}");

        horizontal = Input.GetAxisRaw("Horizontal");

        Jump();
        BetterJump();

        WallSlide();
        WallJump();

        if (!isWallJumping && !isWallSliding)
        {
            Flip();
        }

    }

    void FixedUpdate()
    {
        if (!isWallJumping)
        {
            if (horizontal != 0)
            {
                currentSpeed = Mathf.MoveTowards(currentSpeed, speed * horizontal, acceleration * Time.fixedDeltaTime);
            }
            else
            {
                currentSpeed = Mathf.MoveTowards(currentSpeed, 0, acceleration * Time.fixedDeltaTime);
            }

            rb.velocity = new Vector2(currentSpeed, rb.velocity.y);
        }

    }

    bool isGrounded()
    {
        return Physics2D.OverlapCircle(groundCheck.position, 0.2f, groundLayer);
    }

    private bool isWalled()
    {
        return Physics2D.OverlapCircle(wallCheck.position, 0.2f, wallLayer);
    }

    private void WallSlide()
    {
        if (isWalled() && !isGrounded() && horizontal != 0)
        {
            isWallSliding = true;
            rb.velocity = new Vector2(rb.velocity.x, Mathf.Clamp(rb.velocity.y, -wallSlidingSpeed, float.MaxValue));
        }
        else
        {
            isWallSliding = false;
        }
    }

    private void WallJump()
    {
        if (isWallSliding)
        {
            isWallJumping = false;
            wallJumpingDirection = -transform.localScale.x;
            wallJumpingCounter = wallJumpingTime;

            CancelInvoke(nameof(StopWallJumping));
        }
        else
        {
            wallJumpingCounter -= Time.deltaTime;
        }

        if (Input.GetButtonDown("Jump") && (wallJumpingCounter > 0f || isWallSliding))
        {
            isWallJumping = true;
            isWallSliding = false;
            rb.velocity = new Vector2(wallJumpingDirection * wallJumpingPower.x, wallJumpingPower.y);
            wallJumpingCounter = 0f;

            if (transform.localScale.x != wallJumpingDirection)
            {
                isFacingRight = !isFacingRight;
                Vector3 localScale = transform.localScale;
                localScale.x *= -1f;
                transform.localScale = localScale;
            }

            Invoke(nameof(StopWallJumping), wallJumpingDuration);
        }

        // Verifica se o personagem está na parede após o Wall Jump
        if (isWalled() && !isGrounded())
        {
            isWallSliding = true;
        }
    }

    private void StopWallJumping()
    {
        isWallJumping = false;
    }

    void Jump()
    {
        if (buffetCounter > 0f && coyoteCounter > 0f)
        {
            rb.velocity = new Vector2(rb.velocity.x, jumpForce);

            buffetCounter = 0f;
        }

        if (Input.GetButtonDown("Jump") && rb.velocity.y > 0f)
        {
            rb.velocity = new Vector2(rb.velocity.x, rb.velocity.y * 0.5f);

            coyoteCounter = 0f;
        }
    }

    void BetterJump()
    {
        if (isGrounded())
        {
            coyoteCounter = coyoteTime;
        }
        else { coyoteCounter -= Time.deltaTime; }

        if (Input.GetButtonDown("Jump"))
        {
            buffetCounter = bufferTime;
        }
        else { buffetCounter -= Time.deltaTime; }
    }

    void Flip()
    {
        if (isFacingRight && horizontal < 0f || !isFacingRight && horizontal > 0f)
        {
            isFacingRight = !isFacingRight;
            Vector3 localScale = transform.localScale;
            localScale.x *= 1f;
            transform.localScale = localScale;
        }
    }
}

问题分析与修复

核心问题点

  1. WallJump方法末尾的错误赋值:代码最后强制设置isWallSliding = true,完全忽略了WallSlide方法中「必须按住对应方向键(horizontal≠0)」的判断条件,导致Wall Jump后即使没有按住方向键,也会强制开启滑墙状态,干扰后续的状态判断逻辑。
  2. Flip方法的翻转逻辑错误:localScale.x *= 1f等于没有修改缩放值,角色无法正确翻转,导致wallJumpingDirection计算错误,后续Wall Jump的方向会出问题。
  3. WallJump触发时的状态干扰:WallJump触发时直接设置isWallSliding=false,但后续逻辑没有正确让状态回归到WallSlide的判断流程。

修复后的代码

public class PlayerMovement : MonoBehaviour
{
    [Header("Movement Options")]
    public float speed;
    public float jumpForce;
    public float acceleration;
    private float currentSpeed;

    private float horizontal;
    private bool isFacingRight = true;

    [SerializeField] private bool isWallSliding;
    private float wallSlidingSpeed = 2f;

    private bool isWallJumping;
    private float wallJumpingDirection;
    private float wallJumpingTime = 0.2f;
    private float wallJumpingCounter;
    private float wallJumpingDuration = 0.4f;
    private Vector2 wallJumpingPower = new Vector2(8f, 16f);

    [SerializeField] private Transform wallCheck;
    [SerializeField] private LayerMask wallLayer;

    [Header("Coyote and Buffer")]
    public float coyoteTime;
    private float coyoteCounter;
    public float bufferTime;
    private float buffetCounter;

    [Header("Components")]
    public Rigidbody2D rb;
    public Transform groundCheck;
    public LayerMask groundLayer;

    void Start()
    {
        currentSpeed = 0;
    }

    void Update()
    {
        Debug.Log($"isWallJumping: {isWallJumping}, isWallSliding: {isWallSliding}, isWalled: {isWalled()}, isGrounded: {isGrounded()}");

        horizontal = Input.GetAxisRaw("Horizontal");

        Jump();
        BetterJump();

        // 调整执行顺序:先处理WallJump,再处理WallSlide,避免状态覆盖
        WallJump();
        WallSlide();

        if (!isWallJumping && !isWallSliding)
        {
            Flip();
        }
    }

    void FixedUpdate()
    {
        if (!isWallJumping)
        {
            if (horizontal != 0)
            {
                currentSpeed = Mathf.MoveTowards(currentSpeed, speed * horizontal, acceleration * Time.fixedDeltaTime);
            }
            else
            {
                currentSpeed = Mathf.MoveTowards(currentSpeed, 0, acceleration * Time.fixedDeltaTime);
            }

            rb.velocity = new Vector2(currentSpeed, rb.velocity.y);
        }
    }

    bool isGrounded()
    {
        return Physics2D.OverlapCircle(groundCheck.position, 0.2f, groundLayer);
    }

    private bool isWalled()
    {
        return Physics2D.OverlapCircle(wallCheck.position, 0.2f, wallLayer);
    }

    private void WallSlide()
    {
        // 仅当不在WallJump状态时,才判断滑墙条件
        if (!isWallJumping && isWalled() && !isGrounded() && horizontal != 0)
        {
            isWallSliding = true;
            rb.velocity = new Vector2(rb.velocity.x, Mathf.Clamp(rb.velocity.y, -wallSlidingSpeed, float.MaxValue));
        }
        else
        {
            isWallSliding = false;
        }
    }

    private void WallJump()
    {
        if (isWallSliding)
        {
            isWallJumping = false;
            wallJumpingDirection = -transform.localScale.x;
            wallJumpingCounter = wallJumpingTime;

            CancelInvoke(nameof(StopWallJumping));
        }
        else
        {
            wallJumpingCounter -= Time.deltaTime;
        }

        if (Input.GetButtonDown("Jump") && (wallJumpingCounter > 0f || isWallSliding))
        {
            isWallJumping = true;
            isWallSliding = false;
            rb.velocity = new Vector2(wallJumpingDirection * wallJumpingPower.x, wallJumpingPower.y);
            wallJumpingCounter = 0f;

            if (transform.localScale.x != wallJumpingDirection)
            {
                isFacingRight = !isFacingRight;
                Vector3 localScale = transform.localScale;
                localScale.x *= -1f;
                transform.localScale = localScale;
            }

            Invoke(nameof(StopWallJumping), wallJumpingDuration);
        }

        // 移除错误的强制滑墙赋值逻辑,交给WallSlide方法处理
    }

    private void StopWallJumping()
    {
        isWallJumping = false;
    }

    void Jump()
    {
        if (buffetCounter > 0f && coyoteCounter > 0f)
        {
            rb.velocity = new Vector2(rb.velocity.x, jumpForce);
            buffetCounter = 0f;
        }

        if (Input.GetButtonDown("Jump") && rb.velocity.y > 0f)
        {
            rb.velocity = new Vector2(rb.velocity.x, rb.velocity.y * 0.5f);
            coyoteCounter = 0f;
        }
    }

    void BetterJump()
    {
        if (isGrounded())
        {
            coyoteCounter = coyoteTime;
        }
        else 
        { 
            coyoteCounter -= Time.deltaTime; 
        }

        if (Input.GetButtonDown("Jump"))
        {
            buffetCounter = bufferTime;
        }
        else 
        { 
            buffetCounter -= Time.deltaTime; 
        }
    }

    void Flip()
    {
        if (isFacingRight && horizontal < 0f || !isFacingRight && horizontal > 0f)
        {
            isFacingRight = !isFacingRight;
            Vector3 localScale = transform.localScale;
            // 修复翻转逻辑:乘以-1实现左右翻转
            localScale.x *= -1f;
            transform.localScale = localScale;
        }
    }
}

关键修改说明

  • 移除了WallJump方法末尾强制设置isWallSliding = true的代码,让滑墙状态完全由WallSlide方法的条件判断来控制。
  • 修复了Flip方法中的缩放值修改逻辑,将localScale.x *= 1f改为localScale.x *= -1f,确保角色能正确翻转。
  • 调整了Update中WallJump和WallSlide的执行顺序,避免状态被意外覆盖。
  • 在WallSlide方法中增加了!isWallJumping的判断,确保WallJump过程中不会触发滑墙逻辑。

内容的提问来源于stack exchange,提问作者Al Simmons

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.13 16:59:51