Unity地面移动时盒子自转且移动受阻问题求助
问题解决:Unity角色碰撞后自转且移动异常
问题描述
制作的可跳跃、前后左右移动的盒子角色,在接触带有Collider的物体时会开始自转,且难以正常移动(仅能小幅移动并陷入小范围转圈)。角色包含以下组件:
- 未设置为Trigger的Box Collider
- 未启用重力的Rigidbody(重力逻辑在自定义脚本中实现)
- 自定义玩家移动脚本(代码如下)
using System.Collections; using System.Collections.Generic; using UnityEngine; [RequireComponent(typeof(Rigidbody))] [RequireComponent(typeof(Collider))] public class PlayerMovement : MonoBehaviour { public float moveSpeed = 9f; public float airMoveSpeed = 0.6f; public float jumpSpeed = 8f; public float gravity = 20f; public float groundDrag = 6f; public float airDrag = 4f; public Camera playerCamera; public float sensitivity = 2f; public float lookXLimit = 45f; public GroundCheck groundCheck; Vector3 moveDirection = Vector3.zero; Rigidbody rb; float rotationX = 0; bool isGrounded; [HideInInspector] public bool canMove = true; void Start() { rb = gameObject.GetComponent<Rigidbody>(); isGrounded = false; // Lock cursor Cursor.lockState = CursorLockMode.Locked; Cursor.visible = false; } void Update() { CheckGround(); MovePlayer(); Gun(); ControlDrag(); } void CheckGround() { isGrounded = groundCheck.isGrounded; } void MovePlayer() { // Recalculate move direction based on axes Vector3 forward = transform.TransformDirection(Vector3.forward); Vector3 right = transform.TransformDirection(Vector3.right); float curSpeedX = canMove ? moveSpeed * Input.GetAxis("Vertical") : 0; float curSpeedY = canMove ? moveSpeed * Input.GetAxis("Horizontal") : 0; moveDirection = (forward * curSpeedX) + (right * curSpeedY); // moveDirection.y -= gravity * 1000; if (Input.GetButton("Jump") && canMove && isGrounded) { rb.AddForce(transform.up * jumpSpeed, ForceMode.Impulse); } // Move the player if(isGrounded) { // transform.position += moveDirection; rb.AddForce(moveDirection * Time.deltaTime, ForceMode.Acceleration); } else { // transform.position += moveDirection; rb.AddForce(moveDirection * Time.deltaTime * airMoveSpeed, ForceMode.Acceleration); } // Player and Camera rotation if (canMove) { rotationX += -Input.GetAxis("Mouse Y") * sensitivity; rotationX = Mathf.Clamp(rotationX, -lookXLimit, lookXLimit); playerCamera.transform.localRotation = Quaternion.Euler(rotationX, 0, 0); transform.rotation *= Quaternion.Euler(0, Input.GetAxis("Mouse X") * sensitivity, 0); } } void ControlDrag() { if(isGrounded) rb.drag = groundDrag; else rb.drag = airDrag; } }
核心问题分析
- Rigidbody未锁定旋转:默认状态下Rigidbody允许旋转,碰撞时物理引擎产生的力矩会让角色自转。
- 移动方向依赖角色自身坐标系:碰撞后角色姿态偏移,移动方向随之旋转,导致陷入转圈。
- 自定义重力未正确施加:脚本中注释了重力相关代码,角色缺少向下的力,碰撞时物理状态异常。
修复步骤
1. 锁定Rigidbody旋转
在Rigidbody组件的Inspector面板中,勾选Freeze Rotation下的X、Y、Z选项;或者在脚本的Start()方法中添加代码锁定:
void Start() { rb = gameObject.GetComponent<Rigidbody>(); // 锁定所有轴的旋转,避免碰撞引发自转 rb.freezeRotation = true; isGrounded = false; // Lock cursor Cursor.lockState = CursorLockMode.Locked; Cursor.visible = false; }
2. 重构移动方向计算逻辑
改为基于相机的世界坐标系计算移动方向,避免角色旋转影响移动路径:
void MovePlayer() { // 基于相机的水平方向计算移动向量,忽略Y轴避免上下偏移 Vector3 cameraForward = playerCamera.transform.forward; Vector3 cameraRight = playerCamera.transform.right; cameraForward.y = 0; cameraRight.y = 0; cameraForward.Normalize(); cameraRight.Normalize(); float curSpeedX = canMove ? moveSpeed * Input.GetAxis("Vertical") : 0; float curSpeedY = canMove ? moveSpeed * Input.GetAxis("Horizontal") : 0; moveDirection = (cameraForward * curSpeedX) + (cameraRight * curSpeedY); // 施加自定义重力 if(!isGrounded) { rb.AddForce(Vector3.down * gravity * Time.deltaTime, ForceMode.Acceleration); } if (Input.GetButton("Jump") && canMove && isGrounded) { rb.AddForce(transform.up * jumpSpeed, ForceMode.Impulse); } // 统一处理地面与空中移动,使用ForceMode.Force更稳定 if(canMove) { float currentMoveSpeed = isGrounded ? 1f : airMoveSpeed; rb.AddForce(moveDirection * currentMoveSpeed * Time.deltaTime, ForceMode.Force); } // 玩家与相机旋转逻辑保持不变 if (canMove) { rotationX += -Input.GetAxis("Mouse Y") * sensitivity; rotationX = Mathf.Clamp(rotationX, -lookXLimit, lookXLimit); playerCamera.transform.localRotation = Quaternion.Euler(rotationX, 0, 0); transform.rotation *= Quaternion.Euler(0, Input.GetAxis("Mouse X") * sensitivity, 0); } }
3. 优化地面检测与Drag设置(可选)
确保GroundCheck脚本的地面检测逻辑准确,避免isGrounded状态误判;如果移动仍有卡顿,可以适当调整groundDrag和airDrag的数值,或者在地面时限制角色的Y轴速度。
内容的提问来源于stack exchange,提问作者Arnaud Girard
相关产品推荐
相关产品推荐

