Unity3D中如何解决角色移动时的墙粘滞问题?
解决角色贴墙粘滞并实现墙面滑动效果
问题根源在于使用AddForce持续施加朝向墙面的力,碰撞后角色速度被抵消但力仍在作用,导致粘滞。要实现墙面滑动,需要过滤掉垂直于墙面的移动分量,只保留平行于墙面的移动方向。
具体修改步骤:
- 添加墙面检测逻辑,判断移动方向是否指向障碍物
- 修正移动方向,移除垂直于墙面法线的分量
- 优化力的施加逻辑,避免无效的朝向墙面的力
修改后的完整代码:
public class PlayerMovement : MonoBehaviour { [Header("Movement")] public float moveSpeed; public float walkSpeed; public float sprintSpeed; public float groundDrag; public float jumpForce; public float jumpCooldown; public float airMultiplier; [Header("Keybinds")] public KeyCode sprintKey = KeyCode.LeftShift; [Header("Ground & Wall Check")] public float playerHeight; public LayerMask whatIsGround; public float wallCheckDistance = 0.3f; // 墙面检测距离 bool grounded; public Transform orientation; float horizontalInput; float verticalInput; Vector3 moveDirection; Rigidbody rb; public MovementState state; public enum MovementState { walking, sprinting, air } private void Start() { rb = GetComponent<Rigidbody>(); rb.freezeRotation = true; } private void Update() { grounded = Physics.Raycast(transform.position, Vector3.down, playerHeight * 0.5f + 0.2f, whatIsGround); MyInput(); StateHandler(); if (grounded) rb.drag = groundDrag; else rb.drag = 0; } private void FixedUpdate() { MovePlayer(); SpeedControl(); } private void MyInput() { horizontalInput = Input.GetAxisRaw("Horizontal"); verticalInput = Input.GetAxisRaw("Vertical"); } private void StateHandler() { if (grounded && Input.GetKey(sprintKey)) { state = MovementState.sprinting; moveSpeed = sprintSpeed; } else if (grounded) { state = MovementState.walking; moveSpeed = walkSpeed; } else { state = MovementState.air; } } void MovePlayer() { // 基础移动方向计算 moveDirection = orientation.forward * verticalInput + orientation.right * horizontalInput; if (moveDirection.magnitude < 0.1f) return; // 无输入时直接返回 // 墙面检测与方向修正 if (grounded && Physics.Raycast(transform.position, moveDirection.normalized, out RaycastHit hit, wallCheckDistance, whatIsGround)) { // 计算平行于墙面的移动方向:移除垂直于墙面法线的分量 Vector3 wallNormal = hit.normal; moveDirection = Vector3.ProjectOnPlane(moveDirection, wallNormal).normalized; } // 施加移动力 if (grounded) rb.AddForce(moveDirection * moveSpeed * 10f, ForceMode.Force); else rb.AddForce(moveDirection * moveSpeed * 10f * airMultiplier, ForceMode.Force); } private void SpeedControl() { Vector3 flatVel = new Vector3(rb.velocity.x, 0f, rb.velocity.z); if (flatVel.magnitude > moveSpeed) { Vector3 limitedVel = flatVel.normalized * moveSpeed; rb.velocity = new Vector3(limitedVel.x, rb.velocity.y, limitedVel.z); } } }
关键修改说明:
- 添加
wallCheckDistance参数,控制墙面检测的距离,避免误判 - 在
MovePlayer中,检测到移动方向有墙面时,用Vector3.ProjectOnPlane将移动方向投影到墙面上,只保留平行分量,实现沿墙滑动 - 调整
FixedUpdate中MovePlayer与SpeedControl的顺序,先施加力再限制速度,逻辑更合理 - 增加无输入时的提前返回,减少无效计算
额外优化建议:
- 如果角色使用胶囊体碰撞器,可改用
Physics.CapsuleCast替代Raycast,墙面检测更贴合角色碰撞体形状 - 根据角色碰撞体大小调整
wallCheckDistance的值,确保检测精度
内容的提问来源于stack exchange,提问作者404
相关产品推荐
相关产品推荐

