views:

90

answers:

4

I have a unit in a game which is pointing in a particular direction; when it turns to another direction it should take the shortest turn available. The scheme starts at a particular angle, and requires a given angle to lerp (linear interpolate) towards.

For example, lerping from 10 degrees to 350 degrees should calculate a target angle of -10. Conversely lerping from 350 to 10 should calculate an end end of 370 degrees.

What algorithm can calculate these required end values?

EDIT:: There seems to be some confusion over what I need

I'm using linear interpolation to calculate angles over time. So, if I want to get from 10 to 20 then the end value needs to be 20, then I shall interpolate from 10 to 20 and turn right. However, similarly if I want to go from 20 to 10, then interpolating from 20 to 10 will go anticlockwise, this is fine too. The problem is when the turn is more than 180 in the clockwise direction, to go from 270 to 80 (210 degrees) needs to turn clockwise, direct interpolation from 270 to 80 will go anticlockwise, I need to interpolate from 270 to 420 (360+80), which will go anticlockwise.

+4  A: 

You need

dist = (end - start + 360) % 360;
if (dist > 180)
   dist = dist - 360;

that gives you something in the range -180...180. If that is what you want.

You might also want to do an `if(dist < 180) dist += 360;` to handle another edge case ... where, say, `start` is 0 and `end` is -710...
Chris Burt-Brown
I don't quite understand how this solves the problem? if I'm lerping from 180->190 then I want my end value to be 190...
Martin
I just updated my question, I don't think this answers it (since I was pretty unclear before)
Martin
This calculates `dist`, the closest distance. If you want the closest 'end angle' then just say `end = start + dist`.
Chris Burt-Brown
+1  A: 

If you want something more focused on the 'end angle', how about this:

float startAngle = something, endAngle = something;

endAngle = ((endAngle - startAngle) % 360) + startAngle;
if(endAngle < startAngle - 180) endAngle += 360;
if(endAngle > startAngle + 180) endAngle -= 360;

This brings endAngle as close to startAngle as possible in increments of 360.

Chris Burt-Brown
Well I can confirm this one works, it's designed to achieve the end angle, and doesn't use a loop - gets my vote :)
Martin
+1  A: 

Martin, I think I understand what you want. Basically, you just need to add 360 to your end position until it's greater than your start position. Then subtract 360 from that. Those are the two absolute angles that could possibly be your solution. Then your answer is merely whichever is closest.

Pseudocode / real code:

int current = 350; // where you are now
int desired = 10; // where you want to go

while( desired < current)
  desired += 360;
int end = desired;
int start = desired - 360;

int delta1 = abs( end - current);
int delta2 = abs( start - current);
int answer = delta1 < delta2 ? end : current;
Dave
This look interesting. I'll have a play with this one...
Martin
I'll say right now that Chris' answer is more compact and more elegant, and it does work (at least when I tested a couple of cases). The main advantage of my approach is that it's simple to follow, and if you are up late at night you won't have trouble understanding it. Of course, as long as you have unit tests set up to go through several of your corner cases, you won't even have to look at it. ;)
Dave
A: 

If you are turning towards a specific point and just want to know whether to turn clockwise or anti-clockwise during this frame, the usual way of doing it is to consider the player's direction as a vector, and take the cross-product between it and the vector spanning from the player to the point. If it is positive, turn anti-clockwise; if it's negative, turn clockwise.

BlueRaja - Danny Pflughoeft