Monday, March 30, 2015

c++ - How to lead a moving target from a moving shooter


I saw this question: Predicting enemy position in order to have an object lead its target. My situation is a little different though.



My target moves, and the shooter moves. Also, the shooter's velocity is added to the bullets' velocities, i.e. bullets fired while sliding to the right will have a greater velocity toward the right.


What I'm trying to do is to get the enemy to be able to determine where they need to shoot in order to hit the player. Using the linked SO solution, unless the player and enemy are stationary, the velocity difference will cause a miss. How can I prevent that?




Here is the solution presented from the stack overflow answer. It boils down to solving a quadratic equation of the form:


a * sqr(x) + b * x + c == 0

Note that by sqr I mean square, as opposed to square root. Use the following values:


a := sqr(target.velocityX) + sqr(target.velocityY) - sqr(projectile_speed)
b := 2 * (target.velocityX * (target.startX - cannon.X)
+ target.velocityY * (target.startY - cannon.Y))

c := sqr(target.startX - cannon.X) + sqr(target.startY - cannon.Y)

Now we can look at the discriminant to determine if we have a possible solution.


disc := sqr(b) - 4 * a * c

If the discriminant is less than 0, forget about hitting your target -- your projectile can never get there in time. Otherwise, look at two candidate solutions:


t1 := (-b + sqrt(disc)) / (2 * a)
t2 := (-b - sqrt(disc)) / (2 * a)

Note that if disc == 0 then t1 and t2 are equal.




Answer



Okay, let's put some sanity into this. I am afraid you are not making it easy at all, your code does not compile, is inconsistent with regards to variable names (playerVelocityX becomes playerXvelocity after a few lines? what is xVelocity?) and is too verbose. It is basically impossible to debug lest you put considerable effort into it.


So, here are the things to fix:


Bullet speed


The bullet speed must be 30, period. There is no need for the computations you are doing: the change of the frame of reference is precisely there to avoid the complexity. You only add the enemy's velocity after you found a solution, when you go back to the main reference frame.


Solution validity


You are not checking that the time solution is positive.


Numerous coding errors


You are testing time1 and time2 but always using time1 in the results.


You do playerXvelocity - yVelocity which is inconsistent.



You are doing / 2 * a instead of / (2.f * a). This is the worst error and it's why everything is going wrong.


You compute shootx and shooty as the final position of the bullet, whereas what you are looking for is the velocity of the bullet.


Fixed code


float const bulletSpeed = 30.f;
/* Relative player position */
float const dx = playerX - enemyX;
float const dy = playerY - enemyY;
/* Relative player velocity */
float const vx = playerVelocityX - enemyVelocityX;
float const vy = playerVelocityY - enemyVelocityY;


float const a = vx * vx + vy * vy - bulletSpeed * bulletSpeed;
float const b = 2.f * (vx * dx + vy * dy);
float const c = dx * dx + dy * dy;
float const disc = b * b - 4.f * a * c;

shouldShoot = false;

if (disc >= 0.f)
{

float t0 = (-b - std::sqrt(disc)) / (2.f * a);
float t1 = (-b + std::sqrt(disc)) / (2.f * a);
/* If t0 is negative, or t1 is a better solution, use t1 */
if (t0 < 0.f || (t1 < t0 && t1 >= 0.f))
t0 = t1;
if (t0 >= 0.f)
{
/* Compute the ship's heading */
shootx = vx + dx / t0;
shooty = vy + dy / t0;

heading = std::atan2(shooty, shootx) * RAD2DEGREE;
/* Compute the bullet's velocity by adding the enemy's velocity */
bulletVelocityX = shootx + enemyVelocityX;
bulletVelocityY = shooty + enemyVelocityY;

shouldShoot = true;
}
}

No comments:

Post a Comment

Simple past, Present perfect Past perfect

Can you tell me which form of the following sentences is the correct one please? Imagine two friends discussing the gym... I was in a good s...