m0_65005289 2025-11-24 12:56 采纳率: 0%
浏览 9

Unity人物吸附在墙上然后无法位移

Unity3D 为什么我的人物加了rigbody和胶囊碰撞体,墙加了盒子碰撞体,为什么人物靠近墙就会被吸附过去,然后就无法行走位移,但是动画啥的都还能播放。

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

public class Player : MonoBehaviour
{
    [Header("移动参数")]
    [SerializeField] float moveSpeed = 10.0f;
    [SerializeField] float runSpeed = 15.0f;
    [SerializeField] float jumpForce = 5.0f;
    [SerializeField] bool isGrounded = true;

    [Header("相机设置")]
    [SerializeField] private List<Camera> cameras = new List<Camera>();

    private bool isRunning = false;
    private Vector3 moveDir;
    private Camera playerCamera;

    private Rigidbody rb;
    private Animator ani;

    private int currentCameraIndex = 0;

    // 添加一个变量来跟踪是否有输入
    private bool hasInput = false;

    void Start()
    {
        rb = GetComponent<Rigidbody>();
        ani = GetComponent<Animator>();

        // 确保刚体没有位置冻结
        rb.constraints = RigidbodyConstraints.FreezeRotation;

        // 初始化所有动画状态为Idle
        ResetAnimationStates();

        // 初始化相机
        InitializeCameras();

        //Debug.Log("玩家初始化完成,初始状态应为Idle");
    }

    void Update()
    {
        HandleInput();
        HandleCameraSwitch();
        UpdateAnimation(); 
    }

    void FixedUpdate()
    {
        Move();
    }

    void InitializeCameras()
    {
        // 激活第一个相机,禁用其他相机
        for (int i = 0; i < cameras.Count; i++)
        {
            if (cameras[i] != null)
            {
                cameras[i].gameObject.SetActive(i == 0);

                BaseCamera baseCamera = cameras[i].GetComponent<BaseCamera>();
                if (baseCamera != null)
                {
                    baseCamera.SetPlayerTransform(transform);
                }
            }
        }

        // 设置当前使用的相机
        if (cameras.Count > 0 && cameras[0] != null)
        {
            playerCamera = cameras[0];
        }
    }

    void HandleCameraSwitch()
    {
        if (Input.GetKeyDown(KeyCode.Tab) && cameras.Count > 1)
        {
            // 禁用当前相机
            if (cameras[currentCameraIndex] != null)
            {
                cameras[currentCameraIndex].gameObject.SetActive(false);
            }

            // 切换到下一个相机
            currentCameraIndex = (currentCameraIndex + 1) % cameras.Count;

            // 激活新相机
            if (cameras[currentCameraIndex] != null)
            {
                cameras[currentCameraIndex].gameObject.SetActive(true);
                playerCamera = cameras[currentCameraIndex];
            }

            Debug.Log($"切换到相机: {currentCameraIndex + 1}");
        }
    }

    void HandleInput()
    {
        float horizontal = Input.GetAxis("Horizontal");
        float vertical = Input.GetAxis("Vertical");

        // 检查是否有有效输入 - 使用更严格的阈值
        hasInput = (Mathf.Abs(horizontal) > 0.01f || Mathf.Abs(vertical) > 0.01f);

        if (playerCamera != null)
        {
            Vector3 cameraForward = playerCamera.transform.forward;
            Vector3 cameraRight = playerCamera.transform.right;

            cameraForward.y = 0;
            cameraRight.y = 0;
            cameraForward.Normalize();
            cameraRight.Normalize();

            moveDir = (cameraForward * vertical + cameraRight * horizontal).normalized;
        }
        else
        {
            // 如果没有相机,使用世界坐标方向
            moveDir = new Vector3(horizontal, 0, vertical).normalized;
        }

        // 检测R键按下切换跑步状态
        if (Input.GetKeyDown(KeyCode.R))
        {
            isRunning = !isRunning;
            Debug.Log($"跑步状态切换: {isRunning}");
        }
    }

