I'm trying to rotate an object on a specific axis to a specific angle (in degrees.) I'm trying to use the built in functions as much as possible, but I can't quite make out how to use this: Vector3 RotateTowards (Vector3 from, Vector3 to, float maxRadians, float maxMagnitude) Right now, I'm just rotating in one direction always, but I'd like to rotate in the direction that is the shortest rotation amount to my target rotation. Something like this: Code (csharp): if (Mathf.Round(transform.eulerAngles.x) != Mathf.Round(targetRot)) { if (transform.eulerAngles.x - targetRot >= 180) transform.Rotate(Vector3.up, Time.deltaTime * rotSpeed); else transform.Rotate(Vector3.up, -Time.deltaTime * rotSpeed); } Only, I realize that the subtraction test against 180 is incorrect. So, yes I can work out all the angle calculations (if angle and target angle are > 0 there is one set of tests and all those combinations) but I'm wondering if there is something I can use instead. I've been all over the place with this using target Quaternions and Lerp but that begins switching the use of axis, etc. Sum up: What does the Vector3.RotateTowards actually do? Is there an absolute way to rotate towards an angle and stop at that exact angle on one axis? (handily) Any help would be much appreciated.
I've been trying out the longer method and got thinking about some things. It's odd to me that LerpAngle never reaches the target, but bounces around it. Shouldn't it set the angle to the target if it goes past it? I don't want that kind of movement anyway, but I was surprised. Also, I've seen examples of people doing this and even the documentation suggests this is okay: transform.eulerAngles.x = targetAngle; When I do that (with C#) I get this error: "Cannot modify expression because it is not a variable" Since the examples I've seen on the forum are in Javascript I'm guessing this is some difference in the languages I'm missing. It seems like such a simple idea: "rotate towards a desired angle until you reach it, then stop at it."
The error you are getting is some weird "feature" of C#. The solution is something like: Code (csharp): Vector3 angles = transform.eulerAngles; angles.x = targetAngle; transform.eulerAngles = angles; As far as the rotation stuff... have you considered making a goal rotation expressed as a Quaternion using the EulerAngles->Quaternion constructor, and then Slerping the current transform.rotation to the goal rotation over time? HTH, -Jon
Thanks for the tip on the C# difference there. I had a similar workaround but yours is much better. The Quaternion Slerp was reaching the target angle if I remember right, BUT it would rotate in any old direction it wanted and I wanted to restrict it to rotating on a single axis. I'm getting much, much closer now. I've just been trying to use as many of the Unity classes as possible to learn them and I've just been frustrated. I'll post the code that works for me if anyone is interested. Anybody know how Vector3.RotateTowards works? I'm still curious.
So, here is the latest. Being subject to the automatic conversion to Quaternions causes a serious problem in this code (which is modified from working code from my game Space Barrage.) When using the transform.Rotate method it will, upon reaching a certain angle, invert the other two axis that haven't been rotated... so, in my case, the x is being rotated negatively from 360 towards 180 and about halfway through the Y and Z axis invert (270 to 90 and 90 to 270) and the X axis compensates so that the object looks like it is the right position/rotation but now the X axis is inverted... so, by the end of the rotation, I'm testing for the wrong condition. It's a little infuriating. I guess I'm going to try and check for axis inverting now(?!) There HAS to be a better way... or at least my limited thinking tells me that. Here's my current code: Code (csharp): bool XRotateTransformTowards(Transform t, float targetAngle, float rotationAmt) { Vector3 axis = Vector3.down; float currentAngle = t.eulerAngles.x; Vector3 tempAngles = t.eulerAngles; bool reached = false; if (rotationAmt >= 180f) { //special case - method won't work if we try to rotate > 180(?) reached = true; } else if (currentAngle == targetAngle) { //same angle already? We are done here. reached = true; } else if (currentAngle > targetAngle) { //one set of rules for this case if (currentAngle - targetAngle > 180f) { t.Rotate(axis, rotationAmt); //rotate towards it if (targetAngle + 360f - currentAngle <= rotationAmt ) { //if angle < rotationAmt reached = true; } } else //same idea but going in the opposite rotation { t.Rotate(axis, -rotationAmt); //rotate towards it if (currentAngle - targetAngle < rotationAmt) { reached = true; } } } else if (currentAngle < targetAngle) {//opposite rules apply in this case if (targetAngle - currentAngle < 180f) { t.Rotate(axis, rotationAmt); //rotate towards it if (targetAngle - currentAngle < rotationAmt ) { //if angle < rotationAmt reached = true; } } else { t.Rotate(axis, -rotationAmt); //rotate towards it if (currentAngle + 360f - targetAngle < rotationAmt) { reached = true; } } } //if we've gotten close enough, let's make it as exact as possible if (reached == true) { tempAngles.x = targetAngle; t.eulerAngles = tempAngles; } return reached; }
So... I changed tactics completely. Instead of the above method, I have my object instantiate an invisible little object with a trigger collider at the angle that I want to reach and after setting a torque on my object I keep raycasting until I hit the little trigger collide object. Once it hits it means I can stop rotating. It's still not perfect but it works. I ran into some trouble with layerMasks, but got around using them, so that will be a question for a later day.
I use the three-line example provided by nicholas in this thread: http://forum.unity3d.com/viewtopic.php?t=1237 It works very nicely, basically you just "plug in" the forward vector of your rotating object, and a vector from that object that points out at whatever you want to face (which you could construct yourself, ie <1, .5, 0>, for, say, a telescope pointing north with 45° ascension) The only edgecase, of course, is that it's not always guaranteed which way (left or right) it will turn if it's completely BEHIND the object that's rotating. But there are a few ways around that.