I have a child object with local rotation slightly shifted (9, 150, 0). I need to get a quaternion that is going to be that rotation, but turned 40 degrees around the objects local up axis (as if dragging the green y rotation gizmo in the editor).
So far I’ve tried everything I could google, mostly “multiplying” the local rotation by a quaternion, nothing seems to work properly. Quaternion.Euler, Quaternion.AngleAxis, changing object and quaternion places in the multiplication. Using Transform.TransformDirection / TransformVector or Transform.InverseTransformDirection / InverseTransformVector, and much more.
In many cases above the object rotates around the global Y axis, but I need to make it local. Sometimes I get a nonsense rotation completely. This must be a trivial thing, but could someone explain how to do it?
P.S. I need to get and store a quaternion, so no transform.Rotate and so on.
The trick is that rotations aren’t local or global – that’s determined by how you use them. Suppose you have one rotation qBase (in your case, whatever (9,150,0) makes). Then you have another one rotating around y, called ySpin. ySpin is NOT local or global, yet. ySpinqBase applies ySpin to qBase as a global; qBaseySpin applies it as local.
A nice way of testing it to have a slowly increasing rotation. Then you can watch it spin and determine whether it’s going around the correct axis. Try code like this:
Quaternion baseSpin; // set once at start
float yDegrees=0;
public float speed=4; // allow us to adjust as we run
void Start() {
baseSpin=transform.rotation;
}
void Update() {
Quaternion ySpin=Quaternion.Euler(0, yDegrees,0);
yDegrees+=speed;
transform.rotation=baseSpin*ySpin;
// or, to test global: =yspin*baseSpin;
}
This worked, thank you for a great explanation. This is very counter intuitive though. I always thought that, say, multiplying rotation by 40*Vector3.up would only rotate the Y euler angle by 40, and so on. Hence I was trying to multiply by the local up vector which for instance would be something like (0.1, 1, 0.1).
The star operator is overloaded for quaternions. It is NOT multiply. It’s the “add these rotations together” symbol. baseSpinySpin is actually calling a function something like addRotationsSecondLocalOnFirst(baseSpin, ySpin). We use star for it since real mathematicians do. They like to redefine + and * and so on. It’s like how matrix multiplication is written M1M2 but is really this god-awful sequence of steps.
40Vector3.up is just regular scalar math. It’s 40(0,1,0) which gives (0,40,0). It’s a useful shorthand and it’s obvious they use * for it since it’s a shortcut for three multiplies.
The real question is, why did mathematicians decide adding 2 rotations should use the * symbol? But if I you knew enough math, it would probably make sense.
Yep, I know what you mean, and whenever I said “multiply” I was just referring to the * operator, but not the mathematical action of multiplication. And exactly as you said Vector3.up is just (0,1,0), hence my logic was that rotA * (0, 40, 0) should return (rotA.x, rotA.y + 40. rotA.z). The fact that order matters and determines local/global space was very unintuitive.