Sign in to follow this  

Finding the smallest turn angle

This topic is 4202 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

Hi., This is for a npc orientation system. Imagine the npc walking around in his path, and then turning into some random direction. I find the turning angle, and i turn it to that direction. However, i'm making a smooth rotation, so i need to find the smallest turn angle. For example, the NPC is walking with angle orientation of 1º, and i find that his new orientation will be -30º. So, i just do: if(angle_to_be > npc->current_orientation ) npc->angle++; if(angle_to_be < npc->current_orientation ) npc->angle--; And i update the npc_angle until he matches the npc_current_orientation. However this has problem., imagine that the npc has a walking angle of 1º, and the new orientation angle will be 330. In this case, he will do a huge rotation over himself, to get from 1 to 330, when it would be simpler to rotate -31º, which would be a much smaller. So, if i do this : if( (angle_to_be < 0) && ( npc->current_angle > 0) )angle_to_be += 360; else if( (npc->current_angle < 0) && (angle_to_be > 0) )angle_to_be -=360; Altough this fixes for 80% or 90% of the cases, its still not working 100%, in the first case for example, angle orientation of 1º, and a new orientation of -30º, it fails again, as he will make a rotation of 329º instead of a -30. I think i'm overcomplicating this..., Anyone can give me a hand here ? thanks

Share this post


Link to post
Share on other sites
You could probably get this to work with the right combination of conditionals and whatnot, but I'd think it would be easier to take a slightly different approach. Here's what I'd do:

1. Compute the direction vector for the current angle (you probably already have it available)

2. Compute the direction vector for the goal angle

3. Compute the signed angle between them as atan2(perp_dot(a,b), dot(a,b))

This will always give you the angle to the goal orientation with the smallest magnitude. Also, if you don't already have a 'perp dot' function, it's just a.x*b.y-a.y*b.x.

Share this post


Link to post
Share on other sites
Assuming you have a function that wraps a variable into a specified range you first wrap the desired angle around the desired angle, then you modify the angle, then you wrap the final angle into [180, 180]. So:



// wraps val into the range valMin, valMax by adding/subtracting multiples
// of (valMax - valMin). Requires valMax > valMin.
template<typename T>
inline void Wrap(T &val, const T &valMin, const T &valMax)
{
assert(valMax > valMin);
while (val > valMax)
val -= (valMax - valMin);
while (val < valMin)
val += (valMax - valMin);
}

void YourFn()
{
//... your stuff

Wrap(angle_to_be, npc->angle-180, npc->angle+180);
if (angle_to_be > npc->angle) ++npc->angle;
else if (angle_to_be < npc->angle) --npc->angle;
Wrap(npc->angle, -180, 180);
}


Untested/uncompiled code so apologies for typos, but I hope the idea makes sense :)

Share this post


Link to post
Share on other sites
Try this C++ func i just wrote to tween between two angles using the shortest distance. Let me know if it doesn't work and I'll try a closer look, I've not tested it.

Not claiming this to be the ideal method, it's just the one I happen to use.


float TweenAngle( float from,float to,float tween ) //tween should be between 0 and 1. 0 = from. 1 = to and inbetween = inbetween.

if( from>to )
{
float d1,d2;
d1 = from-to;
d2 = 360 - from + to;
if(d1<d2)
{
return from-(d1*tween);
}else
{
return from+(d2*tween);
}
}else
{
float d1,d2;
d1 = to-from;
d2 = 360 - to +from ;
if(d1<d2)
{
return from+d1*tween;
}else
{
return from-d2*tween;
}
}




This will return the correct angle but you might want to do a ang mod 360 to wrap it into a valid 0-360 range if needed.

Share this post


Link to post
Share on other sites
Try this C++ func i just wrote to tween between two angles using the shortest distance. Let me know if it doesn't work and I'll try a closer look, I've not tested it.

Not claiming this to be the ideal method, it's just the one I happen to use.


float TweenAngle( float from,float to,float tween ) //tween should be between 0 and 1. 0 = from. 1 = to and inbetween = inbetween.

if( from>to )
{
float d1,d2;
d1 = from-to;
d2 = 360 - from + to;
if(d1<d2)
{
return from-(d1*tween);
}else
{
return from+(d2*tween);
}
}else
{
float d1,d2;
d1 = to-from;
d2 = 360 - to +from ;
if(d1<d2)
{
return from+d1*tween;
}else
{
return from-d2*tween;
}
}




This will return the correct angle but you might want to do a ang mod 360 to wrap it into a valid 0-360 range if needed.

Share this post


Link to post
Share on other sites
This should work:


if (new_angle > current_angle && new_angle - current_angle <= 180)
current_angle += new_angle - current_angle;
else if (new_angle > current_angle && new_angle - current_angle > 180)
current_angle -= 360 - (new_angle - current_angle);
else if (new_angle < current_angle && current_angle - new_angle <= 180)
current_angle -= current_angle - new_angle;
else if (new_angle < current_angle && current_angle - new_angle > 180)
current_angle += 360 - (current_angle - new_angle);

Share this post


Link to post
Share on other sites

This topic is 4202 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this