- Home /
 
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);
 }
 
 
              Answer by AdamAlexander · Apr 28, 2017 at 05:00 AM
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.
LOL! Thanks for having a look. Your solution, as written seems to produce even stranger results! As soon as the cube isn't starting from a rotation of (0,0,0) all movements cause the cube to rotate on the wrong axis.
Seeing what you've proposed as a solution has, however, made it abundantly clear that I am way over my head when it comes to vectors and Quaternions!
I tried a couple of variations based on what you've proposed, but I can't get anything better than my initial results. i.e. one axis works fine but the other depends on the angle of the cube.
I don't know if this might help... I've gone ahead and assumed the presence of a parent object (needed to allow the cube to bob up and down relative to something else). The parent object is what actually moves around, while the cube rolls "inside" the parent.
Now, would it be possible to rotate each axis relative to the parent (which doesn't roll)?
Your answer
 
             