F'ing Quaternions -- How do they work?

I have a specific question, but I’m also sick of Quaternions being a pain in my ass every time I want to do anything in Unity, so I’ll ask up front – anyone know of any good lectures for dumb idiots like me that will explain Quaternions simply, yet extensively enough that from now on I’ll “get it”, rather than having to constantly Google for code snippers and half-answers that I don’t understand? I’d really like to get over this frustrating learning curve.

As for my specific question (which is for a 2D project), I have a script that (thanks to a lot of Googling and luck) will rotate the player to face in the direction of an AreaEffector and also rotate a player’s feet toward the axis of an object with a Point Effector. I specifically want to expand this second part so that, if the force is positive (so the player is pushed away from the Point Effector rather than drawn into it), the player’s feet will instead rotate outward to face away from the Point Effector game object’s central axis. Basically, I want to take this method and get it to return a final Quaternion that is rotated an extra 180 degrees around the Z axis (compared to what the result is now, effectively rotating the player so the feet will be where the head is and vice versa).

How do I do it? Please help, I hate Quaternions!

1 Like

How about this?

https://www.youtube.com/watch?v=3BR8tK-LuB0

As for your specific question, I’m afraid I didn’t follow most of it, except this part:

And that’s easy enough. You’ve got your effectorRotation; if you want to append an extra 180° rotation around Z, then this would be simply

effectorRotation * Quaternion.Euler(0, 0, 180)

Or you can multiply in the other order, depending on whether you mean to do this extra rotation before or after what you’re doing now (which is effectively the difference between rotating in the global Z axis vs. the local Z axis).

For what it’s worth, I love quaternions… they represent a rotation in a very concise, useful way, and can be composed just by multiplication. What more could you want from a method of representing rotations?

Best,

  • Joe

P.S. Please use the “Insert” button to put your code right in your post, rather than using Hastebin… it’s much easier to read and reply to that way.

1 Like

Thanks for the reply! I’ll check out the video (actually saw a link to it but figured Numberphile wouldn’t necessarily be the best introduction).

I’ve actually found an answer similar to this by Googling, giving me this result:

    private void GetRotationFromEffector() {
        var gravityUp = (objectToRotate.transform.position - currentEffector.transform.position).normalized;
        var localUp = objectToRotate.transform.up;

        effectorRotation = Quaternion.FromToRotation(localUp, gravityUp) * objectToRotate.transform.rotation;
        effectorRotation *= Quaternion.Euler(0, 0, 180);
    }

… But unfortunately it doesn’t work. It DOES rotate the player so head faces inward and feet face outward, but then after a second or so, the player starts rotating on the X and Y axes, which means that the 2D player folds paper-thin. And this is why I hate Quaternions – they don’t do what you’d expect them to do. Why does making a rotation on the 180 z also cause unwanted rotation in the x and y, and how do I stop it?

The player ends up with a rotation of 11, 270, and 180, when it SHOULD be 0, 0, 191 (it just so happens the player’s angle should be 11 degrees in this case).

Documentation that explains clearly how to use them. :frowning: I’ve never once had a Quaternion work as expected. It’s always offered some headache-inducing strange behavior that I have to search for an answer for.

This is what I’m talking about. Literally everywhere else in math, x * y and y * x mean the same thing, except apparently with Quaternions x * y != y * x. That’s confusing.

1 Like

So the more you work with Quaternions the more you realize that the implementation you see in Unity is some smart dude being nice and giving us the easy front end so we don’t have to deal with the madness of actually computing Quaternions. At least, that has been my experience. From what I can tell, what we’re seeing is the easy side of things. I had to screw with them a lot when making IK point in the right direction which turned out to be a bug, but made me try just about everything I could with them to make it work.

I’ve burned most of the memory of them away with alcohol but I remember a few things, for instance x * y is adding two Quaternions together - not multiplication. You can actually learn a huge amount by carefully reading the Quaternion documentation itself which is what I did while also googling how they generally are supposed to work from places like stackexchange.

