Jump to content

  • Log In with Google      Sign In   
  • Create Account

Interested in a FREE copy of HTML5 game maker Construct 2?

We'll be giving away three Personal Edition licences in next Tuesday's GDNet Direct email newsletter!

Sign up from the right-hand sidebar on our homepage and read Tuesday's newsletter for details!


We're also offering banner ads on our site from just $5! 1. Details HERE. 2. GDNet+ Subscriptions HERE. 3. Ad upload HERE.


Missile Pathing for 2D game


Old topic!
Guest, the last post of this topic is over 60 days old and at this point you may not reply in this topic. If you wish to continue this conversation start a new topic.

  • You cannot reply to this topic
27 replies to this topic

#1 P0jahn   Members   -  Reputation: 272

Like
0Likes
Like

Posted 04 January 2013 - 07:54 PM

I am creating a 2D game, that use the same coordinate system like any other images or 2D games(top-left corner is 0,0. Moving to the right and x increases, moving down and y increases).

I have created a homing/heat seeking missile for my game and I am very dissatisfied with the results. Since the missile is heat seeking, it must update its target position every X amount of frames. When it have updated its target, it could do an ugly 90 degree turn, if necessary. This effect is unwanted. Instead, I want it to move in a curve. I cant figure out how to accomplish this.

Please note that I have low math skills.

Edited by P0jahn, 04 January 2013 - 07:55 PM.


Sponsor:

#2 DgekGD   Members   -  Reputation: 374

Like
0Likes
Like

Posted 05 January 2013 - 05:19 AM

Basically, when your missile updates it's target position(it's not necessary to do every single frame) find target angle of rotation, so that missile points in right direction (with the help of atan2 function).

And than update missile rotation as you update its position. Here is an example:

 missile.angularAcceleration = targetOrientation - missile.orientation;
 if (missile.angularAcceleration < 0)
   missile.angularAcceleration = -maxAngularAcceleration;
 else
   missile.angularAcceleration = maxAngularAcceleration;

 missile.orientation += missile.rotation * time;
 missile.rotation += missile.angularAcceleration * time;

 if (missile.rotation > maxRotation) missile.rotation = maxRotation;


It's far not ideal implementation, just to give you an idea. Actually, you can make your rotation even smoother with the use of slow down radius for example(when angle to rotate remains small it starts to slow down a little bit). Also, maybe you fill need some radius of satisfaction(it's doubtful that your object will rotate EXACTLY at the angle you need and it will end up in flickering).



#3 P0jahn   Members   -  Reputation: 272

Like
0Likes
Like

Posted 05 January 2013 - 07:36 AM

Thank you. I will definitely try that out. 

Some variable names and the usage is a bit confusing. 

 

 

find target angle of rotation

Find the angle between the two points(target and missile)?
If yes, would something like this work?

float deltaX = x2 - x1;
float deltaY = y2 - y1;

return Math.atan2(deltaX, deltaY);

Finally, what do the orientation variables represent?



#4 DgekGD   Members   -  Reputation: 374

Like
0Likes
Like

Posted 05 January 2013 - 07:50 AM

Yep, atan2 function will work, perhaps you have to swap deltaX and deltaY arguments to get an angle from X axis. 

 

  Orientation - is a desired angle or current angle ( position for movement )

  Rotation - is a speed orientation is changed (velocity for movement )

  angularAcceleration - is a speed rotation is changed (acceleration for movement)

 

 I hope everything is clear now:)



#5 P0jahn   Members   -  Reputation: 272

Like
0Likes
Like

Posted 05 January 2013 - 09:24 AM

Still confused. Sorry for being dull.

Could you make a quick example, that contains the missiles and the targets coordinates(missileX, missileY, targetX, targetY)?



#6 DgekGD   Members   -  Reputation: 374

Like
0Likes
Like

Posted 05 January 2013 - 12:10 PM

 Okay. Imagine you missile is going to the right (1, 0) - x and y coordinates accordingly. Currently it has position (200, 200). It's target lies in the point (300, 200), so target angle between missile direction and X axis is zero(targetOrientation = 0) and current angle of missile direction and X axis is zero(missile.orientation = 0)

 

 Next frame your target moves to the position (200, 100), probably you won't have such a huge jumps in just a single frame, but i try to show you an idea. So your desired direction for a missile is (0,1) now which make angle of Pi/2 (90 degrees) with X axis. So you start to rotate your missile. Your maxAngularAcceleration value should be already is defined for each missile, so you increase its rotation :missile.rotation += missile.angularAcceleration * time  and than orientation (or angle between direction and X axis) :  missile.orientation += missile.rotation * time.

 

 Than you use current angle to get movement direction. Start with creating Z axis rotation matrix (Z axis goes into your screen or out of your screen) and multiply vector (1, 0) with this matrix. As a result you will get rotated vector. That you can multiply it by speed scalar. You can read this small tutorial if you still have some questions regarding retrieving direction from current angle.

 

 I hope it helps ;) Let me know if you still have some questions :)



