Sign in to follow this  
P0jahn

Missile Pathing for 2D game

Recommended Posts

P0jahn    307
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

Share this post


Link to post
Share on other sites
DgekGD    374

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).

Share this post


Link to post
Share on other sites
P0jahn    307

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?

Share this post


Link to post
Share on other sites
DgekGD    374

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:)

Share this post


Link to post
Share on other sites
P0jahn    307

Still confused. Sorry for being dull.

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

Share this post


Link to post
Share on other sites
DgekGD    374

 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 :)

Share this post


Link to post
Share on other sites
P0jahn    307

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?

Share this post


Link to post
Share on other sites
alvaro    21246

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.

[code] vector_to_target = target - position;   acceleration = spring_constant * vector_to_target - damping_constant * velocity;   velocity = velocity + delta_t * acceleration;   position = position + delta_t * velocity;[/code]

 

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.

Share this post


Link to post
Share on other sites
alvaro    21246

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.

Share this post


Link to post
Share on other sites
P0jahn    307

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.

Share this post


Link to post
Share on other sites
P0jahn    307

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;

Share this post


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

Share this post


Link to post
Share on other sites
alvaro    21246

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

Share this post


Link to post
Share on other sites
P0jahn    307

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.

Share this post


Link to post
Share on other sites
alvaro    21246

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.

Share this post


Link to post
Share on other sites
P0jahn    307

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".

Share this post


Link to post
Share on other sites
JTippetts    12949
I did a little test using Lua Love2D. alvaro is correct in that you don't need to keep a separate speed variable if you don't normalize your (vx,vy) velocity vector. Here is some quick video of the test using a field 640x480, initial missile velocity of (1,0), spring of 3 and damping of 1 against a circling target:
 
[media]http://www.youtube.com/watch?v=1bQjN_6Xj2k[/media]
 
The missile updating code:
 

	local dx=target.x-missile.x
	local dy=target.y-missile.y

	local accelx=missile.spring*dx-missile.damping*missile.vx
	local accely=missile.spring*dy-missile.damping*missile.vy

	missile.vx=missile.vx+ dt*accelx
	missile.vy=missile.vy + dt*accely
	
	missile.x=missile.x+dt*missile.vx
	missile.y=missile.y+dt*missile.vy


It's pretty much just as written in his initial post, without normalization of (dx,dy) and without an external speed variable. You do have to play with the spring and damping constants quite a bit to get something that feels right.

Share this post


Link to post
Share on other sites
P0jahn    307

Bump!

There is an other thing, about the missiles rotation.

Currently in my game, the missile is always rotated so it is pointing at the target. Instead, I want it to rotate like the youtube click above posted by JTippetts.

In the clip, the missile rotates towards its direction.

 

How to achieve this?

Share this post


Link to post
Share on other sites

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