Which way do I turn?

Started by
6 comments, last by Hedos 15 years, 12 months ago
I'm rewriting my game I made back at uni. I'm changing the control system somewhat so that it uses both the mouse and keys. i.e. Mouse for looking direction and keys for accelleration/strafing. This is happening from a top-down view with a helicopter. Now, as to where I'm currently at. I take the middle point of the sprite and the mouse coordinates. Now I calculate the angle the helicopter wants to point at (toward the mouse) using an atan function. The atan function in question returns a value from -128 to 128 (it's a 256 degree circle, not your usual 360 degree one). I now kind of lerp the current angle of the helicopter and the angle the helicopter wants to point to (the mouse pointer). Normally this would work fine, but when the new heading would change from -128 to 128 or the other way around, the helicopter would spin the long way around. This was my old code: angle is the angle the helicopter is currenltly at. newHeading is the angle the helicopter wants to be. //translating the newHeading to the new angle if (angle >> 2 == newHeading >> 2) angle = newHeading; else if (angle - newHeading > 128) angle += HELIANGLECHANGE; else if (newHeading - angle > 128) angle -= HELIANGLECHANGE; else if (angle < newHeading) angle += HELIANGLECHANGE; else angle -= HELIANGLECHANGE; Is there an easier way to write this? It's quite confusing I know, but being the eccentric programmer I am, I find it hard to explain the code I wrote almost 6 months ago :S Thanks guys in advance. :) If you need me to explain anything clearer, just ask.
My blog - Attempting to sell my skills to the game industry:http://devyourdream.blogspot.com
Advertisement
Here's what I'd do.

// Return the difference between 'a' and 'b' as an angle between -128 to 128// Using a counter-clockwise orientation, if the smallest angle goes from a to b// this function will return a positive value. If the smallest angle goes from b// to a, this function will return a negative value.int AngleBetween(int a, int b){  int d1 = abs(a - b);  int d2 = 256 - abs(a - b);  int sign;  if( d1 <= d2 )    sign = (a <= b ? 1 : -1)  else    sign = (b <= a ? 1 : -1)    return sign*min(d1, d2);}// Now the function that rotates the helicopter a little bit toward the mousevoid RotateHelicopter(int newHeading){  // Suppose that 'angle' is the current heading of the helicopter  if( AngleBetween(angle, newHeading) > 0 )    angle += HELIANGLECHANGE;  else    angle -= HELIANGLECHANGE;}


So with the function AngleBetween, when I call it with the arguments in this order : AngleBetween(angle, newHeading), it returns a positive number if 'newHeading' is to the left of 'angle' and a negative number if 'newHeading' is to the right of 'angle'.

So, the idea to make your code simpler is to abstract away with useful functions like this. The complicated code goes into a lower level function, so that the higher level code can be simpler.

What is interesting is that you can also use this function to do more, such as making the helicopter take the turn smoothly, and that is incredibly simple. You could make the speed at which it turns proportionnal to the angle between the current heading and the mouse, instead of just a constant, i.e.,

