Page 1 of 1

A new angle on things

PostPosted: Mon Jul 09, 2007 5:16 am
by Fuzzy
Have you ever made a game, and you have to have an actor flip exactly 180 degrees and head the other way?

GEs angle is a double float. Its got a lot more precision than we probably need, and there are several drawbacks.

Say your angle is 0. and you want to turn back 1 degree. In GE, if you do this, you get the result -1. Thats not right! it should be 359! Luckily GE handles this quite well, and knows that the angle is the same as 359.

Say your angle is 359 and you add one..you get 360! That aint right! It should be 0. Lets add one more.. 361! Arg! it should be 1. but again, GE knows.

Still this raises merry hell with your math! If only there was a way to constrain this to 0-359. There is, and it comes standard with C/C++ and GE too. Its called the modulus operator. It looks like %, and you use it like this
Code: Select all
angle = angle % 359;


One problem. modulus doesnt like float values, and angle is a double. In any case, Makslane has already designed it to correct, so in essence, we would be doing this stage twice.

Next problem. We have a soldier, and an enemy is sneaking up behind him. He hears, and needs to spin around in one direction of the other. Which ever is shorter of course. We could just add 180 to the angle, but we want him to turn slowly. Which way to go?

We could use a series of ifs, but if you know me, I dont like those! Thats a lot more thinking from draw event to draw event than we want.

So lets make our own angle system, and plug it in to GEs angle right near the end. If we use integers, we can make use of modulus, and if we do it right, several other tricks can be created.

lets use two unsigned chars. one will handle from 0-179 and the other from 180-359. We want to make use of all the bits of those chars, so our angle system will have 8+8 bits, or 16 bits of precision, or a value from 0-65535.

That should be enough discrete angles for most applications. In fact, thats
180 places for each angle in the 360 degree system.

We know that the halfway point is going to be 32767. So lets define that. We will do 1 quarter and 1 eighth as well. The nice thing about this system is it divides very well. Also, a conversion number.
Code: Select all
// Global code
#define HALF = 32768
#define QUARTER = 16384
#define EIGHTH = 8192
#define TEENTH = 4096
#define THIRTY2 = 2048
#define SIXTY4 = 1024;
#define ONE28 = 512;
#define TWO56 = 256;
// and so on... and so on...


So we also define an unsigned short int as a WORD in global code.
Code: Select all
#define WORD unsigned short int


And then, also in global code, we define the new angle type.
Code: Select all
WORD IntAng;


So now, we test it out, and subtracting 1 from 0 in variable IntAng gives us 65535. Thats the same as 359. if we add one or two to 65535, we get 1 or 2, depending. If we try to assign a value of 35536 to IntAng, it corrects it to 0.

Now.. we have to be careful to not assign degree values to our system. one degree in our new system is 0.0027777777777777777777777777777778 degrees in the regular system. So all we have to do is store that and multiply that value by our special degrees to get regular ones. lets #define it in global code.

Code: Select all
#define INTDEG_TO_DEG = 0.0027777777777777777777777777777778


and to reverse it, we need to squeeze that angle into 16bits. 360 degrees actually is stored as 9 bits(not considering that its really a 64 bit float), which is why we get that odd 361 degree situation in the beginning.

What we need to do with out angle is called normalizing, which takes a abitrary number and ranges it from 0.0 to 1.0 and from there, we go to 0-65535. Its really easy. Just divide our angle by 360.

360/360 is 1.0, so we know thats right. the problem comes from 360/0, which just isnt possible. So a trick is in order, and its going to play into the hands of the 361 problem. Add one to your angle, and then divide by 361!
Code: Select all
 // a little function...
double Normalize(double number)
{
    return (number+1/361)-(1/361);
}


use it like this:
Code: Select all
angle = Normalize(angle);


Now we just have to typecast that. GEs internals will do it for us.

Code: Select all
IntAng = (WORD)angle*65536;


Note that you wouldnt use the angle variable in here, but instead, a temporary float value. changing the angle on the fly could cause some odd behavior.

If you are smart, you will probably see that i took the long way. we simply could have type casted angle into a WORD, and vice versa. But I got to cover some helpful stuff, like the normalizing, and how to avoid a divide by zero without an if.

You can use things like integer addition tricks to increment by fractions..

Code: Select all
IntAng++; // will add actually add 0.0027777777777777777777777777777778 and not simply one degree
// IntAng += TEENTH; // will add a nice 1/16 step around the angle circle.


and knowing which way to turn is a snap. In our case, the compiler will never have to mistakenly assume that 376 is greater than -16. (-16 is actually larger by 32).

So you get the angle to the enemy soldier sneaking up, change it to our system, ask if its greater than HALF, and you know which way to turn.

Then to finish up you just assign our angle(IntAng) to GEs angle. You dont even really have to type cast it, GE will figure it out.

Ok. thats wordy enough, and I am overly tired. I took the long way through, but I hope I covered some new things for you all. Forgive my typos and syntax error if any!

PostPosted: Mon Jul 09, 2007 3:17 pm
by Oman
uh... wow :? , i only understand half of that, but thank you for that demo.

PostPosted: Tue Jul 10, 2007 1:20 am
by Game A Gogo
wow, here is a well deserved point!

Never

PostPosted: Sun Jul 15, 2007 2:17 am
by kyensoftware
Never had that problem.
I have always used
Code: Select all
if (man.angle => 360)
{
    man.angle = 0
    //and vice-versa
}