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

Unity 2D场景重置后跳跃角色无法移动及卡墙角问题求助

Unity 2D角色移动与场景重置问题修复

问题1:跳跃状态下触发场景重置后角色无法移动

问题原因

  • 场景重置时,若玩家按住跳跃键,jumpHeld会持续保持true,FixedUpdate中会不断执行Jump()方法,强制将角色Y轴速度设为跳跃力,导致角色始终处于上升/跳跃状态,无法落地,自然无法进行移动操作。
  • 原跳跃逻辑未限制单次跳跃,按住跳跃键会持续触发跳跃,进一步加剧角色失控问题。

修复方案

  1. 将跳跃输入从Input.GetButton("Jump")改为Input.GetButtonDown("Jump"),确保仅在按键按下的瞬间触发一次跳跃。
  2. 添加跳跃状态锁(canJump),防止连续触发跳跃。
  3. 在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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.13 15:07:10