views:

289

answers:

5

I have a rather simple bird's-view 2D game where tower sprites defend against incoming moving sprites by shooting a bullet at them. My question: How do I calculate the needed bullet speed for the bullet to reach its moving target, provided that the bullet will always have the same defined speed?

I'm using JavaScript and have these sprite variables (among others): sprite.x, sprite.y, sprite.width, sprite.height, sprite.speedX (i.e. velocity), sprite.speedY... so I have the objects originSprite, targetSprite and bulletSprite, all with these type of values, and I need to set the right bulletSprite speed values.

Probably for it to look good, the bullet would start at the outside of the originSprite (or some defined radius, though I guess starting from the originSprite center would also work), but its bullet center would try hit into the center of the targetSprite or so. Note there's no gravity or anything in this world. (Perhaps I should have my sprites variables using angle and velocity but right now I'm using speedX and speedY...)

Thanks so much!

A: 

I assume that the target will move on a straight line with constant velocity.

If both the direction and the speed of the bullet are variable (i.e. you try to calculation speedX and speedY for the bullet), there are infinitely many solutions.

If you set a fixed direction, you simply intersect the two lines of the bullet and the target. From the distance between the current point of the target and the intersection point (and the target's speed) you can calculate the time the target will take to reach this intersection point.

From the distance between the origin of the bullet and the intersection point (and the previously calculated time) you can calculate the speed of the bullet.

MartinStettner
something is missing in the specification. I guess that it is the RANGE of the bullets. If the OP specifies a RANGE, then the number of solutions is also infinite, but there will be a MINIMUM speed for the bullet (ie that one that intersects the bullet and target at that RANGE)
belisarius
+1  A: 

Compute the distance between shooter and target: dist = sqrt((xt - xs)^2 + (yt - ys)^2)
Divide the x and y distances by the above one: nx = (xt - xs)/dist; ny = (yt - ys)/dist; (normalization of the vector)
Multiply the results by a factor to get n pixels per time unit, ie. a speed in each direction. It should give a constant speed in the wanted direction.

PhiLho
This doesn't take into account the speed of the target at all and will therefore miss unless the target is moving directly towards or away from the shooter.
Chris
+6  A: 

Treat the targets sprite as a straight line in a 2 dimensional room where:

A(time) = (sprite.positionX + sprite.speedX * time, sprite.positionX + sprite.speedX * time)

As your bullet have constant speed you also know:

bullet.speedX^2 + bullet.speedY^2 = bullet.definedSpeed^2

Then you can also calculate a straight line for the bullet:

B(time) = (bullet.positionX + bullet.speedX * time, bullet.positionX + bullet.speedX * time)

And you know that both lines interset somewhere:

A(time) = B(time)

Then it's up to you to solve those equations with your given values and seek a minimum for time.

Christian
If solving the system of linear equations is consuming too much CPU time when you are doing this in real-time (which it might, depending on how many bullets are in flight at the same time), you can probably pre-compute a set of solutions and only solve the corner cases in real-time. Assuming your targets will move with a known speed, calculate a result based on (say) eight different directions that they could approach from (N,NE,E,etc). When the shooter detects a target, simply look up whether you have a result pre-computed for a target with that speed and bearing. If not, calculate it.
bta
@bta: Of course it makes sense to solve those equations on paper by treating those the known variables as constants before one goes to write code.
Christian
+1  A: 

Using vectors can make the math around this seem a little simpler. Sylvester seems to be a promising implementation of vectors in JavaScript, but for the purpose of my example, I'll write my own vector functions. I'm also going to assume .x / .y are measured top/left corner.

// this is a "constant"  - representing 10px motion per "time unit"
var bulletSpeed = 10; 
// calculate the vector from our center to their center
var enemyVec = vec_sub(targetSprite.getCenter(), originSprite.getCenter());
// measure the "distance" the bullet will travel
var dist = vec_mag(enemyVec);
// adjust for target position based on the amount of "time units" to travel "dist"
// and the targets speed vector
enemyVec = vec_add(enemyVec, vec_mul(targetSprite.getSpeed(), dist/bulletSpeed));
// calculate trajectory of bullet
var bulletTrajectory = vec_mul(vec_normal(enemyVec), bulletSpeed);
// assign values
bulletSprite.speedX = bulletTrajectory.x;  
bulletSprite.speedY = bulletTrajectory.y;  

// functions used in the above example:

// getCenter and getSpeed return "vectors"
sprite.prototype.getCenter = function() { 
  return {
    x: this.x+(this.width/2), 
    y: this.y+(this.height/2) 
  }; 
};

sprite.prototype.getSpeed = function() { 
  return {
    x: this.speedX, 
    y: this.speedY 
  }; 
};

function vec_mag(vec) { // get the magnitude of the vector
  return Math.sqrt( vec.x * vec.x + vec.y * vec.y); 
 }
function vec_sub(a,b) { // subtract two vectors
  return { x: a.x-b.x, y: a.y-b.y };
}
function vec_add(a,b) { // add two vectors
  return { x: a.x + b.x, y: a.y + b.y };
}
function vec_mul(a,c) { // multiply a vector by a scalar
  return { x: a.x * c, y: a.y * c };
}
function vec_div(a,c) { // divide == multiply by 1/c
  return vec_mul(a, 1.0/c);
}
function vec_normal(a) { // normalize vector
  return vec_div(a, vec_mag(a)); 
}
gnarf
Works like a charm. (A note for others copying this, in one place you need to amend veg_mag into vec_mag.) I had to dabble around a bit with finding the right bulletSpeed value and then the bullets hit their moving targets spot on. Thanks!!
Philipp Lenssen
@Philipp - Thanks, fixed that typo.
gnarf
+3  A: 
belisarius