What I ended up using most often was Quaternion.LookRotation which simply asks for forward and up, then returns a rotation for you. This is pretty flexible once you get used to it.

All I can really say is read the documentation and test the example codes. They work, and they show you what things do so you can find what you need.

1 Like

Thanks for the link. After a bit of looking, I’ve found that this:

effectorRotation * Quaternion.Euler(0, 0, 180);

is the same as this:

effectorRotation * Quaternion.AngleAxis(180, Vector3.forward);

which is the same as this:

Quaternion.Euler(effectorRotation.eulerAngles.x, effectorRotation.eulerAngles.y, effectorRotation.eulerAngles.z + 180);

But unfortunately they’re all still wrong. Again, instead of my final rotation being 0, 0, 191, it ends up being 11, 270, 180. What’s with that?

Keep in mind by “final rotation”, I’m referring to the fact that I’m using this to angle the player in FixedUpdate():

objectToRotate.transform.rotation = Quaternion.Slerp(objectToRotate.transform.rotation, effectorRotation, rotateSpeed * Time.deltaTime);
1 Like

This website has a lot of information regarding quaternions. You need to understand that there are multiple ways to represent the same rotation with a quaternion. However, the result of converting a quaternion back to euler angles should give you the correct results… What is the value of “effectorRotation”?

effectorRotation is a private class member that holds the rotation I’ll be using to Slerp in FixedUpdate. I get it like so:

var gravityUp = (objectToRotate.transform.position - currentEffector.transform.position).normalized;
var localUp = objectToRotate.transform.up;

effectorRotation = Quaternion.FromToRotation(localUp, gravityUp) * objectToRotate.transform.rotation;

Keep in mind I don’t understand why this code works. It’s just something I snatched from StackExchange.

1 Like

So you’re basically saying your current code is putting the rotation like it should, but you’re just having trouble getting it to rotate 180 degrees. Is that right? Break it out and make sure it gives the result you expect, work backwards by taking it apart to confirm it is actually working properly in smaller chunks.

So if it is working up to that point, you just do * AngleAxis and give it the local up?

I don’t understand what you mean. “give it” the local up? As in multiply? Or use some other function? What are you saying?

I literally have no idea what any of this code means, so I don’t know what I can or can’t just. :slight_smile:

AngleAxis will do a rotation based on 1) how much you want to rotate 2) an up direction. So you just give it the local up direction since you probably dont want it to be in world space (as in Vector3.up).

Can you give us the debug value of effectorRotation before you add the 180 degrees?

Debug.Log(effectorRotation.eulerAngles);

So you mean something like this?

effectorRotation = effectorRotation * Quaternion.AngleAxis(180, localUp);

Because that’s apparently nowhere close. The character just wiggles all over the place and I have no clue what direction it’s going for (it has extensive spin in X, Y, and Z, which is very, very not what I want).

The result is:

I want it to be:

You’re getting (11, 270, 180) with the following code?

Quaternion effectorRotation = Quaternion.Euler(0, 0, 11);
effectorRotation *= Quaternion.Euler(0, 0, 180);
Debug.Log(effectorRotation.eulerAngles);

Well, the initial effectorRotation value isn’t explicitly set like that – it comes form the angle between the player and the PointEffector’s game object. This is how it currently works:

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

I want the player instead rotate so the feet are facing outward, rather than the head (191 degrees instead of 11 degrees on the z). Your code works fine, but like I said, my method works like this:

var gravityUp = (objectToRotate.transform.position - currentEffector.transform.position).normalized;
var localUp = objectToRotate.transform.up;

effectorRotation = Quaternion.FromToRotation(localUp, gravityUp) * objectToRotate.transform.rotation;

If I do something like this instead:

var gravityUp = (objectToRotate.transform.position - currentEffector.transform.position).normalized;
var localUp = objectToRotate.transform.up;

effectorRotation = Quaternion.FromToRotation(localUp, gravityUp) * objectToRotate.transform.rotation;
effectorRotation *= Quaternion.Euler(0, 0, 180);

I get this:

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

Which you can see almost works, but the player ends up flipping around in ways I don’t want them to (you see this happen at about the 3-second mark).

The full code for this script (without the Euler(0, 0, 180) part) is here.

If I had to guess these are likely the problem. If either of those end up having values on more than two dimensions (and those still have to be the two dimensions you want), you’re going to get rotation off of your intended axis.

The closest I’ve come to understanding quaternions, as far as the values are concerned, is to visualize an arrow pointing to the surface of a sphere. That takes three dimensions because it’s actually mapping to a point in space. The forth variable is then a handle on top of the arrow controlling it’s roll, although the alternate way to look at it (more mathematically) is as a variable that inversely determines the size of the sphere since the magnitude of a quaternion is always 1 (for a rotation). For only one axis of rotation, quaternions are good about keeping the variables used down to two (essentially representing a unit circle), but the instance another axis comes into play, all of a quaternion’s variables come into play.

At least that’s the best way I can think about it without my brain exploding from trying to grasp 4D spatial constructs that needs three imaginary numbers to work.

Check out this thread. The _Mysteries_ of Quaternions - Industries - News & General Discussion - Unity Discussions

And in particular, check out this video. Best explanation of quaternions on the net. Hands down.

2 Likes

@Cybearg , I think the first step is to admit that the problem isn’t Quaternions — it’s rotations. If you don’t understand why rotating by A and then B is different from rotating by B and then A (which in code, is AB vs. BA), then you don’t understand rotations, plain and simple.

At this point you should grab a coffee cup or other odd-shaped object off your desk and start playing with it. Do a X-axis rotation followed by a Z-axis rotation, and see what you end up with. Then do it in the other order. Different result, right?

And as you do these manual tests, notice that you have to think carefully about what loose terms like “X-axis” means. Do you mean relative to your desk (the world), or relative to the coffee cup (local)? In fact you should always define your rotations relative to the desk. But note that initially these are the same (define a “starting” position for your coffee cup, neatly aligned with the desk). So, whatever rotation you do first is effectively a local rotation as well.

None of this has anything to do with Quaternions; it is the very nature of rotation itself. Take the time to really grok all this fully.

Then, when you say things like

…it indicates that something else entirely is going on. Quaternions have nothing to do with time. You have some other script that is running, or the same script that is running multiple times, with some cumulative effect you haven’t fully thought through. Your original question was about a single rotation (and you can always cast a problem that way, by keeping track of what rotation(s) you need and starting from no-rotation every frame).

Finally, if your player is 2D, then you really don’t need to be (and almost certainly should not be) worrying about 3D rotations at all. Define what plane you’re working in, define your rotation angle as a single (float) number of degrees, and rotate by that. For example, if (as in a standard sprite setup) you want to only rotate in Z, then forget about quaternions and just set transform.eulerAngles to (0, 0, angle).

3 Likes

Thanks so much for the help, everyone! All the different resources and explanations have helped a ton. I feel like I’ve gone through a crash course, but I feel much more comfortable now!

I finally follow what you mean, now. :slight_smile: Now I’ve changed my code to not depend on fiddling with Quaternions and instead simply use explicit Z Eulers, while at the same time I feel I understand Quaternions better, too. The desired functionality no works without any strange turning on axes I didn’t want manipulated.

However, I’m still a little unclear on how to manipulate Quaternions. Aside from using some convenient Quaternion functions, setting Eulers, and combining Quaternions (or slerping), are there any other common things done with Quaternions? Because while I feel like I follow what they are and have a jist of how they work and how to use them in the Euler/combining sense, I don’t see how to interact with them beyond that. Is there still more to it, or do most people just add Eulers to Quaternions, multiply them against each other, Slerp, and use a handful of the built-in functions?

As a general rule you don’t manipulate quarternions directly.

1 Like