我写了一个瞄准子程序xtank http://quozl.netrek.org/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
then t1
and 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)