Unity 3D刚体非预期轴向吸附与冲量响应异常问题排查
Unity刚体Velocity异常问题排查与解决
Unity版本:2022.3.11f1,3D URP项目
开发简易玩家控制器时遇到两个刚体Velocity异常问题:
问题一:移动旋转时速度吸附至世界X轴
- 黄线代表Velocity,红线代表W键输入结合鼠标方向的推力
- 现象:移动中旋转玩家刚体,速度/方向突然吸附到世界X轴
- 疑问:为何速度会吸附到世界轴向?如何解决?
问题二:跳跃时出现额外Z轴推力与轨迹偏转
- 黄线代表Velocity,红线代表W键输入结合鼠标方向的推力
- 现象:按空格键施加Y轴冲量时,刚体受到世界Z轴方向额外推力;轨迹末端出现类似碰撞隐形墙体的偏转
- 疑问:为何设置世界Y轴冲量后速度立即向Z轴倾斜?轨迹末端偏转原因是什么?
资源结构
CAMERA
- 父物体:CameraHolder,挂载MoveCamera脚本
using System.Collections; using System.Collections.Generic; using UnityEngine; public class MoveCamera : MonoBehaviour { public Transform cameraPosition; void Update() { transform.position = cameraPosition.position; } }
- 子物体:PlayerCam(主相机),挂载PlayerCam脚本
using System.Collections; using System.Collections.Generic; using UnityEngine; public class PlayerCam : MonoBehaviour { public float sensX; public float sensY; public Transform orientation; float xRotation; float yRotation; void Start() { Cursor.lockState = CursorLockMode.Locked; Cursor.visible = false; } void Update() { float mouseX = Input.GetAxisRaw("Mouse X") * Time.deltaTime * sensX; float mouseY = Input.GetAxisRaw("Mouse Y") * Time.deltaTime * sensY; yRotation += mouseX; xRotation -= mouseY; xRotation = Mathf.Clamp(xRotation, -90f, 90f); transform.rotation = Quaternion.Euler(xRotation, yRotation, 0); orientation.rotation = Quaternion.Euler(0, yRotation, 0); } }
PLAYER
- 父物体:Player,挂载Rigidbody和PlayerMovement脚本
using System.Collections; using System.Collections.Generic; using UnityEngine; public class PlayerMovement : MonoBehaviour { [Header("Movement")] public float moveSpeed; float horizontalInput; float verticalInput; Vector3 moveDirection; Rigidbody rb; public float groundDrag; public float jumpForce; public float jumpCooldown; public float airMultiplier; bool readyToJump = true; [Header("Keybinds")] public KeyCode jumpKey = KeyCode.Space; [Header("Ground Check")] public float playerHeight; public LayerMask whatIsGround; public Transform orientation; bool grounded; void Start() { rb = GetComponent<Rigidbody>(); rb.freezeRotation = true; } void Update() { Debug.DrawRay(rb.position, rb.velocity, Color.yellow); Debug.DrawRay(rb.position, rb.GetAccumulatedForce(), Color.red); grounded = Physics.Raycast(transform.position, Vector3.down, playerHeight * 0.5f + 0.2f, whatIsGround); MyInput(); SpeedControl(); rb.drag = grounded ? groundDrag : 0; } private void FixedUpdate() { MovePlayer(); } private void MyInput() { horizontalInput = Input.GetAxis("Horizontal"); verticalInput = Input.GetAxis("Vertical"); if (Input.GetKey(jumpKey) && readyToJump && grounded) { readyToJump = false; Jump(); Invoke(nameof(ResetJump), jumpCooldown); } } private void MovePlayer() { moveDirection = orientation.forward * verticalInput + orientation.right * horizontalInput; Debug.Log("Orientation --- " + orientation.position); Debug.Log("Orientation --- " + orientation.forward); Debug.DrawLine(rb.position, moveDirection, Color.green); var force = moveDirection.normalized * moveSpeed * 10f; if (!grounded) force *= airMultiplier; rb.AddForce(force, ForceMode.Force); } private void SpeedControl() { Vector3 flatVelocity = new Vector3(rb.velocity.x, 0f, rb.velocity.y); if(flatVelocity.magnitude > moveSpeed) { Vector3 limitedVelocity = flatVelocity.normalized * moveSpeed; rb.velocity = new Vector3(limitedVelocity.x, rb.velocity.y, limitedVelocity.z); } } private void Jump() { rb.velocity = new Vector3(rb.velocity.x, 0f, rb.velocity.z); rb.AddForce(transform.up * jumpForce, ForceMode.Impulse); } private void ResetJump() { readyToJump = true; } }
- 子物体:PlayerObj,挂载Capsule Collider
- 子物体:Orientation,仅用于方向控制的Transform
- 子物体:CameraPos,仅用于位置控制的Transform
问题排查与解决方案
针对问题一:速度吸附至世界X轴
问题根源在SpeedControl方法中的向量构造错误:
// 错误代码:混淆了速度轴向,将Y分量赋值给Z Vector3 flatVelocity = new Vector3(rb.velocity.x, 0f, rb.velocity.y);
旋转玩家时,原代码会把Y轴速度(地面移动时Y速度为0)赋值给Z分量,导致平面向量仅剩X轴分量,限制速度后就会吸附到世界X轴。修正为:
// 正确代码:保留X、Z平面分量,Y置0 Vector3 flatVelocity = new Vector3(rb.velocity.x, 0f, rb.velocity.z);
针对问题二:跳跃时Z轴额外推力与轨迹偏转
Z轴额外推力原因:
同样来自SpeedControl的轴向错误,跳跃后Y轴有速度,原代码会把跳跃的Y速度错误赋值给Z分量,导致限制速度时给Z轴增加了额外速度。修正上述SpeedControl的向量构造即可解决。轨迹末端偏转原因:
按以下步骤排查:- 检查场景中是否存在隐藏碰撞体:在场景视图勾选Gizmos中的碰撞体显示,确认无意外的地形、测试碰撞盒;
- 调整刚体碰撞检测模式:将Rigidbody的碰撞检测模式设为
Continuous Dynamic,避免高速移动时碰撞检测遗漏; - 校验地面检测LayerMask:确保
whatIsGround仅包含地面层,避免误判空中状态导致错误施加地面拖拽或移动力; - 检查Orientation物体:确认其仅受
PlayerCam脚本控制旋转,无额外父物体或脚本影响其姿态。
内容的提问来源于stack exchange,提问作者Logan Alexander Thies
相关产品推荐
相关产品推荐

