How can I get Rigidbody.MoveRotation to rotate in world space on two axes?

I’m trying to make a script for rolling cubes. I use the size of the cube and a couple of hand edited animation curves to, essentially, figure out what rotation the cube should have and how much to bob it up and down based on the cube’s world-space position. The idea is that as the cube moves, it automatically handles the rolling behaviour. The works great along one axis, but I would like it to work along both X and Z. The problem is that if the cube has rotated along the first axis, the rotation on the second axis will now happen on the wrong axis. My question is, how can I rotate my cube (preferably using Rigidbody.MoveRotation) so my rotations always happen around the world X and Z axes?

Here is my script as it currently appears (sorry for the JS):

#pragma strict

private var thisTransform : Transform; // reference to this object's transform
private var thisRigidbody : Rigidbody; // reference to this object's rigidbody
private var startPosition : Vector3 = Vector3.zero; // used to store the start position of the object

public var size : float = 8.0f; // size of the tumbler cube on one side
private var cornerHeightOffset : float = 0.0f; // distance difference between height of center when flat vs 45degs

public var heightCurve : AnimationCurve; // this curve defines how the cube moves up and down during the rotation
public var rotationCurve : AnimationCurve; // this curve defines the rotation of the cube over the course of the movement

function Awake () 
{
	thisTransform = gameObject.transform;
	thisRigidbody = gameObject.GetComponent.<Rigidbody>();
	startPosition = thisTransform.position; //initialize the start position
	cornerHeightOffset = Mathf.Sqrt( ( 2 * ( Mathf.Pow( (size/2), 2) ) ) ); //calculate the distance between center and corner
	cornerHeightOffset = cornerHeightOffset - (size/2.0f); //calculate difference between height from corner and height from side
}

function FixedUpdate () 
{
	var offset : Vector3 = thisTransform.position - startPosition; //calculate the offset from start position


	var heightOffset : float = 0.0f; // this is the offset which will be applied to the height of the cube
	var tempHeightEval : Vector2 = Vector2.zero; // this is the calculated offset by X and Z movement

	//evaluate height in X and Z
	tempHeightEval.x = heightCurve.Evaluate(offset.x / size);
	tempHeightEval.y = heightCurve.Evaluate(offset.z / size);
	Debug.Log("heightEvaluation = " + tempHeightEval , gameObject);

	tempHeightEval *= cornerHeightOffset; // multiply the evaluated result by the cornerHeight
	heightOffset = Mathf.Max( tempHeightEval.x , tempHeightEval.y ); //set heightOffset to whichever is greatest

	var rotation : Quaternion = Quaternion.identity; // this will hold the final calculated rotation to apply
	var tempRotationEval : Vector2 = Vector2.zero; // this is the rotation on each axis.

	//evaluate the rotation in X and Z (*4 to account for 360degs being 4 1/4 rotations)
	tempRotationEval.x = rotationCurve.Evaluate(offset.z / (size*4.0f)); // rotate along one axis as the other axis moves
	tempRotationEval.y = rotationCurve.Evaluate(-offset.x / (size*4.0f)); 

	tempRotationEval *= 360.0f; // mutiply the result by 360 to get a full rotation between 0 and 1
	rotation = Quaternion.Euler(tempRotationEval.x , 0.0f , tempRotationEval.y);
    

    // apply the movement and rotation
	thisRigidbody.MovePosition(Vector3 (thisTransform.position.x , startPosition.y + heightOffset , thisTransform.position.z));
	thisRigidbody.MoveRotation(rotation);
}

When you use euler angles it apples them in the order: z, x, y. When it applies the z rotation then it also rotates the local x axis. What you’ve gotta do is rotate the axis of rotation as well. Try something like this:

var xRot = Quaternion.AngleAxis(tempRotationEval.x, transform.right);
var zRot = Quaternion.AngleAxis(tempRotationEval.y, xRot * transform.forward);
this.Rigidbody.MoveRotation(xRot * zRot);

I think that might do the trick. You may need to change transform.right to transform.left, or transform.forward to transform.back but that’s the general gist of it.

When you use euler rotations unity applies the rotations in the order z, x, y. So rotating by z is also rotating the local x axis. To get the behaviour you want you’ll need something like this:

var xRot = Quaternion.AngleAxis(tempRotationEval.x, Vector3.right);
var zRot = Quaternion.AngleAxis(tempRotationEval.y, xRot * Vector3.forward);
this.RigidBody.MoveRotation(xRot * zRot);

I think that should do the trick. You may need to swap Vector3.right to Vector3.left, or Vector3.forward to Vector3.backward but that is the gist of it.