Quaternion.FromToRotation - is it a bug? Possible workarounds?
Hello all. I just faced a very strange behavior when calculating rotations using Quaternion class. FromToRotation gives me different results with the same input depending on where it was called. Code of the method below:
public virtual bool TryHorizontalAimTo(Vector3 Target)
{
Target.y = Ship.transform.position.y;
Vector3 targetDirection = Vector3.Normalize(Target - Ship.transform.position);
Quaternion rotation = Quaternion.FromToRotation(_actualForwardDirection.normalized, targetDirection);
if (Ship.name == "PrototypeShip_2" && name == "TurretB")
{
Debug.Log("Target dir: " + targetDirection);
Debug.Log("Forward dir: " + _actualForwardDirection);
Debug.Log("Rotation: " + rotation.eulerAngles);
}
return TryApplyRotation(rotation);
}
If called from Start() and from FixedUpdate() this method produces the following logs:
If called from LateUpdate() it gives me the expected result of rotation around Y axis:
As you can see in both cases I have (0.7, 0, 0.7) and (-0.7, 0, -0.7) vectors but rotations are different for some reason. I have Rigidbody attached to the GameObject but it is IsKinematic in both cases so this should not be a problem.
How should I handle with this? Are any workarounds exist?
Thanks in advance.
UPDATE: I tested it more and it seems no matter when the method is called. It looks like the issue appears only when object rotated at 45 degrees or 225 degrees which leads to (0.7, 0, 0.7) and (-0.7, 0, -0.7) vector parameters passed to FromToRotation. With other angles it wors fine.
LateUpdate is only called after all updates are executed, the result is different because all other transformation scripts already executed, in Update some scripts are to be executed.
It looks like no difference method was called from Start or from LateUpdate. I updated the question.
Answer by flaviusxvii · Oct 10, 2016 at 03:48 PM
A couple of things. Your "from" and "to" look to be very nearly colinear.. exact opposite directions. There's an infinite number of ways to rotate from one to the other. It's the pathological undefined case for rotating with quaternions. The only case where you wouldn't know for sure which way it's going to go..
Now.. as to the reason you're getting different results. When you print a Vector3 to the console, you only get to see 1 decimal of precision. If you were to print the components with more precision you'd see that they're probably slightly different (floats are notorious for rounding and precision errors accumulating as math is done to them). It's those little differences that are going to tip the Quaternion math to choosing one rotation over another.
You are right about getting different results - I displayed floats in the log with 4 decimal places and they are sligtly different. That is why I updated my question. FromToRotation between 45 Y degrees and 225 Y degrees gives not 180 degrees around Y as I expected but 270 around Y and 180 around Z. If it is normal 'mathematical' behaviour of quaternions than it is quite strange as to me.
not 180 degrees around Y as I expected
Your expectation is the problem here. Quaternions don't prefer rotating around any particular axis.
The other trick is that eulerAngles aren't the real rotation - it's just the best way to describe the real quat. I think (0,270,180) is a rotation around a southeast arrow, so an "over the top." A way to test is to run it slowly, using Slerp. Or maybe just print angleAxis(180,southeast).
If you do the math, (0,270,180) is correct: 270 on Y faces west, with the original arrow pointing southeast. Then a 180 spin on Z (which is now facing west) flips the southeast arrow into a northeast arrow. But, again, that's only a clumsy euler description, and not the real rotation.
I applied Quaternion to my GameObject not eulerAngles. In the image below you can see that back 'turret' rotated inside the 'hull' and also you can see 'guns' protruding from the hull (red square) which means turret also rotated ins$$anonymous$$d of seeing behind the 'ship'. It is not that I expected.
Turrets are very common. Try this:
Quaternion currentOrientation = transform.rotation;
Quaternion toTargetOrientation = Quaterion.LookRotation(targetDirection, Vector3.up);
Quaternion rotation = Quaternion.RotateTowards(currentOrientation, toTargetOrientation, 360f);