- Home /
Rotate Object to Euler Angle using Physics?
I'm certain I'm probably doing this wrong, so I'm asking here to find out the right way.
I am trying to rotate a rectangular object's localRotation to a euler angle on the Z axis. I am currently using a kinematic rigidbody and the following code to 1) Calculate the new Z rotation by adding some euler angles to the local rotation, and 2) Using a Quaternion.Lerp to have the rotation occur over a specific interval.
Here is the code I'm using:
1) Calculate the new Euler Angle by taking the local rotation and adding a Z eulerAngle:
Vector3 Destination;
Quaternion StartRotation = this.transform.localRotation;
float myRotation = 90.0f;
Destination = new Vector3(this.transform.localRotation.eulerAngles.x, this.transform.localRotation.eulerAngles.y, this.transform.localRotation.eulerAngles.z + myRotation);
2) Start the coroutine which takes care of the rotation:
yield return StartCoroutine(RotateLerp(StartRotation.eulerAngles, Destination));
3) And here's the co-routine that rotates over time:
IEnumerator RotateLerp(Vector3 StartPosEuler, Vector3 EndPosEuler) { float i = 0.0f; float rate = 1.0f/(RotateSpeed);
while (i < 1.0) {
i += Time.deltaTime * rate;
this.transform.localRotation = Quaternion.Lerp(Quaternion.Euler(StartPosEuler), Quaternion.Euler(EndPosEuler), i);
yield return true;
}
}
All of this works great. The object rotates fine over time. My problem is that I have a another non-kinematic rigidbody sitting on top of the object, and I was hoping that when I increase the speed of the rotation, this "target" rigidbody would get "tossed" into the air or out of the way when the rotating object collides with it. However, it seems to just slightly "push" it enough to get by. It doesn't really toss it at all, which I suppose is to be expected since there is no real force involved here. I realize this is normal behavior, but I'm new at this and not really sure of a better way.
I've dug around a bit on here but I can't find a particularly clear answer. I suppose what I need to know is how to apply a force to the rigidbody such that the rigidbody would turn in the Z-axis to a particular euler angle over time. Any advice on this issue would be greatly appreciated.
Thank you so much!!
Answer by Senhor de todo o Mal · May 10, 2011 at 11:00 AM
Instead of transform.localRotation have you tried rigidbody.MoveRotation or rigidbody.AddTorque?
Answer by Cameron 3 · May 11, 2011 at 04:59 PM
Yes, rigidbody.MoveRotation seemed to be the way to go. I had to re-write stuff to perform the rotation in FixedUpdate, but that wasn't a big deal.
I do have one more question related to this. For some reason, using a Euler angle of 180.0f makes the object rotate clockwise properly a few times, but then it starts bouncing back and forth beween 180 degrees and 6.465943e-05 (basically zero, even tho I'm telling Quaternion.Euler to use 0.0f). So it starts moving clockwise, then bounces between clockwise and counter-clockwise. I'm using an orthographic camera on an X/Y plane, so +Z represents rotation right, and -Z represents rotation left.
I changed my RotateLerp function to the following:
IEnumerator RotateLerp(Vector3 StartPosEuler, Vector3 EndPosEuler) { endRotation = Quaternion.identity; endRotation.eulerAngles = EndPosEuler;
Debug.Log("Starting Rotation: " + StartPosEuler + " - End: " + EndPosEuler.z + " EndRotation: " + endRotation.eulerAngles);
while (Quaternion.Angle(this.rigidbody.rotation, endRotation) > 0.1f)
{
yield return true;
}
}
So basically a CoRoutine starts which takes the object's current rotation euler angles, and another vector3 which is just that euler angles vector with +180.0f on the Z-angle. Then it builds the endRotation quaternion which is a global of type Quaternion that FixedUpdate works with. I tried building it two ways, using the method above, where you initialize to identity and just set the euler angles, and I also tried using Quaternion.Eulers(EndPosEuler). The CoRoutine waits in a while loop until the rotation is "effectively" done, and then sets the state of the rotation object to MoveState.Waiting so that FixedUpdate stops rotating it.
Then in my Fixedupdate, I check the state of the object and if it is supposed to be moving, it uses this.rigidbody.MoveRotation to move the object:
if (currentState == MoveState.Moving) { float Angle = Quaternion.Angle(this.rigidbody.rotation, endRotation); Debug.Log("Angle: " + Angle);
Quaternion moveRotation = Quaternion.RotateTowards(this.rigidbody.rotation, endRotation, Time.deltaTime * (RotateSpeed * RotationPoints[CurrentRotationPoint].SpeedMultiplier));
this.rigidbody.MoveRotation(moveRotation);
}
So what happens is, the object rotates 180 degrees clockwise, then another 180 degrees clockwise, then another, so it's going in a circle... But eventually it starts going 180 degrees clockwise, then 180 degrees COUNTER-clockwise, and bounces back and forth between those two points forever.
My thoughts are it could be one of these things:
1) The while loop with my Quaternion.Angle check, but I don't know a good way to test that it's reached a specific euler angle since it doesn't appear to be an exact science. I can't test for "0.0f", for example, because Unity is setting the Z euler to 6.465943e-05, etc. An equality comparison didn't work the first time I tried it.
2) Quaternion.RotateTowards is doing some kind of rounding/optimization with the two rotations I am giving it, and not producing exact euler angles even tho both rotations have exact euler angles on them... This seems to be the case since it's ending up with 6.465943e-05 instead of zero.
Another interesting effect is that if I use 90 degree turns, or 45 degree turns, the object continues to rotate clockwise just fine. This seems to only be a problem when I do 180.0f.
Also, if I do 180.0001f, it works fine and turns clockwise.
Any light that can be shed on this will be very much appreciated :) And it will make an awesome Unity answer too! :) :)
Thanks!
Answer by Cameron 3 · May 12, 2011 at 06:46 AM
I figured it out, I just used Mathf.Floor to find out if the rotation has reached the right spot, and when setting the rotation...
to set up my rotation:
if (currentState == MoveState.Rotating) { float amountToRotate = 180.0f; float startRotation; float endRotation;
startRotation = Mathf.Floor(this.rigidbody.rotation.eulerAngles.z);
endRotation = Mathf.Floor(startRotation + amountToRotate);
}
Then:
void FixedUpdate() { Quaternion moveRotation = Quaternion.RotateTowards(Quaternion.Euler(0, 0, Mathf.Floor(this.rigidbody.rotation.eulerAngles.z)), Quaternion.Euler(0, 0, endRotation), Time.deltaTime * RotateSpeed);
this.rigidbody.MoveRotation(moveRotation);
if (Mathf.Floor(this.rigidbody.rotation.eulerAngles.z) == Mathf.Floor(moveRotation.eulerAngles.z))
{
this.rigidbody.rotation = Quaternion.Euler(0, 0, endRotation);
currentState = MoveState.DoneRotating;
}
}