Unity 3D Quaternion Rotations:帮助我避免 Gimbal lock 并进行平滑旋转

Unity 3D Quaternion Rotations: Help me to avoid Gimbal lock and make a smoth rotation

我目前正在开发一款手机游戏,它是一个相当简单的核心机制,你从一个 3D 立方体开始,每一轮你都会得到一个新的立方体,需要将其放置或捕捉到一个人的脸上场景中的立方体。您可以 select 场景中立方体的不同面,预览立方体将在该面附近生成,因此您可以旋转它并找到所需的位置。当您对自己的选择感到满意时,只需点击 "build cube button" 预览立方体就会消失,具有相同值和旋转度的新立方体将出现在 selected 面上。 我只需要将对象围绕 selected 轴旋转 90 或 -90 度。就是这样,但是没用呵呵,如果有人可以帮助我,那将是很多,这困扰了我好几天了。谢谢!

public class CubeBehaviour : MonoBehaviour
{
    private Movement _movement;

    private void Awake()
    {
        if (!GetComponent<Movement>())
            Debug.LogWarning("Please add a movement script to the gameobject.");

        _movement = GetComponent<Movement>();
    }

    public void Move(Vector3[] positions, Action callBack)
    {
        _movement.StartMove(positions,callBack);
    }

    [ContextMenu("Rotate X Positive")]
    public void RotateAround_xAxisPositive()
    {
        Rotate(RotationAxis.X, true);
    }

    [ContextMenu("Rotate X Negative")]
    public void RotateAround_xAxisNegative()
    {
        Rotate(RotationAxis.X, false);
    }

    [ContextMenu("Rotate Y Positive")]
    public void RotateAround_yAxisPositive()
    {
        Rotate(RotationAxis.Y, true);
    }

    [ContextMenu("Rotate Y Negative")]
    public void RotateAround_yAxisNegative()
    {
        Rotate(RotationAxis.Y, false);
    }

    [ContextMenu("Rotate Z Positive")]
    public void RotateAround_zAxisPositive()
    {
        Rotate(RotationAxis.Z, true);
    }

    [ContextMenu("Rotate Z Negative")]
    public void RotateAround_zAxisNegative()
    {
        Rotate(RotationAxis.Z, false);
    }

    public void Rotate(RotationAxis axis, bool positiveRotation)
    {
        StartCoroutine(DoRotation(axis,90, positiveRotation));
    }

    IEnumerator DoRotation(RotationAxis axis,float angles, bool positiveRotation)
    {
        float time = 1;
        float elapsedTime = 0;
        Quaternion destinationRotation = Quaternion.identity;
        Vector3 anglesToRotate = Vector3.zero;

        switch (axis)
        {
            case RotationAxis.X:
                if (positiveRotation)
                    anglesToRotate = new Vector3(90, 0, 0);
                else
                    anglesToRotate = new Vector3(-90, 0, 0);
                break;
            case RotationAxis.Y:
                if (positiveRotation)
                    anglesToRotate = new Vector3(0, 90, 0);
                else
                    anglesToRotate = new Vector3(0, -90, 0);
                break;
            case RotationAxis.Z:
                if (positiveRotation)
                    anglesToRotate = new Vector3(0, 0, 90);
                else
                    anglesToRotate = new Vector3(0, 0, -90);
                break;
            default:
                Debug.LogError("You must assign a rotation axis!");
                break;
        }
        destinationRotation *= this.transform.rotation * Quaternion.Euler(anglesToRotate);

        Quaternion yRotation = Quaternion.identity;
        Quaternion xRotation = Quaternion.identity;
        Quaternion zRotation = Quaternion.identity;


        while (elapsedTime < time)
        {
            yRotation = Quaternion.AngleAxis(anglesToRotate.y * Time.deltaTime, Vector3.up);
            xRotation = Quaternion.AngleAxis(anglesToRotate.x * Time.deltaTime, Vector3.right);
            zRotation = Quaternion.AngleAxis(anglesToRotate.z * Time.deltaTime, Vector3.forward);

            this.transform.rotation = yRotation * xRotation * zRotation * this.transform.rotation;

            elapsedTime += Time.deltaTime;
            yield return null;
        }
        //this.transform.rotation = yRotation * xRotation * zRotation;
        //this.transform.rotation = destinationRotation;
    }
}

public enum RotationAxis
{
    X,
    Y,
    Z
}

你永远不应该像这样设置旋转,它根本不起作用,Unity 文档也会对此发出警告。

this.transform.rotation = yRotation * xRotation * zRotation * this.transform.rotation;

如果您一次旋转一个轴,则可以使用。

transform.Rotate(90.0f ,0.0f,0.0f,Space.World);

https://docs.unity3d.com/ScriptReference/Transform.Rotate.html

您也可以使用 LookAt

transform.LookAt(targetCube);

https://docs.unity3d.com/ScriptReference/Transform.LookAt.html

您也可以使用Quaternion.Lerp

https://docs.unity3d.com/ScriptReference/Quaternion.Lerp.html

一般来说,Unity 中的旋转可能很棘手,尤其是当它们在检查器中看起来很容易时。

public class TestROtate : MonoBehaviour
{
[SerializeField] float duration = 1; // seconds, must be >0.0f
Quaternion targetRotation = Quaternion.identity;

[ContextMenu("Rotate X Positive")]
public void RotateAround_xAxisPositive()
{
    targetRotation *= Quaternion.Euler(90, 0, 0);
    StopAllCoroutines();
    StartCoroutine(DoRotation());
}

[ContextMenu("Rotate Y Positive")]
public void RotateAround_yAxisPositive()
{
    targetRotation *= Quaternion.Euler(0, 90, 0);
    StopAllCoroutines();
    StartCoroutine(DoRotation());
}

[ContextMenu("Rotate Z Positive")]
public void RotateAround_zAxisPositive()
{
    targetRotation *= Quaternion.Euler(0, 0, 90);
    StopAllCoroutines();
    StartCoroutine(DoRotation());
}


// other rotate methods

IEnumerator DoRotation()
{
    float currentTime = 0;
    Quaternion startRotation = transform.rotation;

    while (currentTime < 1)
    {
        currentTime += Time.deltaTime / duration;

        transform.rotation = Quaternion.Slerp(startRotation, targetRotation, 
 currentTime);

            yield return null;
        }
        transform.rotation = targetRotation;
    }
 }