Facing an object towards another object in 2D

Started by
23 comments, last by Mybowlcut 13 years, 9 months ago
I've got a homing missile that homes in on its targets, using this code:
projectile->Set_Direction(Movement::get_dir_towards(    projectile->Get_Position(), target->Get_Position()));PointF movement(projectile->Get_Direction() * proj_speed * delta);projectile->Set_Position(projectile->Get_Position() + movement);projectile->Set_Dist_Travelled(projectile->Get_Dist_Travelled() + delta * proj_speed);

PointF Movement::get_dir_towards(const PointF& source, const PointF& target){    const float cos_a = (target.y - source.y) / sqrt(pow(target.x - source.x, 2) + pow(target.y - source.y, 2));    const float sin_a = (target.x - source.x) / sqrt(pow(target.x - source.x, 2) + pow(target.y - source.y, 2));    return PointF(sin_a, cos_a);}
It does it with no worries, but up till now I've just been using a ball as the missile. I decided to change the ball to a proper missile and create a sprite class that changes the sprite's frame depending on an angle passed in:
void Rotatable_Sprite::Calculate_Y_Clip(float angle){    // Frame is based on the angle.    int angle_frame = static_cast<int>(Maths::increment_closest_to(        degree_increment_size, angle) / degree_increment_size);    clip.y = angle_frame * Get_Frame_Height();}

float Maths::increment_closest_to(float increment, float angle){    // How many times does the increment go into the angle?    float times_into = angle / increment;    // The decimal part of times_into (e.g. if times_into = 1.56, will be .56).    float decimal_part = times_into - floor(times_into);    // If the part is bigger than 0.5, round up to the larger increment, vice versa.    return increment * (decimal_part >= 0.5f ? ceil(times_into) : floor(times_into));}

int Rotatable_Sprite::Get_Frame_Width() const{    return surface.Width() / animator.Get_Qty_Frames();}int Rotatable_Sprite::Get_Frame_Height() const{    return static_cast<int>(surface.Height() / (360.0 / degree_increment_size));}

This class seems to work fine - I've debugged it and it is setting the correct increment (I've done the artwork for the missile in rotation increments of 22.5 degrees to save time) and the correct clipping in the sprite sheet. However, the code I'm using to get the angle between the missile and its target seems to be a bit dodgy, as the missile sometimes faces the wrong way, etc.:
float angle = 0;const Unit* target(map.Unit_At(projectile->Get_Target_ID()));if(target && target->Is_Alive()){    PointF proj_pos = projectile->Get_Position();    PointF target_pos = target->Get_Position();    angle = abs(Maths::angle_between(proj_pos, target_pos));}projectile->Get_Sprite()->Set_Angle(angle);projectile->Get_Sprite()->Update(delta);

float Maths::angle_between(const PointF& p1, const PointF& p2){    return rad_to_deg(atan2(p2.y - p1.y, p2.x - p1.x));}

Can anyone see anything wrong with Maths::angle_between? I tried using Movement::get_dir_towards (the same function that determines the direction the missile should move in) but it seems to have much the same result.

Cheers.

[Edited by - Mybowlcut on July 13, 2010 4:05:40 AM]

Advertisement
You might know this, but watch out atan2 returns (-3.14f to +3.14f) radians, so maybe your rad to deg is to fault, but i didnt read the code that hard, so im just tipping.
Quote:Original post by rouncED
You might know this, but watch out atan2 returns (-3.14f to +3.14f) radians, so maybe your rad to deg is to fault, but i didnt read the code that hard, so im just tipping.

Hey thanks for the quick reply.

Here is my rad_to_deg (and deg_to_rad for good measure) function:
float Maths::rad_to_deg(float radians){	return radians * (180.0f / PI);}float Maths::deg_to_rad(float radians){	return radians * (PI / 180.0f);}

Homing Missile Tutorial in case you want the normal way.
Regarding your current method, assuming you have all the necessary deg<->radian conversions in place, it may be related to the fact that atan2() returns the angle in the range [-pi, pi] (as rouncED pointed out).

I didn't look at your code that carefully, but it looks like maybe the function that determines the integer index from the angle might not work correctly if the input angle is negative. To see if this is the problem, try adding 2pi to the angle if it's negative before calling the function that determines the current frame.
Quote:Original post by Sirisian
Homing Missile Tutorial in case you want the normal way.

Hey Sirisian.

I've already got your code in my library (credited via link of course ;) ). I've tried it several times but it never worked for me. I ended up using the code below from this thread:
PointF Movement::intercept(const PointF& shooter, float bullet_speed,const PointF& target, const PointF& target_velocity){    float a = bullet_speed * bullet_speed - target_velocity.Get_Dot_Product(target_velocity);    float b = -2 * target_velocity.Get_Dot_Product(target - shooter);    float c = -(target - shooter).Get_Dot_Product(target - shooter);    return target + target_velocity * Maths::largest_root_of_quadratic_equation(a, b, c);}
I have no idea what it does but it seems to work OK.

Quote:Original post by jyk
Regarding your current method, assuming you have all the necessary deg<->radian conversions in place, it may be related to the fact that atan2() returns the angle in the range [-pi, pi] (as rouncED pointed out).

I didn't look at your code that carefully, but it looks like maybe the function that determines the integer index from the angle might not work correctly if the input angle is negative. To see if this is the problem, try adding 2pi to the angle if it's negative before calling the function that determines the current frame.

Hey jyk.

I tried what you suggested in the form of this code:
angle = angle < 0 ? angle + PI * 2 : angle;

It gave me weird results because my Rotatable_Sprite class deals with angles between 0 and 360 degrees. Not sure what to do now..

deg=(int)(((rad+3.14f)/6.28f)*359.0f)


but its hard to say, maybe what you did is right, I cant remember I remember I had problems like this too, but I got it in the end. :) Good luck to you man.

I take it the missile is pointing, its just not pointing in the right direction, just sounds like your average game bug, youll get it i bet.

[Angle brackets for tags that are actually part of HTML; square brackets for tags that are part of GDNet forum markup. - Zahlman]

[Edited by - Zahlman on July 23, 2010 12:10:56 AM]
What you probably want is an angle between 0 and 15 to represent which of the 16 sprite frames you need. Try this:

int get_rotated_frame_number(const PointF& p1, const PointF& p2){    if (p1 == p2) return 0; // Can't calculate angle if the points are the same    int frame = (int)((atan2(p2.y - p1.y, p2.x - p1.x) + PI) * (8.0f / PI));    return frame % 16;}

Yeh do what Adam_42 said, and if the rotation is off by so many frames back and forward just add a number to it, maybe itll work?
Quote:Original post by Adam_42
What you probably want is an angle between 0 and 15 to represent which of the 16 sprite frames you need. Try this:

*** Source Snippet Removed ***

Hey Adam.

Not really sure what this is doing? I get that this bit:
(atan2(p2.y - p1.y, p2.x - p1.x))
is getting the angle between the two points, but what is this doing:
 + PI) * (8.0f / PI))
?

My sprite frame calculation is working fine; what I want to achieve is an angle between 0 and 360 that represents the angle between two points.

Cheers.

This topic is closed to new replies.

Advertisement