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); } }
修改说明:
- 将移动逻辑移至
FixedUpdate(),与物理更新同步; - 改用
Input.GetAxisRaw()获取无平滑输入,避免摇杆过渡延迟; - 新增
CheckGrounded()方法,用Physics2D.OverlapCircle检测地面,逻辑更稳定; - 在
Start()中初始化Rigidbody2D的线性阻力,减少物理残留运动; - 调整输入判断条件为阈值判断,避免微小输入值导致的滑动;
- 修正
coyoteTime的更新逻辑,放在固定帧中计算更准确。
内容的提问来源于stack exchange,提问作者ImpureHatred
相关产品推荐
相关产品推荐