    void Move()
    {
        if (moveDir.magnitude >= 0.1f && hasInput)
        {
            float currentSpeed = isRunning ? runSpeed : moveSpeed;

            Vector3 movement = moveDir * currentSpeed * Time.fixedDeltaTime;
            rb.MovePosition(transform.position + movement);


        }
    }
    void UpdateAnimation()
    {
        // 调试信息
        //if (Input.GetKeyDown(KeyCode.W) || Input.GetKeyDown(KeyCode.A) || Input.GetKeyDown(KeyCode.S) || Input.GetKeyDown(KeyCode.D) ||
        //    Input.GetKeyDown(KeyCode.UpArrow) || Input.GetKeyDown(KeyCode.LeftArrow) || Input.GetKeyDown(KeyCode.DownArrow) || Input.GetKeyDown(KeyCode.RightArrow))
        //{
        //    Debug.Log($"按键按下 - 输入状态: {hasInput}, 跑步状态: {isRunning}");
        //}

        // 根据输入状态和跑步状态更新动画
        if (hasInput)
        {
            if (isRunning)
            {
                // 跑步状态
                SetAnimationState(false, true, false);
                Debug.Log("切换到跑步状态");
            }
            else
            {
                // 行走状态
                SetAnimationState(true, false, false);
                Debug.Log("切换到行走状态");
            }
        }
        else
        {
            // 空闲状态
            SetAnimationState(false, false, true);
        }
    }

    void SetAnimationState(bool walking, bool running, bool idle)
    {
        ani.SetBool("isWalking", walking);
        ani.SetBool("isRunning", running);
        ani.SetBool("isIdle", idle);
    }

    void ResetAnimationStates()
    {
        // 重置所有动画状态,确保初始状态为Idle
        ani.SetBool("isWalking", false);
        ani.SetBool("isRunning", false);
        ani.SetBool("isIdle", true);

        // 强制Animator立即更新状态
        ani.Update(0f);

        Debug.Log("动画状态已重置为Idle");
    }
}

  • 写回答

