How to go from localEulerAngles.x +20 degrees to -20 degrees ?

Hi,

I’m trying to leap a x rotation value from +20 to -20 degrees but the transform spins the long way around.

This is the line of code in a while loop, v is 2 seconds.

transform.rotation = Quaternion.Euler(Mathf.Lerp(incomingLocalEulerAnglesX, -20, v), transform.localEulerAngles.y, transform.localEulerAngles.z);

Where incomingLocalEulerAnglesX is 20 degrees.

How would I leap it the shortest way ?

Cheers.

@Griffo , this is a common problem and it’s solution isn’t terribly difficult but not a single line either. I recommend looking at some available assets that perform object rotation. The Unity Standard Assets come to mind, especially the character controllers that turn the character based on mouse movement (mouse look). Also, I know that googling this in the past has returned several good threads. Sorry I can’t offer more.

Hmm… I think it is actually a one-liner. Just use Mathf.LerpAngle instead of Mathf.Lerp.

…Though actually, Lerping from 20 to -20 as you claim would not go the long way around. It would go down through 19, 18, 5, 0, -5, etc. it doesn’t matter in this particular case that the number is an angle. (Though since it is, LerpAngle is the correct thing to use in general.)

So, there’s something else going on. Please post the whole loop.

Good catch! I hadn’t seen LerpAngle before.

Another thing to note, though it has nothing to do with your rotation problem. You said v = 2 seconds. All lerp time values are clamped between 0 and 1. So you need to make sure you normalize your time to that value. That is, if v takes on a value from 0->2 makes sure you Lerp on v/2. If you just pass in v directly the lerp will work correctly from 0->1 seconds… then from 1->2 seconds it will just clamp the value back down to one. So effectively your rotation will happen 2x as fast as you want… then sit there at the end for 1 second.

You should probably perform the lerp between two quarternions, not two Euler angles.

Well, it’s fine if he’s doing a 2D rotation, where there is really only one scalar value to worry about.

I just don’t trust eulers. There are some points in rotation space, like rotation through straight up, where this will fail.

OK, because you so rarely spew nonsense, I’d like to pursue this a little further if you’re up for it. :slight_smile:

I agree that Euler angles are bad mojo when dealing with 3D rotations. But when you’re talking about rotation in a 2D plane — say, because you’re making a typical sprite game where everything is in the XY plane — then rotation is completely described by a single scalar (which is a rotation around the Z axis). I don’t believe this can ever fail.

And while I loves me some quaternions for handling 3D rotations, and they would obviously work for 2D rotations too, they seem like overkill. A single scalar value is a heckuva lot easier to grasp and apply.

So, can you give an example of where/how a Z rotation value could cause a problem?

(Of course feel free to tell me to buzz off if the topic has gotten dull.)

I’m going to have to pull up Unity and text it again before making any assertions. Let me get back to you.

There was one case that messed me up on Eulers and made me wary of the whole system.

But having you disagree with me makes me doubt my sanity. :wink:

Thanks everyone for the input, much appreciated …

j = 0;
var transformRotY : float = transform.localEulerAngles.y;
var transformRotZ : float = transform.localEulerAngles.z;

while(j < 1)
{
    j += time.deltaTime/2;
    j = Mathf.Clamp(j, 0, 1);

    transform.rotation = Quaternion.Euler(Mathf.LerpAngle(transform.localEulerAngles.x, -20, j), transformRotY, transformRotZ);
    yield;
}

The above code works for me, but I now have another problem with rotating the transform to fave an object, using Quaternion.Slerp as I led to believe

is a spherical linear interpolation. The interpolation is mapped as
though on a quarter segment of a circle so you get the slow out and slow in effect, the distant between each step is not equidistant

I don’t want that, so I used Quaternion.Lerp as I believe

is linear interpolation so that the distant between each step is
equal across the entire interpolation

But it seem to act the same, slowing down before reaching the end … The code I’m using is …

angleFinderLookAtFlightPosition = waypointScript.flightPoint - angleFinder.position;
rotation = Quaternion.LookRotation(angleFinderLookAtFlightPosition);
angleFinder.rotation = Quaternion.Lerp(angleFinder.rotation, rotation, time.deltaTime * 2);

Any reason why this would happen ?

thanks.

Ugh, yes, it’s because you’re abusing Lerp. This is a very common mistake (probably because the Unity docs commit the same sin, IIRC). Please go read and take the time to fully grok this article on using Lerp properly.

@JoeStrout … Thanks for pointing me in the right direction …

startTime = Time.time;

while(angleFinder.rotation != rotation)
{
    lerpTime = (Time.time - startTime) / 3;
    angleFinderLookAtFlightPosition = waypointScript.flightPoint - angleFinder.position;
    rotation = Quaternion.LookRotation(angleFinderLookAtFlightPosition);
    angleFinder.rotation = Quaternion.Lerp(angleFinder.rotation, rotation, (Time.time - startTime) * lerpTime);
    yield;
}

Still abusing Lerp.

It’s easy to spot Lerp abuse, because your first parameter to the Lerp call is the same thing as what you’re assigning to. Any time you see “foo = Lerp(foo, bar, baz)”, somebody doesn’t understand how Lerp works. (Or on rare occasions they do understand and are abusing it on purpose, fully accepting that it won’t be a linear interpolation at all.)

I’m not a huge fan of coroutines, but to just fix the Lerp abuse and otherwise keep your code the same, it would look like this:

    startTime = Time.time;
    startRotation = angleFinder.rotation;
    angleFinderLookAtFlightPosition = waypointScript.flightPoint - angleFinder.position;
    rotation = Quaternion.LookRotation(angleFinderLookAtFlightPosition);
   
    while(angleFinder.rotation != rotation)
    {
        float t = (Time.time - startTime) / lerpTime; // how far to lerp, from 0-1
        angleFinder.rotation = Quaternion.Lerp(startRotation, rotation, t);
        yield;
    }

There are two key differences. First, I pulled out the computation of ‘t’ — pulling it out isn’t the key bit, but I also corrected it (dividing by lerpTime instead of multiplying). That ‘t’ parameter is supposed to go from 0 to 1; 0 means give me all of the first parameter value, 1 means give me all of the second parameter value, and 0.5 means combine them 50/50. So when interpolating over time, you compute this by dividing the elapsed time by the lerp time.

You needed to multiply as sort of a hackish work-around (no offense) for the really key bit, which is that the first parameter to Lerp should be the start position, never the current position. So I stored the value of angleFinder.rotation in a new variable (startRotation), and then you just use that value over and over. The only Lerp parameter that should change on each call is the last one (t).

1 Like

@JoeStrout

Thanks again for the information, and putting me right, it’s great to learn, much appreciated.

No worries, always happy to help!

1 Like