2D игровой алгоритм для расчета необходимой скорости пули, чтобы поразить цель?

У меня есть довольно простая 2D-игра с птичьим видом, где спрайты башни защищаются от входящих движущихся спрайтов, стреляя в них пулей. Мой вопрос: Как рассчитать необходимую скорость пули для достижения движущейся цели при условии, что пуля всегда будет иметь одну и ту же определенную скорость?

Я использую JavaScript и имею эти переменные sprite (среди других): фея.Икс, спрайт.г, спрайт.ширина, спрайт.рост, спрайт.скорость (т. е. скорость), спрайт.скорый... так что я имейте объекты originSprite, targetSprite и bulletSprite, все с этими типами значений, и мне нужно установить правильные значения скорости bulletSprite.

вероятно, для того, чтобы это выглядело хорошо, пуля начнется с внешней стороны originSprite (или некоторого определенного радиуса, хотя я думаю, начиная с центра originSprite также будет работать), но его центр пули попытается попасть в центр targetSprite или около того. Обратите внимание, что в этом мире нет гравитации. (Возможно, я должен у моих переменных спрайтов есть угол и скорость, но сейчас я использую speedX и speedY...)

спасибо!

5 ответов


используя векторы может сделать математику вокруг этого кажется немного проще. Сильвестр кажется многообещающей реализацией векторов в JavaScript, но для моего примера я напишу свои собственные векторные функции. Я также собираюсь предположить .x / .y измеряются верхний / левый угол.

// 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)); 
}

рассматривать цели спрайт как прямую линию в 2-мерной комнате, где:

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

поскольку ваша пуля имеет постоянную скорость, вы также знаете:

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

тогда вы также можете рассчитать прямую линию для пули:

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

и вы знаете, что обе строки где-то интерсет:

A(time) = B(time)

тогда вам решать эти уравнения с заданными значениями и искать минимум для time.


некоторое физическое понимание

1) для цели, являющейся "точечным объектом"

Таким образом, вы должны решить векторное уравнение

позицияпуля [ time=t1 > t0 ] == позицияцель [ time=t1 > t0 ] -- (экв 1)

где позиции задаются движением (также вектор) уравнения

позицияобъект [ t] = позицияобъект [ t0 ] + скоростьобъект * (t-t0 )

теперь условие для пули, чтобы быть в состоянии достичь цели является то, что эквалайзер 1 имеет решения для x и y. Запишем уравнение для x:

Xпуля [ t0 ] + SpeedXпуля * (t - t0) = Xцель [ t0 ] + SpeedXцель * (t-t0 )

Итак, для времени столкновения у нас есть

(tстолкновение - t0) = (xцель [ t 0 ] - xпуля [ t0 ]) / (SpeedXпуля - SpeedXцель) -- (Eq 2)

как нам нужны решения с t > Т0, это означает, что для перехвата достаточно того, что>

знак ( xцель[ t0 ] - xпуля[ t0 ]) = Знак (SpeedXпуля - SpeedXцель ) -- (экв 3)

Which tells us the evident fact that if an object is moving faster than the other, and in the same direction, they will eventually collide.

из эквалайзера 2 Вы можете видеть, что для данного В speedxцельсуществуют бесконечные решения (как уже указывалось в других ответах) для t и SpeedXпуля, поэтому я думаю, что ваши спецификации не являются полными.

I guess (as stated in a commentary I made in another answer) thinking in a "tower defense" kind of game, that your bullets have a limited range.
Поэтому вам нужно еще одно ограничение:

Расстояние [ Должностьцель [ tстолкновение - t0 ] - позицияпуля [ t0 ] ]

Which still permits infinite solutions, but bounded by an upper value for the Collision time, given by the fact that the target may abandon the range.
Далее, расстояние задается

расстояние[v, u]= +Sqrt [(Vx-Ux)^2 + (Vx-Vy)^2]

Итак, Eq 4 становится,

(Xцель[tстолкновение - t0] - Xпуля[t0])2 + (Yцель[tстолкновение - t0] - Yпуля[t0])22 -- (экв 5)

обратите внимание, что { Xпуля[t0], Yпуля[t0} положение башни.

теперь, заменяя в эквалайзере 5 значения для целевой позиции:

(Xцель[t0] + SpeedXцель * (t-t0) - Xпуля[t0])2 + (Yцель[t0] + SpeedYцель * (t-t0) - Yпуля[t0])22 -- (экв 6)

вызов начальных расстояний:

Dxt0 = Xцель[t0] - Xпуля[t0]

и

Dyt0 = Yцель[t0] - Yпуля[t0]

уравнение 6 становится

(Dtx0 + SpeedXцель * (t-t0))2 + (Dty0 + SpeedYцель * (t-t0))22 -- (Экв 7)

это квадратное уравнение будет решена в Т-т0. Положительное решение даст нам самое большое время, необходимое для столкновения. После этого цель будет вне зоны досягаемости.

называют

скоростьцель2 = SpeedXцель2 + SpeedYцель2

и

H = Dtx0 * В speedxцель + Dty0 * SpeedYцель


TСтолкновение Макс = t0 - (H + / - Sqrt (BulletRange2 * скоростьцель2 - H2 ) ) / скоростьцель2

So you need to produce the collision BEFORE this time. The sign of the square root should be taken such as the time is greater than t0

After you select an appropriate flying time for your bullet from the visual 
effects point of view, you can calculate the SpeedX and SpeedY for the bullet 
from  

SpeedXпуля = ( Xцель [ t0 ] - Xпуля [ t0 ] ) / (tстолкновение - t0) + SpeedXцель

и

быстрыйпуля = (Yцель [ t0 ] - Yпуля [ t0 ] ) / (tстолкновение - t0 ) + Спиди!--13-->цель

2) для цели и башни быть "обширными объектами"

Теперь тривиально обобщать для случая, когда целью является круг радиуса R. То, что вы получаете, эквивалентно "расширенному диапазону" для пуль. Это расширение является Р.

Таким образом, заменив BulletRange на (BulletRange + R), вы получите новые уравнения для максимально допустимого времени столкновения.

Если вы также хотите рассмотреть радиус для пушек, те же соображения применяются, давая " двойной расширенный диапазон

NewBulletRange = BulletRange + Rцель + Rбашня

Неограниченный Диапазон Пуль

в случае, если вы решите, что некоторые специальные пули не должны иметь ограничений диапазона (и обнаружения), все еще есть ограничение границы экрана. Но с этим немного сложнее справиться. Если вам нужен такой снаряд, оставьте комментарий, и я попытаюсь сделать математику.


вычислить расстояние между стрелком и целью:dist = sqrt((xt - xs)^2 + (yt - ys)^2)
Разделите расстояния x и y на указанные выше:nx = (xt - xs)/dist; ny = (yt - ys)/dist; (нормализация вектора)
Умножить результат на коэффициент, чтобы получить N пикселей в единицу времени, т. е.. скорость в каждом направлении. Он должен давать постоянную скорость в нужном направлении.


Я предполагаю, что цель будет двигаться по прямой с постоянной скоростью.

Если оба направления и скорость пули являются переменными (т. е. вы пытаетесь вычислить speedX и speedY для пули), существует бесконечно много решений.

Если вы задаете фиксированное направление, вы просто пересекаете две линии пули и цели. От расстояния между текущей точкой цели и точкой пересечения (и скорость цели) вы можете рассчитать время, которое цель займет, чтобы достичь этой точки пересечения.

по расстоянию между началом пули и точкой пересечения (и ранее рассчитанным временем) можно рассчитать скорость пули.