3条回答 默认 最新

  • 檀越@新空间 2025-11-24 13:06
    关注

    下午好🌅🌅🌅
    本答案参考通义千问

    你提到的问题是:Unity中人物角色在靠近墙时被吸附过去,然后无法位移,但动画可以正常播放。这通常是因为 Rigidbody碰撞体(Collider) 的交互出现了问题。


    🧠 问题原因分析:

    1. Rigidbody 和 Collider 的物理交互异常

      • 如果你的角色和墙壁的碰撞体之间存在穿透错误的物理响应,可能会导致角色“卡”在墙上。
      • Unity 的物理引擎会自动处理碰撞,但如果角色速度过快或有其他物理约束,可能导致角色“穿模”或“吸附”。
    2. Rigidbody 的设置不当

      • RigidbodyConstraints 设置可能限制了角色的移动方向,比如 Freeze Position X/ZFreeze Rotation 等。
      • 如果 Rigidbody 被设置为 Kinematic,则不会受到物理力的影响,但此时你需要手动控制位置。
    3. 碰撞体(Collider)配置错误

      • 墙壁的 Box Collider 可能没有正确设置为 Is Trigger,或者角色的 Capsule Collider 没有正确检测到碰撞。
      • 如果角色的 Capsule Collider 太小,也可能导致碰撞检测失败。
    4. MovePosition 使用不当

      • FixedUpdate() 中使用 rb.MovePosition() 是正确的做法,但如果 moveDir 不合理或方向错误,可能导致角色“贴墙”。

    ✅ 解决方案

    1. 检查 Rigidbody 设置

    • 确保 Rigidbody 没有冻结位置

      rb.constraints = RigidbodyConstraints.FreezeRotation;
      

      这行代码只冻结了旋转,没有冻结位置,这是合理的。

    • 不要将 Rigidbody 设置为 Kinematic: 如果你设置了 rb.isKinematic = true;,那么角色将不再受物理影响,需要手动控制位置。


    2. 检查 Capsule Collider 和 Box Collider

    • 确保角色的 Capsule Collider 与墙壁的 Box Collider 正确碰撞

      • 检查角色的 Capsule Collider 是否与墙壁的 Box Collider 有重叠区域。
      • 如果角色太小,尝试增大 Capsule Collider 的半径或高度。
    • 检查是否设置为 Is Trigger

      • 如果墙壁的 Box Collider 设置为 Is Trigger,则不会产生物理碰撞,只会触发事件(如 OnTriggerEnter)。
      • 如果你不希望墙壁触发事件,应关闭 Is Trigger

    3. 检查 MoveDirection 逻辑

    • 确保 moveDir 是基于世界坐标还是相机方向

      • 你当前的逻辑是基于相机方向计算 moveDir,这可能是正确的,但要确保 playerCamera 正确引用了摄像机。
      • 你可以添加调试信息来查看 moveDir 的值:
        Debug.Log("Move Direction: " + moveDir);
        
    • 避免角色在墙边“卡住”

      • 如果角色在墙边移动时被“吸附”,可能是由于 moveDir 方向与墙壁法线方向冲突。
      • 尝试在移动前检查是否有碰撞,例如:
        if (Physics.Raycast(transform.position, moveDir, out RaycastHit hit, 0.1f))
        {
            // 遇到障碍物,调整移动方向
            moveDir = Vector3.zero;
        }
        

    4. 优化 Move() 方法

    • 修改 Move() 方法以避免角色卡在墙上
      void Move()
      {
          if (moveDir.magnitude >= 0.1f && hasInput)
          {
              float currentSpeed = isRunning ? runSpeed : moveSpeed;
      
              // 防止角色“贴墙”
              if (Physics.SphereCast(transform.position, 0.5f, moveDir, out RaycastHit hit, 0.1f))
              {
                  // 如果碰到物体,停止移动
                  moveDir = Vector3.zero;
              }
      
              Vector3 movement = moveDir * currentSpeed * Time.fixedDeltaTime;
              rb.MovePosition(transform.position + movement);
          }
      }
      

    5. 调试建议

    • 添加调试日志

      void Update()
      {
          HandleInput();
          HandleCameraSwitch();
          UpdateAnimation(); 
      
          Debug.Log("Move Dir: " + moveDir);
          Debug.Log("Is Grounded: " + isGrounded);
      }
      
    • 检查角色是否真的在地面上

      • 如果 isGroundedtrue,但角色仍然“浮空”,可能是地面的 Collider 没有正确设置。

    ✅ 修改后的完整代码(重点部分加粗)

    using System.Collections;
    using System.Collections.Generic;
    import UnityEngine;
    
    public class Player : MonoBehaviour
    {
        [Header("移动参数")]
        [SerializeField] float moveSpeed = 10.0f;
        [SerializeField] float runSpeed = 15.0f;
        [SerializeField] float jumpForce = 5.0f;
        [SerializeField] bool isGrounded = true;
    
        [Header("相机设置")]
        [SerializeField] private List<Camera> cameras = new List<Camera>();
    
        private bool isRunning = false;
        private Vector3 moveDir;
        private Camera playerCamera;
    
        private Rigidbody rb;
        private Animator ani;
    
        private int currentCameraIndex = 0;
    
        // 添加一个变量来跟踪是否有输入
        private bool hasInput = false;
    
        void Start()
        {
            rb = GetComponent<Rigidbody>();
            ani = GetComponent<Animator>();
    
            // 确保刚体没有位置冻结
            rb.constraints = RigidbodyConstraints.FreezeRotation;
    
            // 初始化所有动画状态为Idle
            ResetAnimationStates();
    
            // 初始化相机
            InitializeCameras();
    
            Debug.Log("玩家初始化完成,初始状态应为Idle");
        }
    
        void Update()
        {
            HandleInput();
            HandleCameraSwitch();
            UpdateAnimation(); 
    
            Debug.Log("Move Dir: " + moveDir);
            Debug.Log("Is Grounded: " + isGrounded);
        }
    
        void FixedUpdate()
        {
            Move();
        }
    
        void InitializeCameras()
        {
            // 激活第一个相机,禁用其他相机
            for (int i = 0; i < cameras.Count; i++)
            {
                if (cameras[i] != null)
                {
                    cameras[i].gameObject.SetActive(i == 0);
    
                    BaseCamera baseCamera = cameras[i].GetComponent<BaseCamera>();
                    if (baseCamera != null)
                    {
                        baseCamera.SetPlayerTransform(transform);
                    }
                }
            }
    
            // 设置当前使用的相机
            if (cameras.Count > 0 && cameras[0] != null)
            {
                playerCamera = cameras[0];
            }
        }
    
        void HandleCameraSwitch()
        {
            if (Input.GetKeyDown(KeyCode.Tab) && cameras.Count > 1)
            {
                // 禁用当前相机
                if (cameras[currentCameraIndex] != null)
                {
                    cameras[currentCameraIndex].gameObject.SetActive(false);
                }
    
                // 切换到下一个相机
                currentCameraIndex = (currentCameraIndex + 1) % cameras.Count;
    
                // 激活新相机
                if (cameras[currentCameraIndex] != null)
                {
                    cameras[currentCameraIndex].gameObject.SetActive(true);
                    playerCamera = cameras[currentCameraIndex];
                }
    
                Debug.Log($"切换到相机: {currentCameraIndex + 1}");
            }
        }
    
        void HandleInput()
        {
            float horizontal = Input.GetAxis("Horizontal");
            float vertical = Input.GetAxis("Vertical");
    
            // 检查是否有有效输入 - 使用更严格的阈值
            hasInput = (Mathf.Abs(horizontal) > 0.01f || Mathf.Abs(vertical) > 0.01f);
    
            if (playerCamera != null)
            {
                Vector3 cameraForward = playerCamera.transform.forward;
                Vector3 cameraRight = playerCamera.transform.right;
    
                cameraForward.y = 0;
                cameraRight.y = 0;
                cameraForward.Normalize();
                cameraRight.Normalize();
    
                moveDir = (cameraForward * vertical + cameraRight * horizontal).normalized;
            }
            else
            {
                // 如果没有相机,使用世界坐标方向
                moveDir = new Vector3(horizontal, 0, vertical).normalized;
            }
    
            // 检测R键按下切换跑步状态
            if (Input.GetKeyDown(KeyCode.R))
            {
                isRunning = !isRunning;
                Debug.Log($"跑步状态切换: {isRunning}");
            }
        }
    
        void Move()
        {
            if (moveDir.magnitude >= 0.1f && hasInput)
            {
                float currentSpeed = isRunning ? runSpeed : moveSpeed;
    
                // 防止角色“贴墙”
                if (Physics.SphereCast(transform.position, 0.5f, moveDir, out RaycastHit hit, 0.1f))
                {
                    // 如果碰到物体,停止移动
                    moveDir = Vector3.zero;
                }
    
                Vector3 movement = moveDir * currentSpeed * Time.fixedDeltaTime;
                rb.MovePosition(transform.position + movement);
            }
        }
    
        void UpdateAnimation()
        {
            // 动画更新逻辑...
        }
    
        void ResetAnimationStates()
        {
            // 初始化动画状态
        }
    }
    

    📌 总结

    | 问题 | 解决方法 | |------|----------| | 角色吸附在墙上 | 检查 Rigidbody 和 Collider 设置,避免穿透;使用 SphereCast 防止贴墙 | | 无法位移 | 确保 Rigidbody 没有冻结位置;检查 moveDir 计算逻辑 | | 动画可播放但无法移动 | 检查是否误用了 isKinematic;确认物理引擎正常工作 |

    如果你还有其他问题,欢迎继续提问!

    评论

报告相同问题?

问题事件

  • 创建了问题 11月24日