#7 P0jahn   Members   -  Reputation: 272

Like
0Likes
Like

Posted 05 January 2013 - 02:05 PM

I dont know if this is compatible with my game mechanics.

 

 

The missile use its x and y coordinates to move towards the target every frame. Your example dont even touch such variables. 

Should I add missile.orientation to the missiles x coordinate every frame?

Should I add missile.rotation to the missiles y coordinate every frame?

How often should I recalculate these variables?

Formula to calculate the orientations?



#8 Álvaro   Crossbones+   -  Reputation: 13645

Like
1Likes
Like

Posted 05 January 2013 - 02:24 PM

I wouldn't use atan2 or angles at all. Your missile has a position and a velocity. Each frame, you look at your target position and apply some acceleration to try to get you there, as if the missile and the target were joined by a spring.

 vector_to_target = target - position;
  acceleration = spring_constant * vector_to_target - damping_constant * velocity;
  velocity = velocity + delta_t * acceleration;
  position = position + delta_t * velocity;

 

I don't use the coordinates (x,y) because I am using vector notation. If you want to express the code above using coordinates, each line becomes two lines: One for the x value of each vector and one for the y value. delta_t is the time elapsed since the previous frame (you can make it a constant).

 

By playing a little bit with the constants, you should be able to get the behavior you want.



#9 P0jahn   Members   -  Reputation: 272

Like
0Likes
Like

Posted 05 January 2013 - 02:47 PM

Thanks. I am getting there now.

Can you recommend constant values for damping, spring and delta_t?



#10 Álvaro   Crossbones+   -  Reputation: 13645

Like
0Likes
Like

Posted 05 January 2013 - 03:09 PM

delta_t is the length of a frame in seconds, so something like 1/60, if your game runs at 60 FPS. For the other two, I would have to do some fiddling around myself.

 

I was just going over my previous post and I think you may want to normalize vector_to_target, so your missile accelerates equally regardless of how far away the target is.



#11 P0jahn   Members   -  Reputation: 272

Like
0Likes
Like

Posted 05 January 2013 - 05:54 PM

Normalizing it is causing weird behavior.

Code:

 

float dx = targetX - posX;
float dy = targetY - posY;
float length = (float) Math.sqrt( dx*dx + dy*dy );
dx /= length;
dy /= length;

The three last lines is the normalization. Without them, it is like you explained above, it start to move slower when as it comes closer to its target.

With them, the result is inconsistent, the missile just move one path into an wall.



#12 Álvaro   Crossbones+   -  Reputation: 13645

Like
0Likes
Like

Posted 05 January 2013 - 05:55 PM

Can you show the other lines of code?



#13 P0jahn   Members   -  Reputation: 272

Like
0Likes
Like

Posted 05 January 2013 - 06:02 PM

Full set:

 

float dx = targetX - posX;
float dy = targetY - posY;
double length = Math.sqrt( dx*dx + dy*dy );
dx /= length;
dy /= length;


float accX = spring_constant * dx - damping_constant * moveSpeed;
float accY = spring_constant * dy - damping_constant * moveSpeed;


float velocityX = moveSpeed + unknown_constant * accX;
float velocityY = moveSpeed + unknown_constant * accY;


//Apply the movment to this object
posX += unknown_constant * velocityX;
posY += unknown_constant * velocityY;


#14 JTippetts   Moderators   -  Reputation: 8571

Like
0Likes
Like

Posted 05 January 2013 - 06:21 PM

Seems like you are multiplying your damping constant by your speed, rather than your velocity. There is a difference, since velocity is speed*direction.

#15 P0jahn   Members   -  Reputation: 272

Like
0Likes
Like

Posted 05 January 2013 - 06:27 PM

speed*direction? Can you please elaborate what direction is?



#16 JTippetts   Moderators   -  Reputation: 8571

Like
0Likes
Like

Posted 05 January 2013 - 06:46 PM

The vector (dx,dy) normalized denotes a direction (or, also a velocity with a speed of one), and the vector (dx*speed, dy*speed) denotes velocity. People often mistakenly think that velocity is equal to speed, but it isn't. In a unit-length vector (such as your normalized (dx,dy)) the speed is 1, since that is the length of the vector. If you add that vector to a position, the position will translate by 1 unit; ie, it will move at a speed of 1 unit per frame along the direction (dx,dy). If you scale (dx,dy) by speed then you have a vector whose length is speed. Translating a point by this vector now will move it speed units; ie, it moves at a speed of speed units per frame along the direction indicated by (dx,dy).

