Walking on the surface of a planetoid with Quaternions

In Rain, Sand, Stars, all objects inhabit little planetoids a la Mario Galaxy (or much before that, a la The Little Prince). Therefore, at each time step, all dynamic objects must be rotated so they stand upright on the planet surface. In a game like Mario Galaxy, their transform.up should coincide with the normal of the surface they are standing on.

Rain, Sand, Stars is simpler, so this vector always coincides with the opposite to the vector from the object towards the planetoid center (black lines in the picture). Along with this vector we also need the tangent vector to the planetoid surface (red lines) and the object’s forward vector (transform.forward in Unity3D, blue lines in the picture). Yellow lines point from each enemy towards the player, but we don’t need them now. Note that although it’s 3D, objects run in circles.

Computing the tangent is easy but tricky, as it’s direction should be always constant in its local space (say, ‘looking to the right’). Knowing that the cross product of two vectors produces a third vector which is perpendicular to the plane in which the first two lie, we obtain the planet tangent using Vector3.forward, which is (0.0, 0.0, 1.0f), and the towardsPlanetCenter vector.

Vector3 towardsPlanetCenter =
  planet.position - transform.position;
 
Vector3 planetTangent = Vector3.Cross (
  Vector3.forward, 
  towardsPlanetCenter);
Our hero being chased by an infamous cube. Black lines are vectors towards the planetoid's center. Blue lines are object's facing vectors. Red lines are the tangent vector to the planet's surface, computed from Vector3.forward and the vector towards the planetoid center. You may happily ignore yellow lines (they are vectors from each enemy towards the player)

The first rotation, the one that makes objects stand upwards when walking or running, must be applied instantly. The second one rotates objects smoothly towards their desired facing direction, which coincides with the planet’s tangent (or it’s opposite) at the object’s position.

Thanks to Unity3D’s Quaternion methods, FromToRotation and LookRotation, we may achieve this in four lines of code. First, we compute the required rotation so that an object’s transform.up coincides with the opposite of towardsPlanetCenter, and we apply it:

Quaternion standUpRotation = Quaternion.FromToRotation (
  transform.up,
  -towardsPlanetCenter.normalized);
 
transform.rotation =
  standUpRotation * transform.rotation;

Then we compute the rotation that represents the object’s desired facing direction. We use a heading variable, which is either -1.0 or 1.0 depending on user input or AI, to face towards the planet tangent or its opposite. This time, we rotate the object smoothly depending on its turning speed (Zadkiel turns much faster than Yaks):

Quaternion headingRotation = Quaternion.LookRotation(
  planetTangent * heading,
  -towardsPlanetCenter);
 
transform.rotation = Quaternion.Slerp(
  transform.rotation, 
  headingRotation,
  turnSpeed);

And that’s it! Yes, I know, that’s not all. But seriously, it’s more difficult to explain it than to implement it. And you may use something similar for moving objects in true 3D around a planetoid, as Mario does.

 

Leave a Reply

Your email address will not be published. Required fields are marked *