Unity禁用物理旋转骰子时如何避免其穿入地面?
禁用物理旋转时Unity骰子穿入地面的解决方法
问题描述
在Unity开发场景中,启用物理旋转骰子对象时,骰子可稳定保持在地面平面上;但禁用物理旋转时,旋转过程中骰子有时会有一半穿入地面平面。
场景说明
- 骰子对象与地面平面均带有
Box Collider组件 - 骰子对象包含
Rigidbody组件
当前使用的旋转代码
using UnityEngine; public class RotateDice : MonoBehaviour { public float torque = 50.0f; public Rigidbody rb; public bool usePhysics = false; public bool isRotating = true; void Start() { rb = GetComponent<Rigidbody>(); } void OnMouseDrag() { if ((isRotating)) { if (usePhysics) { rb.AddTorque(Vector3.up * torque * -Input.GetAxis("Mouse X")); rb.AddTorque(Vector3.right * torque * Input.GetAxis("Mouse Y")); } else { rb.useGravity = false; rb.isKinematic = true; float rotationX = Input.GetAxis("Mouse X") * torque * Time.deltaTime; float rotationY = Input.GetAxis("Mouse Y") * torque * Time.deltaTime; transform.Rotate(Vector3.up, rotationX); transform.Rotate(Vector3.right, rotationY); } } } }
解决方法
方法1:旋转后基于碰撞体几何修正位置
适合地面为固定高度平面的场景,每次旋转后直接计算骰子碰撞体尺寸,调整位置让底部贴合地面:
void OnMouseDrag() { if (isRotating) { if (usePhysics) { rb.AddTorque(Vector3.up * torque * -Input.GetAxis("Mouse X")); rb.AddTorque(Vector3.right * torque * Input.GetAxis("Mouse Y")); } else { rb.useGravity = false; rb.isKinematic = true; float rotationX = Input.GetAxis("Mouse X") * torque * Time.deltaTime; float rotationY = Input.GetAxis("Mouse Y") * torque * Time.deltaTime; transform.Rotate(Vector3.up, rotationX); transform.Rotate(Vector3.right, rotationY); // 获取骰子碰撞体,计算底部到中心的垂直距离 BoxCollider diceCollider = GetComponent<BoxCollider>(); float halfHeight = diceCollider.size.y * transform.localScale.y / 2f; // 替换为你场景中地面的实际Y坐标 float groundY = 0f; // 修正位置,确保骰子底部刚好贴合地面 transform.position = new Vector3(transform.position.x, groundY + halfHeight, transform.position.z); } } }
方法2:射线检测动态贴合地面
如果地面有高度变化或不是平面,用射线检测地面位置,动态调整骰子高度:
void OnMouseDrag() { if (isRotating) { if (usePhysics) { rb.AddTorque(Vector3.up * torque * -Input.GetAxis("Mouse X")); rb.AddTorque(Vector3.right * torque * Input.GetAxis("Mouse Y")); } else { rb.useGravity = false; rb.isKinematic = true; float rotationX = Input.GetAxis("Mouse X") * torque * Time.deltaTime; float rotationY = Input.GetAxis("Mouse Y") * torque * Time.deltaTime; transform.Rotate(Vector3.up, rotationX); transform.Rotate(Vector3.right, rotationY); BoxCollider diceCollider = GetComponent<BoxCollider>(); float halfHeight = diceCollider.size.y * transform.localScale.y / 2f; // 从骰子中心向下发射射线,检测地面 Ray ray = new Ray(transform.position, Vector3.down); if (Physics.Raycast(ray, out RaycastHit hit, halfHeight + 0.1f)) { // 调整位置,让骰子底部接触地面 transform.position = new Vector3(transform.position.x, hit.point.y + halfHeight, transform.position.z); } } } }
方法3:绕地面投影点旋转(根源避免穿模)
直接绕骰子在地面的投影点旋转,旋转过程中骰子底部始终不会低于地面:
void OnMouseDrag() { if (isRotating) { if (usePhysics) { rb.AddTorque(Vector3.up * torque * -Input.GetAxis("Mouse X")); rb.AddTorque(Vector3.right * torque * Input.GetAxis("Mouse Y")); } else { rb.useGravity = false; rb.isKinematic = true; float rotationX = Input.GetAxis("Mouse X") * torque * Time.deltaTime; float rotationY = Input.GetAxis("Mouse Y") * torque * Time.deltaTime; // 计算地面上的旋转中心点(骰子在地面的投影) Vector3 pivotPoint = new Vector3(transform.position.x, 0f, transform.position.z); // 绕中心点完成旋转 transform.RotateAround(pivotPoint, Vector3.up, rotationX); transform.RotateAround(pivotPoint, Vector3.right, rotationY); } } }
额外优化
退出拖拽时恢复刚体状态,避免后续物理行为异常:
void OnMouseUp() { if (!usePhysics) { rb.isKinematic = false; rb.useGravity = true; } }
内容的提问来源于stack exchange,提问作者Daniel Lip
相关产品推荐
相关产品推荐