alvaro indicated that you need to normalize your (dx,dy) vector to unit length, otherwise the acceleration of the missile's course correction scales by the distance between current position and target position.

Now, in the original posted equations alvaro said to calculate acceleration by
acceleration = spring_constant * vector_to_target - damping_constant * velocity;

This breaks down to:
ax=spring_constant*dx - damping_constant*vx*speed
ay=spring_constant*dy - damping_constant*vy*speed

Where (vx,vy) indicates the current normalized direction vector of the missile (what direction it is currently travelling along) and speed, of course, is the speed of the missile. Correspondingly, he indicated you should calculate the new velocity by:
 
velocity = velocity + delta_t * acceleration;

This, again, breaks down to:
 
vx=vx*speed+delta_t*acceleration
vy=vy*speed+delta_t*acceleration

Note that your velocity will grow, as (vx,vy) will now be non-normalized (ie longer than 1). You should normalize (vx,vy) and either set speed from the length of (vx,vy) before normalization or cap the speed at some maximum, however you want to handle it. Otherwise, the velocity will grow to crazy amounts and your missiles will whizz wildly off the screen.

Edited by JTippetts, 05 January 2013 - 06:51 PM.


#17 Álvaro   Crossbones+   -  Reputation: 13645

Like
0Likes
Like

Posted 05 January 2013 - 07:13 PM

I didn't read JTippetts's post very carefully, but I don't think you need a notion of speed at all. If you are thinking of something like a maximum speed for the missile, that will be a natural consequence of the balance between the attractive force and the damping (from drag). This is actually what sets the speed of a missile in real life.

 

 

float dx = targetX - posX;
float dy = targetY - posY;
double length = Math.sqrt( dx*dx + dy*dy );
dx /= length;
dy /= length;
 
 
float accX = spring_constant * dx - damping_constant * velocityX;
float accY = spring_constant * dy - damping_constant * velocityY;
 
 
velocityX += delta_t * accX;
velocityY += delta_t * accY;
 
 
//Apply the movment to this object
posX += delta_t * velocityX;
posY += delta_t * velocityY;
 
As I explained before, delta_t is the time between frames.
 
EDIT: "Thrust" is probably a better name than "spring_constant", and "drag_constant" would be better than "damping_constant". I used the other names earlier because of the analogy to a damped spring, but once you normalize the vector to the target, the analogy breaks down.
 

Edited by Álvaro, 05 January 2013 - 07:15 PM.


#18 P0jahn   Members   -  Reputation: 272

Like
0Likes
Like

Posted 05 January 2013 - 07:33 PM

Thanks all.

There is no velocity variables in my game, so I calculate them after dx dy normalization like this:

So with your code above by Alvaro, inserting this to line 6

 

float velocityX = dx * moveSpeed;
float velocityY = dy * moveSpeed;
That is what my code currently look like. (Also tried some stuff JTippetts suggested).
 
Alvaro, I must ask you, how do this algorithm look ingame for you? Because for me, it looks identical to a simple normal "move towards point" algorithm.
I can post a video if you want.


#19 Álvaro   Crossbones+   -  Reputation: 13645

Like
1Likes
Like

Posted 05 January 2013 - 07:40 PM

The state of the missile includes its position and its velocity. If you make the velocity proportional to the acceleration at each step, you are effectively setting the velocity, not the acceleration, and you get constant-velocity movement instead of the desired seeking curve. Fix your code.



#20 P0jahn   Members   -  Reputation: 272

Like
0Likes
Like

Posted 05 January 2013 - 08:20 PM

I would once again like to emphasize that my overall math knowledge is awful. Now, tried to make sense of your post(no offence to you, it is my lack of knowledge that is the problem), this is what I came up with:

 

 

 

//This function is called every frame!


//Delta
float dx = targetX - posX;
float dy = targetY - posY;


double length = Math.sqrt( dx*dx + dy*dy );
dx /= length;
dy /= length;


//Update targetX, targetY, velX and velY every X amount of frames
if(++turnCounter % turnRate == 0)
{
targetX = currTarget.posX + currTarget.width  / 2;
targetY = currTarget.posY + currTarget.height / 2;


velocityX = dx * moveSpeed;
velocityY = dy * moveSpeed;
}


float accX = spring_constant * dx - damping_constant * velocityX;
float accY = spring_constant * dy - damping_constant * velocityY;


velocityX += unknown_constant * accX;
velocityY += unknown_constant * accY;


posX += unknown_constant * velocityX;
posY += unknown_constant * velocityY;

 

 

Result: same as before. Just normal "move towards point".






Old topic!
Guest, the last post of this topic is over 60 days old and at this point you may not reply in this topic. If you wish to continue this conversation start a new topic.



PARTNERS