void RotateHelicopter(int newHeading){  // Suppose that 'angle' is the current heading of the helicopter  angle += A*(1-exp( B*AngleBetween(angle, newHeading) ));}


Where A and B would be constants you can adjust.
Dot product mini tutorial

something like this normally works
//target vector, the vector that you want to turn toVector2 tv = new Vector2(mouseX-pos.x, mouseY-pos.y);//helicopterDirection is the direction vector the helicopter is facing (cos(angle),sin(angle)) if you stored the angle and don't have itVector2 temp = new Vector2();if(tv.y*helicopterDirection.x - tv.x*helicopterDirection.y > 0){	temp.x = Math.cos(turningRate) * helicopterDirection.x - Math.sin(turningRate) * helicopterDirection.y;	temp.y = Math.cos(turningRate) * helicopterDirection.y + Math.sin(turningRate) * helicopterDirection.x;	helicopterDirection.x = temp.x;	helicopterDirection.y = temp.y;	if (tv.y * helicopterDirection.x - tv.x * helicopterDirection.y < 0)	{		tv.normalize(1);		helicopterDirection.x = tv.x;		helicopterDirection.y = tv.y;	}}else{	temp.x = Math.cos(-turningRate) * helicopterDirection.x - Math.sin(-turningRate) * helicopterDirection.y;	temp.y = Math.cos(-turningRate) * helicopterDirection.y + Math.sin(-turningRate) * helicopterDirection.x;	helicopterDirection.x = temp.x;	helicopterDirection.y = temp.y;	if (tv.y * helicopterDirection.x - tv.x * helicopterDirection.y > 0)	{		tv.normalize(1);		helicopterDirection.x = tv.x;		helicopterDirection.y = tv.y;	}}

yeah, store the rotation matrix unless you don't have a matrix library. (should work :P)

[Edited by - Sirisian on May 5, 2008 8:55:40 AM]
Does atan2 not work for you?
I believe he wants gradual rotation not instantaneous rotation of his helicopter.
Sorry guys, it was about 2am when I posted this, so sorry if I didn't make complete sense.

I've made an exe for you to try out.
http://rapidshare.com/files/108629898/nA2-movement_example.zip.html
Basically it'll turn gradually as you say Sirisian.
Oh and I am using atan2 from the Allegro library. Sorry I didn't mention it.

I'll most likely be using Hedos' example. It seems to fit my problem well.

Thanks for all the input. This really helps me a lot!

Oh and if the exe doesn't work, please tell me.
My blog - Attempting to sell my skills to the game industry:http://devyourdream.blogspot.com
Quote:Original post by Hedos
Here's what I'd do.

void RotateHelicopter(int newHeading){  // Suppose that 'angle' is the current heading of the helicopter  angle += A*(1-exp( B*AngleBetween(angle, newHeading) ));}


Where A and B would be constants you can adjust.


Okay, here's my question:

Does angle change more when AngleBetween() returns a larger value (positive or negative)? Or is it that angle will change more when AngleBetween returns a smaller number?

Should exp( B*AngleBetween(angle, newHeading) ) return a number from [0, 1]?

EDIT: Okay, so I understand why the 1-exp(value) is there. It doesn't have to be [0, 1]. My question is, what happens if AngleBetween returns a negative?

For cases AngleBetween returning -5 and 5 (with A = 2, B = 1 and angle = 64).

angle += 2(1-exp(1*5));
angle += 2(1-100000);
angle += -199998;

angle += 2(1-exp(1*-5));
angle += 2(1-0.00001));
angle += 1.99998

Uhm... I would have thought the results would have been the same, only one would be negative and one positive.

I'm probably missing something, but I'll write something myself 'til I understand it better.

[Edited by - lone_wolfII on April 23, 2008 12:09:21 AM]
My blog - Attempting to sell my skills to the game industry:http://devyourdream.blogspot.com
Quote:Original post by lone_wolfII
Quote:Original post by Hedos
Here's what I'd do.




Where A and B would be constants you can adjust.


Okay, here's my question:

Does angle change more when AngleBetween() returns a larger value (positive or negative)? Or is it that angle will change more when AngleBetween returns a smaller number?

Should exp( B*AngleBetween(angle, newHeading) ) return a number from [0, 1]?

EDIT: Okay, so I understand why the 1-exp(value) is there. It doesn't have to be [0, 1]. My question is, what happens if AngleBetween returns a negative?

For cases AngleBetween returning -5 and 5 (with A = 2, B = 1 and angle = 64).

angle += 2(1-exp(1*5));
angle += 2(1-100000);
angle += -199998;

angle += 2(1-exp(1*-5));
angle += 2(1-0.00001));
angle += 1.99998

Uhm... I would have thought the results would have been the same, only one would be negative and one positive.

I'm probably missing something, but I'll write something myself 'til I understand it better.


You're right, I didn't consider negative angles in my last example. However, exp(1*5) != 100000, you're confusing with the notation 1e5 which means 10^5. In C++, the exp(x) function corresponds to e^x, an exponential.

So, if AngleBetween returns a large number, the variable 'angle' changes more. When AngleBetween returns zero, 'angle' doesn't change because e^0 = 1.

Here's how to make it "symmetric" so that it works with negative angles too:

// That function returns 1 if the argument is positive or zero// and -1 if the argument is negativeint sign(int x){  return (x >= 0 ? 1 : -1);}void RotateHelicopter(int newHeading){  // Suppose that 'angle' is the current heading of the helicopter  int t = AngleBetween(angle, newHeading);  angle += sign(t)*A*(1-exp(B*abs(t)));}


Note that you can adjust 'B' to change the speed at which 'angle' varies, but B should always be negative. To understand why, you may want to graph the function of (1 - e^(x)) and (1 - e^(-x)).

Also, the exponential I used is just one way to make turning smooth, because it satisfies the two criterias of 'angle' changing fast when AngleBetween is small and 'angle' changing slow when AngleBetween is almost zero. You could use many other functions.

This topic is closed to new replies.

Advertisement