Reputation: 3391
Quaternion q1 = Quaternion.LookRotation(Vector3.Cross(transform.right, normal), normal);
Quaternion q2 = Quaternion.LookRotation(target.position - transform.position, normal);
I'm trying to rotate and object in a 3D scene using two Quaternions together so that the object remains perpendicular to the terrain below it (q1) and the front of the object faces a target transform (q2);
In FixedUpdate
using Rigidbody.MoveRotation(q1)
or Rigidbody.MoveRotation(q2)
causes an object to rotate to the required rotation and stay pointing in that direction. If I use Rigidbody.MoveRotation(q1 * q2)
it spins round and round on its up axis.
I've tried everything I can think of to generate q1 and q2 and regardless of how I come up with those angles its multiplying them together that causes the spinning problem.
Do we need to cache some kind of rotation delta or something to track how far its rotated since last frame & adjust?
Cross posted on GameDev.
Upvotes: 1
Views: 493
Reputation: 20269
The quaternions produced by LookRotation
are each such that rotate from the identity rotation to the rotation that the parameters specify. Applying the second rotation from a non-identity rotation (which is what q1 * q2
does) may make it fail to meet one or both of the LookRotation
parameters that q2
was created with.
Use Vector3.OrthoNormalize
to find the vector orthogonal to normal
that is closest to target.position - transform.position
. You'll have to handle the situation where normal and that direction are parallel.
Then, use Quaternion.LookRotation
to get the rotation that will point a transform's forward
and up
in those directions:
Vector lookDirection = target.position - transform.position;
if (Mathf.Approximately(1f, Vector3.Dot(normal, lookDirection)))
{
// normal and lookDirection are parallel
// Do something reasonable about look direction here.
// That may mean doing nothing at all!
return;
}
Vector3.OrthoNormalize(ref normal, ref lookDirection);
Quaternion newRotation = Quaternion.LookRotation(lookDirection, normal);
Rigidbody.MoveRotation(newRotation);
An alternative is to use LookRotation
to look in the normal
direction and have up as close to pointing away from target.position as possible first, then pitch downwards 90 degrees:
Vector lookDirection = target.position - transform.position;
if (Mathf.Approximately(1f, Vector3.Dot(normal, lookDirection)))
{
// normal and lookDirection are parallel
// Do something reasonable about look direction here.
// That may mean doing nothing at all!
return;
}
Quaternion bottomTowardsTarget = Quaternion.LookRotation(normal, -lookDirection);
Quaternion newRotation = bottomTowardsTarget * Quaternion.Euler(-90f,0,0);
Rigidbody.MoveRotation(newRotation);
Upvotes: 1