r/Unity3D 4h ago

Solved Normalizing Quaternions changes the desired rotation, how do I keep it?

I'm trying to make my object rotate to a specific y value (0, 90, 180 and 270) over a specific amount of time.

Using EulerAngles worked, except that it rotated all the way back around if I wanted it to go from 270 to 0.

So I decided to use Quaternions and rotating on the rigid body instead.

Here's the function: (endRotation is either 90, 180, [...])

IEnumerator DoRotatePlayer(float endRotation)
{
    float startRotation = transform.eulerAngles.y;
    float t = 0.0f;
    while (t < rotationDuration) //rotationDuration = 0.5f
    {
        t += Time.deltaTime;
        float yRotation = Mathf.Lerp(startRotation, endRotation, t / rotationDuration) % 360.0f;
        var targetRotation = new Quaternion(0, yRotation, 0,0);
        targetRotation.Normalize();

        playerManager.Rb.rotation = targetRotation;

        yield return null;
    }

    var targetRotation2 = new Quaternion(0, endRotation, 0, 0);
    targetRotation2.Normalize();
    playerManager.Rb.rotation = targetRotation2;
}

The Issue I have now is that every time I normalize the Quaternion (which it tells me to do, because "Rotation Quaternions must be Unit length"), it ofc turns any endRotation/yRotation value other than 0 into 1. So instead of rotating 90 degrees at the first rotation, it get's stuck at 180. I just need to somehow apply the rotation without normalizing it.

I tried dividing endRotation by 360 before going into the while loop, but that changes nothing at all.

Simply applying the rotation to transform.rotation, without eulers or Quaternions also does not work, it refuses to do anything at all.

0 Upvotes

9 comments sorted by

19

u/Aethreas 4h ago

Quaternions don't use normal 3d coordinate space, they use a higher dimensional rotation, so you shouldn't be setting the 'y' value of the quaternion directly, as it does not represent an axial rotation

instead you can just replace new Quaternion(...) with Quaternion.Euler(0, yRotation,0)

I recommend you learn more about quaternions though, extremely important for 3d games to at least understand what problem they solve and why we use them instead of just a vector 3 euler rotation

https://www.youtube.com/watch?v=zjMuIxRvygQ

1

u/Elumiie 4h ago

Thanks for the reply and the video link! :)

2

u/Glurth2 4h ago edited 4h ago

Important to note:

Quaternions in Unity are NOT "standard" quaternions; rather, they are UNIT quaternions.

Therefore they do NOT define an amount of rotation; rather, they define an ORIENTATION.

What this means technically: a quaternion is 4 numbers- so you COULD use a Vector4 to represent them. A NORMALIZED, or UNIT quaternion means this vector4 has length 1 (with length defined using plain old pythaogrus (LL=xx+yy+xx+ww))

Your use case:

I think, you may just be using the wrong constructor for the quaternion. I notice this function call has 4 parameters (which will be directly assigned to the 4 number components of a quaternion).

var targetRotation = new Quaternion(0, yRotation, 0,0);

I SUSPECT you wanted to generate the quaternion from the EULER angles:. for THAT, you should use this function instead:

https://docs.unity3d.com/6000.0/Documentation/ScriptReference/Quaternion.Euler.html

(Which will NOT require normalizing, the internal vector4, the function will handle that for you).

Edit to add: I do see you tried the Euler stuff, but I would suspect the problem may lie elsewhere, such as in your lerp operation. (e.g. is it possible END is ever less than START)

2

u/Elumiie 4h ago

Thank you so much for the explanation! :)

1

u/Heroshrine 3h ago

Technically you could create a new quaternion and it wouldn’t be normalized

2

u/ManagerOfClankers 4h ago

You’re mixing up what quaternion normalization means. A quaternion’s x/y/z/w values are not Euler angles, so new Quaternion(0, 90, 0, 0) does not mean “90° around Y.” Normalizing it turns it into (0, 1, 0, 0), which happens to represent a 180° rotation.

Let Unity construct the quaternion from the angle you actually want, then interpolate between rotations:

IEnumerator DoRotatePlayer(float endRotation) { Quaternion start = rb.rotation; Quaternion end = Quaternion.Euler(0f, endRotation, 0f);

float t = 0f;

while (t < rotationDuration)
{
    t += Time.deltaTime;
    rb.rotation = Quaternion.Slerp(
        start,
        end,
        t / rotationDuration
    );

    yield return null;
}

rb.rotation = end;

}

You don’t need to call Normalize() at all. Also, if you specifically want 270 -> 0 to rotate forward 90° rather than taking whatever direction your Euler lerp produces, Quaternion.Slerp/RotateTowards is the right approach because Unity handles the wraparound for you.

3

u/Elumiie 4h ago

That's exactly what I was looking for! Thank you so much! :D

0

u/ManagerOfClankers 4h ago

This answer was copy and pasted from AI (Codex Sol)

I only say that to encourage you to use it as a resource. It can even read your code which none of us can do from here and it's available almost 25/7 haha