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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.26 01:48:19