Absinthe
Absinthe

Reputation: 3391

Rigidbody.MoveRotation: multiplying quaternions causes constant rotation

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

Answers (1)

Ruzihm
Ruzihm

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

Related Questions