- 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)?