I’m fairly new to programming. I wonder if anyone can help me please with a (probably basic) question.
I’m trying to find an efficient and versatile way to track a GameObject’s rotation, and do stuff when its angle reaches certain degrees, a bit like this:
// --------------------------------
var myObject : Transform;
var rotationOfMyObject : float;
var angleToDoStuffAt : float;
var mySpeed : float;
function Start () {
angleToDoStuffAt = 10;
mySpeed = 5.0;
}
function Update () {
myObject.Rotate (0.0, mySpeed* Time.deltaTime, 0.0);
//
rotationOfMyObject = myObject.eulerAngles.y;
if ( rotationOfMyObject >= angleToDoStuffAt ){
// Do Something...
angleToDoStuffAt += Random.Range(10, 40);
}
}
// --------------------------------
The problem I have with the above is :-
i) It’s only going to work for the first 360 degree turn, because after that, myObject.eulerAngles.y wraps around to zero again.
ii) It’s only going to work when the object is spinning in a positive direction, and not, for example if mySpeed = -5.0
I could probably solve this myself by using a million if-statements, but I can’t help but think I’m probably missing an easier and more efficient way to do the same thing.
You could put a little catch in there for when it goes over 360.0, then set it back like this :
if(rotationOfMyObject >= 360.0)
{
rotationOfMyObject -= 360.0;
// Not sure if you have to assign it back, I've just switch to C# and you have to do that with everything there.
myObject.eulerAngley.y = rotationOfMyObject;
}
That statement should clamp your angles between 0 and 360, and you can probably just test for the negative if you’re going to rotate the other way, or just test for less than 0 and add 360.
As for the second issue, without knowing more specific of the problem, you could just have a second test for if the speed is negative then reverse your other test cases. That means you won’t have a million if statements, just a few.
Thank you Murcho for your prompt reply, and apologies for not responding again quicker.
In my example, ‘rotationOfMyObject’ is already being automatically clamped between 0 and 360 (because it is derived from ‘myObject.eulerAngles.y’, which wraps between 0 and 360, if it didn’t I would not have a problem).
This is exactly the problem I have, ie. it is difficult to test exactly when the rotation has exceeded 360 and has automatically wrapped back round to 0.
I can’t test “if (rotationOfMyObject == 360)”, as I’m incrementing in steps of larger than 1 integer.
I can’t test “if (rotationOfMyObject > 360)” because this case never occurs, it wraps.
I also can’t test for a wrap, like “if (rotationOfMyObject < 10)” because this might also be the case at the start.
var kEpsilon : float = 0.1; // Tweak at your leisure.
var didStuff : boolean = false;
function Update()
{
// ...
if (rotationOfMyObject >= (angleToDoStuffAt - kEpsilon) rotationOfMyObject <= (angleToDoStuffAt + kEpsilon))
{
if (!didStuff)
{
DoStuffHere();
didStuff = true;
}
}
else
didStuff = false;
}
The kEpsilon gives you a margin of error, to account for floating point imprecision. The didStuff boolean ensures that DoStuffHere() is called only once per turn.
Caveat: DoStuffHere() would be skipped if the delta of the rotation between subsequent Update() calls is bigger than 2 * kEpsilon.
You can avoid the caveat as well, by taking into account the previous rotation.
edit: Come to think of it, using the last rotation is a better solution for this problem:
var didStuff : boolean = false;
function Update ()
{
var prevRotation = myObject.eulerAngles.y;
myObject.Rotate (0.0, mySpeed* Time.deltaTime, 0.0);
var curRotation = myObject.eulerAngles.y;
var minRot = Mathf.Min(prevRotation, curRotation);
var maxRot = Mathf.Max(prevRotation, curRotation);
if (minRot <= angleToDoStuffAt maxRot >= angleToDoStuffAtt)
{
if (!didStuff)
{
DoStuffHere();
didStuff = true;
}
}
else
didStuff = false;
}
This will call DoStuffAt() once per turn, regardless of speed and time between Updates(). Phew!