Jump to content
  • Advertisement
Sign in to follow this  
Mybowlcut

Facing an object towards another object in 2D

This topic is 2981 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

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]

Share this post


Link to post
Share on other sites
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.

Share this post


Link to post
Share on other sites
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);
}

Share this post


Link to post
Share on other sites
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.

Share this post


Link to post
Share on other sites
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..

Share this post


Link to post
Share on other sites

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]

Share this post


Link to post
Share on other sites
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;
}


Share this post


Link to post
Share on other sites
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?

Share this post


Link to post
Share on other sites
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.

Share this post


Link to post
Share on other sites
Sign in to follow this  

  • Advertisement
×

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

We are the game development community.

Whether you are an indie, hobbyist, AAA developer, or just trying to learn, GameDev.net is the place for you to learn, share, and connect with the games industry. Learn more About Us or sign up!

Sign me up!