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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.28 01:55:19