简体   繁体   中英

Unity messes up X-axis rotation

I have a very simple script that I wish to rotate something on the X-axis, and I'd expect the other two axes to stay put(they flip between 0 and 180), and only X to change.

Below you can see the code intended to do just that.

    public class TestScript : MonoBehaviour
    {
        private void Start()
        {
            transform.eulerAngles = Vector3.zero;
            Debug.Log($"start rotation: {transform.eulerAngles}");
        }

        private void Update()
        {
            float time = (Time.time - 1f) * 10f;
            if (time < 0f || time > 1f)
            {
                return;
            }
            Vector3 rotation = transform.eulerAngles;
            float x = Mathf.Lerp(170f, 0, time);
            rotation.x = x;
            transform.eulerAngles = rotation;
            Debug.Log($"rotation: {transform.eulerAngles}, x: {x}");
        }
    }

The output from the console. You can clearly see that the rotation does not go from 170 to 0, but from 0 to 90 and then back to 0.

结果

Now, I'm pretty sure this has something to do with quaternions and their identity, but not sure how can this be avoided

PS: The same idea but for Y and Z works just fine.

Now, I'm pretty sure this has something to do with quaternions

Well the real culprit is Euler angles.

If we take a look at your code:

Vector3 rotation = transform.eulerAngles;
float x = Mathf.Lerp(170f, 0, time);
rotation.x = x;
transform.eulerAngles = rotation;
Debug.Log($"rotation: {transform.eulerAngles}, x: {x}");

...we can see you are performing rotations via transform.eulerAngles . The thing about 3D rotations is that you should avoid using Euler due to their limitations and problems (gimbal lock anyone) and use quaternions instead. The latter is the source of truth.

Unity (my emphasis):

When you read the.eulerAngles property, Unity converts the Quaternion's internal representation of the rotation to Euler angles. Because, there is more than one way to represent any given rotation using Euler angles, the values you read back out may be quite different from the values you assigned. This can cause confusion if you are trying to gradually increment the values to produce animation.

...which is exactly what is happening with your code.

Consider this:

在此处输入图像描述

Notice anything about the 23.5 and the 156.5 ?

23.5 + 156.5 = 180

In other words both will lead to the same rotation as per "there is more than one way to represent any given rotation" .

An arguable simpler approach is:

public class RotateWithTime : MonoBehaviour
{
    [SerializeField,Tooltip("Rotation rate in degrees/second")] 
    private Vector3 rotationSpeed; // e.g. (30,0,0) for 30 rads/sec X-only

    private void Reset()
    {
        rotationSpeed = Vector3.zero;
    }

    // Update is called once per frame
    void Update()
    {
        var amount = rotationSpeed * Time.deltaTime;
        transform.Rotate(amount);
    }
}

In order to avoid this question becoming a chameleon question due to the lack of info in the initial question, I have asked a new one and will be closing this one. You can find it here

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM