transform.Rotate() but for Quaternions

Is there a way to rotate like transform.Rotate() but for Quaternions instead of Transforms?

What I mean is I can do something like this and it moves how I want:

transform.Rotate(new Vector3(axis2,axis1,0),Space.World);

Where axis1 and axis2 are directly from input from arrow keys, and I want L & R keys to rotate the object and Up & Down keys to. That method works great for transform, but not for example if I want to rotate a kinematic rigidbody. Then I want to use .MoveRotation() which takes a Quaternion as a parameter.

But if I rotate a Quaternion like this:

rotrot *= Quaternion.Euler(axis1, axis2, 0);

or like this:

rotrot *= Quaternion.AngleAxis(axis1, Vector3.up) * Quaternion.AngleAxis(axis2, Vector3.right);

Then I have something going on where one axis will rotate relative to the other axis rather than both remaining relative to the world, like it does with the transform.Rotate() example.

This seems like should be a solved problem but my searching turns up a lot of threads with answers like use euler angles or just use transform.Rotate() neither of which fixes the problem.

Now I could do the kludgey thing and have an invisible GameObject that I apply this transform to and grab its transform.rotation as my Quaternion value, but there has to be a way to do this directly to the Quaternion without the middleman.

1 Like

Why not just store your rotation axis separately and then recompose into the quaternion each time?

axis1 += Input.GetAxisRaw("Horizontal") * rotationSpeed * Time.deltaTime;
axis2 += Input.GetAxisRaw("Vertical") * rotationSpeed * Time.deltaTime;

rotrot = Quaternion.Euler(axis1, axis2, 0);

Because that does the same thing I’m trying to avoid. The one axis ends up rotated by the other axis.

In case describing this with words is insufficient, I’ve made two animated gifs of rotating hats. One uses transform.Rotate() the other uses Euler angles.

1877218--120688--hatspinningByEulers.gif
This is by Eulers. Notice the axis each hat rotates around is always parallel to the brim of the hat but not always parallel to each other.

1877218--120689--hatspinningByTransforms.gif
This is by transform. Notice the axis each hat rotates around is parallel to the axis used by all the other hats. Because it’s always the same no matter what the rotation of the hat is. That’s what I want.

2 Likes

maybe use transform.up or transform.right instead of Vector3.up or Vector3.right?

Ok. So you’re doing this and it does what you want:

transform.Rotate(new Vector3(axis2,axis1,0),Space.World);

You’re rotating the object’s transform around the z axis by axis2 degrees and around the x axis by axis1 degrees in world space according to the Unity documentation. I’m understanding that you want to do the same thing with a quaternion.

In an effort to simplify everything, it seems to me like they’ve made it more complicated. :roll_eyes:

The first question is whether a quaternion is even needed? Or more to the point, how do you know the code above doesn’t use a quaternion? They’ve obfuscated the rotation away from you, so you really don’t know how the rotation is actually being performed. It may take those pitch, yaw, roll values and build a quaternion and the transform may be stored as a quaternion under the hood. Without source code, there’s no way to know.

Lol. Actually - after writing that above, I just found that in the documentation “Unity stores rotations as Quaternions internally. To rotate an object, use Transform.Rotate. Use Transform.eulerAngles for setting the rotation as euler angles.”

It looks to me like you can set a Transformation’s rotation amount directly.

You should be able to do this:

//These two are NOT the same even though they look like they are if you are not familiar with quaternion math.
transform.rotation = transform.rotation * Quaternion.Euler(axis2, axis1, 0);
//or
transform.rotation = Quaternion.Euler(axis2, axis1, 0) * transform.rotation;

I believe one should revolve around the world axis and the other around the local axis. I would have to actually test it to make sure it works but I’ve found that the order of operations matters significantly. When you use the *= operator its going to determine which gets applied first. And its my experience that when you take a world matrix and multiply it by a rotation matrix the order in which you perform that rotation will determine whether its done in local space or world space. Transformations are world matrices…usually. In this case, its apparently a quaternion under the hood. But the same applies to quaternions also.

But it looks to me like it doesn’t really change much to use quaternions here since its apparently using quaternions in both cases under the hood. It kind of looks to me like both options do the exact same thing by converting pitch, yaw, and roll into quaternions and then performing a quaternion rotation and then applying the results as a world matrix at draw time (Graphics cards don’t understand what a quaternion is. They only accept matrices and so in the end it will end up being a matrix anyway.)

Where you might see a slight performance advantage would be if you’re storing a value as a quaternion, doing mathematical operations on the quaternion itself, and then applying it as a rotation. Here, I’m not sure there’s any difference at all between using quaternions and using transform.Rotate().

Anyway, try writing the multiplication operation out instead of using the *= operator and try switching which gets multiplied to which. Multiplication combines quaternions, but its not commutative (I had to go look that word up since I’m not a math teacher) like multiplication with numbers; the order in which they are multiplied greatly matters. And you have to write it out in long form if you don’t like the order that *= does it in.

Anyway, see if that helps.

Oh. Also, when you do a rotation away from the origin, you generally have to move the object to the origin, rotate it, and move it back. That may come in to play in this problem or it may not. In the first example, it looked like you were asking Unity to perform the rotation for you and so it may be doing that under the hood without you being aware of it. In the example I gave, it should be a direct rotation which may cause the object to orbit the origin rather than rotating around itself. If it’s a child of another object, that object’s position will be its origin. But whatever its origin is, it will always rotate around its origin. If the object is moved away from its origin, you must move it to the origin, rotate, then move it back where it was unless you want it to orbit rather than rotate.

5 Likes

Very helpful BBeck Thank you man even 2 years later :wink: