Я написал подпрограмму прицеливания для xtank некоторое время назад. Я постараюсь изложить, как я это сделал.
Отказ от ответственности: Возможно, я где-то здесь допустил одну или несколько глупых ошибок; Я просто пытаюсь восстановить рассуждения с моими ржавыми математическими навыками. Однако сначала я перейду к поиску, поскольку вместо математического класса это вопросы и ответы по программированию: -)
Как это сделать
Это сводится к решению квадратного уравнения вида:
a * sqr(x) + b * x + c == 0
Обратите внимание, что под sqr
я подразумеваю квадрат, а не квадратный корень. Используйте следующие значения:
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)
Теперь мы можем взглянуть на дискриминант, чтобы определить, есть ли у нас возможное решение.
disc := sqr(b) - 4 * a * c
Если дискриминант меньше 0, забудьте о попадании в цель - ваш снаряд никогда не попадет вовремя. В противном случае посмотрите на два возможных решения:
t1 := (-b + sqrt(disc)) / (2 * a)
t2 := (-b - sqrt(disc)) / (2 * a)
Обратите внимание, что если disc == 0
, то t1
и t2
равны.
Если нет других соображений, таких как препятствия, просто выберите меньшее положительное значение. (Отрицательные значения t потребуют запуска назад во времени, чтобы использовать!)
Подставьте выбранное значение t
обратно в уравнения положения цели, чтобы получить координаты ведущей точки, к которой вы должны стремиться:
aim.X := t * target.velocityX + target.startX
aim.Y := t * target.velocityY + target.startY
Выведение
В момент времени T снаряд должен быть (евклидовым) расстоянием от пушки, равным истекшему времени, умноженному на скорость снаряда. Это дает уравнение для круга, параметрическое по истекшему времени.
sqr(projectile.X - cannon.X) + sqr(projectile.Y - cannon.Y)
== sqr(t * projectile_speed)
Аналогично, в момент времени T цель перемещалась вдоль своего вектора на время, умноженное на ее скорость:
target.X == t * target.velocityX + target.startX
target.Y == t * target.velocityY + target.startY
Снаряд может поразить цель, когда его расстояние от орудия совпадает с расстоянием снаряда.
sqr(projectile.X - cannon.X) + sqr(projectile.Y - cannon.Y)
== sqr(target.X - cannon.X) + sqr(target.Y - cannon.Y)
Замечательно! Подстановка выражений для target.X и target.Y дает
sqr(projectile.X - cannon.X) + sqr(projectile.Y - cannon.Y)
== sqr((t * target.velocityX + target.startX) - cannon.X)
+ sqr((t * target.velocityY + target.startY) - cannon.Y)
Подстановка другой части уравнения дает это:
sqr(t * projectile_speed)
== sqr((t * target.velocityX + target.startX) - cannon.X)
+ sqr((t * target.velocityY + target.startY) - cannon.Y)
... вычитая sqr(t * projectile_speed)
с обеих сторон и переворачивая его вокруг:
sqr((t * target.velocityX) + (target.startX - cannon.X))
+ sqr((t * target.velocityY) + (target.startY - cannon.Y))
- sqr(t * projectile_speed)
== 0
... теперь разрешите результаты возведения в квадрат подвыражений ...
sqr(target.velocityX) * sqr(t)
+ 2 * t * target.velocityX * (target.startX - cannon.X)
+ sqr(target.startX - cannon.X)
+ sqr(target.velocityY) * sqr(t)
+ 2 * t * target.velocityY * (target.startY - cannon.Y)
+ sqr(target.startY - cannon.Y)
- sqr(projectile_speed) * sqr(t)
== 0
... и группировать похожие термины ...
sqr(target.velocityX) * sqr(t)
+ sqr(target.velocityY) * sqr(t)
- sqr(projectile_speed) * sqr(t)
+ 2 * t * target.velocityX * (target.startX - cannon.X)
+ 2 * t * target.velocityY * (target.startY - cannon.Y)
+ sqr(target.startX - cannon.X)
+ sqr(target.startY - cannon.Y)
== 0
... затем объедините их ...
(sqr(target.velocityX) + sqr(target.velocityY) - sqr(projectile_speed)) * sqr(t)
+ 2 * (target.velocityX * (target.startX - cannon.X)
+ target.velocityY * (target.startY - cannon.Y)) * t
+ sqr(target.startX - cannon.X) + sqr(target.startY - cannon.Y)
== 0
... дает стандартное квадратное уравнение в t . Нахождение положительных реальных нулей этого уравнения дает (ноль, один или два) возможных местоположения попадания, что можно сделать с помощью квадратной формулы:
a * sqr(x) + b * x + c == 0
x == (-b ± sqrt(sqr(b) - 4 * a * c)) / (2 * a)