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

Unity角色移动持续滑动:已尝试高摩擦与代码修复仍无效求原因

问题描述

我的角色移动时总会出现滑动现象,完全不想让它产生滑动。尝试过调高摩擦值解决,但没有效果;也编写了代码试图阻止滑动,同样无效。请问可能的原因是什么?

用户提供的原代码:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class PlayerMovementSteamDeck : MonoBehaviour
{
    public float moveSpeed = 5f;
    public float jumpForce = 10f;
    public Transform groundCheck;
    public float checkRadius = 0.1f;
    public float coyoteTime = 0.2f;
    private float coyoteTimeCounter;

    private bool isGrounded;
    private bool jumpHeld;

    private Rigidbody2D rb;
    private Animator anim;

    void Start()
    {
        rb = GetComponent<Rigidbody2D>();
        anim = GetComponent<Animator>();
    }
 
    void Update()
     {
        Move();
 
        jumpHeld = Input.GetButton("Jump"); // A button for jumping on Steam Deck
 
         UpdateAnimations();
     }

    void FixedUpdate()
     {
        if (jumpHeld && (isGrounded || coyoteTimeCounter > 0))
          {
             Jump();
         }
    }

    void Move()
    {
        // Detect joystick input for Steam Deck
        float moveInput = Input.GetAxis("Horizontal");

        // Check if both A and D keys or both Left and Right arrow keys are pressed
        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; // Prevent movement if both keys are pressed
        }
         else if (aPressed || leftArrowPressed)
         {
             moveInput = -1; // Move left when only A or Left Arrow is pressed
        }
        else if (dPressed || rightArrowPressed)
         {
            moveInput = 1; // Move right when only D or Right Arrow is pressed
        }

        // Apply movement or stop sliding
         if (moveInput == 0)
        {
            rb.velocity = new Vector2(0, rb.velocity.y); // Stop horizontal movement to prevent sliding
        }
        else
        {
            rb.velocity = new Vector2(moveInput * moveSpeed, rb.velocity.y); // Move player
            transform.localScale = new Vector3(Mathf.Sign(moveInput), 1, 1); // Flip character
        }
    }

    void Jump()
    {
        rb.velocity = new Vector2(rb.velocity.x, jumpForce);
        isGrounded = false;
        coyoteTimeCounter = 0f;
    }

    void UpdateAnimations()
    {
         anim.SetFloat("Speed", Mathf.Abs(rb.velocity.x));
         anim.SetBool("isJumping", !isGrounded && coyoteTimeCounter <= 0);
    }

    private void OnCollisionEnter2D(Collision2D collision)
    {
        if (collision.gameObject.CompareTag("Ground") && collision.contacts[0].normal.y > 0.5f)
        {
            isGrounded = true;
            coyoteTimeCounter = coyoteTime;

            if (jumpHeld)
            {
                Jump();
            }
        }
    }

    private void OnCollisionExit2D(Collision2D collision)
    {
        if (collision.gameObject.CompareTag("Ground"))
        {
            isGrounded = false;
            coyoteTimeCounter = 0f; // Reset coyote time when leaving the ground
        }
    }

    private void OnDrawGizmosSelected()
     {
        Gizmos.color = Color.red;
        Gizmos.DrawWireSphere(groundCheck.position, checkRadius);
    }
}
可能的原因及解决方案
  • Rigidbody2D物理参数设置不当
    • 线性阻力(Linear Drag)过低:即使代码将速度设为0,物理引擎仍会保留微小的运动残留。建议将Linear Drag设为5-10的区间,同时配合调整地面的摩擦值。
    • 插值功能干扰:如果Rigidbody2D开启了Interpolate,可能导致视觉上的滑动延迟,可尝试关闭或切换为Extrapolate。
  • 输入处理的平滑过渡问题
    • 原代码使用Input.GetAxis("Horizontal")获取摇杆输入,该方法默认带平滑过渡,摇杆回到中立位置时输入值不会立刻变为0,导致moveInput == 0的判断无法触发,进而出现滑动。可改用Input.GetAxisRaw("Horizontal")获取无平滑的输入,或给输入值设置阈值(比如Mathf.Abs(moveInput) < 0.1f时判定为输入为0)。
  • 地面检测逻辑不可靠
    • 依赖OnCollisionEnter2D/Exit2D判断地面状态,容易因碰撞角度(normal.y > 0.5f的条件过严)、短暂离地等情况导致状态切换延迟,空中时未阻止滑动。建议改用Physics2D.OverlapCircle通过地面检测点判断是否接地,逻辑更稳定。
  • 代码执行时机不同步
    • 移动逻辑放在Update()中,而Rigidbody2D的物理更新在FixedUpdate()中,输入与物理帧不同步可能导致滑动。建议将移动逻辑移至FixedUpdate()中执行。
代码修改建议

以下是调整后的代码,解决上述问题:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class PlayerMovementSteamDeck : MonoBehaviour
{
    public float moveSpeed = 5f;
    public float jumpForce = 10f;
    public Transform groundCheck;
    public float checkRadius = 0.1f;
    public LayerMask groundLayer; // 需在Inspector中指定地面所在层
    public float coyoteTime = 0.2f;
    private float coyoteTimeCounter;

    private bool isGrounded;
    private bool jumpHeld;

    private Rigidbody2D rb;
    private Animator anim;

    void Start()
    {
        rb = GetComponent<Rigidbody2D>();
        anim = GetComponent<Animator>();
        // 初始化线性阻力,减少物理残留运动
        rb.drag = 8f;
    }
 
    void Update()
    {
        jumpHeld = Input.GetButton("Jump"); // Steam Deck上的A键跳跃
        UpdateAnimations();
    }

    void FixedUpdate()
    {
        CheckGrounded();
        Move();
        if (jumpHeld && (isGrounded || coyoteTimeCounter > 0))
        {
            Jump();
        }
        // 固定帧更新 coyote time
        if (!isGrounded)
        {
            coyoteTimeCounter -= Time.fixedDeltaTime;
        }
    }

    void CheckGrounded()
    {
        // 使用OverlapCircle检测地面,替代碰撞事件判断,更可靠
        isGrounded = Physics2D.OverlapCircle(groundCheck.position, checkRadius, groundLayer);
        if (isGrounded)
        {
            coyoteTimeCounter = coyoteTime;
        }
    }

    void Move()
    {
        // GetAxisRaw获取无平滑输入,避免摇杆过渡导致的滑动
        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;
        }

        // 用阈值判断输入是否为0,避免微小输入值导致的滑动
        if (Mathf.Abs(moveInput) < 0.1f)
        {
            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;
    }

    void UpdateAnimations()
    {
        anim.SetFloat("Speed", Mathf.Abs(rb.velocity.x));
        anim.SetBool("isJumping", !isGrounded && coyoteTimeCounter <= 0);
    }

    private void OnDrawGizmosSelected()
    {
        Gizmos.color = Color.red;
        Gizmos.DrawWireSphere(groundCheck.position, checkRadius);
    }
}

修改说明:

  1. 将移动逻辑移至FixedUpdate(),与物理更新同步;
  2. 改用Input.GetAxisRaw()获取无平滑输入,避免摇杆过渡延迟;
  3. 新增CheckGrounded()方法,用Physics2D.OverlapCircle检测地面,逻辑更稳定;
  4. 在Start()中初始化Rigidbody2D的线性阻力,减少物理残留运动;
  5. 调整输入判断条件为阈值判断,避免微小输入值导致的滑动;
  6. 修正coyoteTime的更新逻辑,放在固定帧中计算更准确。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.16 06:37:32