在三维空间中向移动目标发射直线轨迹的弹丸

8

我已经在Google上搜寻了这个问题,但只找到了2D的解决方案或者对我不起作用的公式(发现了一个看起来很好的公式:http://www.ogre3d.org/forums/viewtopic.php?f=10&t=55796,但似乎不正确)。

我已经给出:

Vec3 cannonPos;
Vec3 targetPos;
Vec3 targetVelocityVec;
float bulletSpeed;

我所寻找的是时间t,使得

targetPos+t*targetVelocityVec

这是瞄准并射击大炮的交点。

我正在寻找一个简单、廉价的公式来计算t值(简单意味着不进行多余的向量空间转换等操作)。

谢谢!


考虑到弹丸沿直线轨迹运动,使得这个问题变得相当无聊 :-( - salva
1个回答

15
实际问题在于找出子弹可以与目标路径相交的空间位置。子弹速度恒定,因此在一定时间内,无论我们以何种方向发射它,它将行驶相同的距离。这意味着它在时间t之后的位置始终位于一个球体上。这是一个丑陋的2D示意图: 该球体可以用以下数学公式表示:
(x-x_b0)^2 + (y-y_b0)^2 + (z-z_b0)^2 = (bulletSpeed * t)^2      (eq 1)

x_b0、y_b0和z_b0表示火炮的位置。您可以使用问题中提供的方程式解出时间t,具体方法是解这个方程式:

targetPos+t*targetVelocityVec                                   (eq 2)

(eq 2)是一个向量方程,可以分解成三个单独的方程:

x = x_t0 + t * v_x
y = y_t0 + t * v_y
z = z_t0 + t * v_z

这三个公式可以插入到(eq 1)中:

(x_t0 + t * v_x - x_b0)^2 + (y_t0 + t * v_y - y_b0)^2 + (z_t0 + t * v_z - z_b0)^2 = (bulletSpeed * t)^2

这个方程只包含已知变量,可以解出 t。通过将二次子表达式的常数部分赋值给常数,我们可以简化计算:

c_1 = x_t0 - x_b0
c_2 = y_t0 - y_b0
c_3 = z_t0 - z_b0
(v_b = bulletSpeed)

(t * v_x + c_1)^2 + (t * v_y + c_2)^2 + (t * v_z + c_3)^2 = (v_b * t)^2

将其重新排列为标准二次方程:
(v_x^2+v_y^2+v_z^2-v_b^2)t^2 + 2*(v_x*c_1+v_y*c_2+v_z*c_3)t + (c_1^2+c_2^2+c_3^2) = 0

使用标准公式可以轻松解决这个问题。它可能有零、一个或两个解。零解(不计算复数解)意味着没有可能让子弹到达目标。当目标轨迹与球体的边缘相交时,很少会出现一个解的情况。两个解将是最常见的情况。负解意味着你无法击中目标,因为你需要向过去开火。这些都是你需要检查的条件。

当你解出方程后,可以通过将它放回(eq 2)来找到t的位置。伪代码如下:

# setup all needed variables
c_1 = x_t0 - x_b0
c_2 = y_t0 - y_b0
c_3 = z_t0 - z_b0
v_b = bulletSpeed
# ... and so on

a = v_x^2+v_y^2+v_z^2-v_b^2
b = 2*(v_x*c_1+v_y*c_2+v_z*c_3)
c = c_1^2+c_2^2+c_3^2

if b^2 < 4*a*c:
    # no real solutions
    raise error

p = -b/(2*a)
q = sqrt(b^2 - 4*a*c)/(2*a)

t1 = p-q
t2 = p+q

if t1 < 0 and t2 < 0:
    # no positive solutions, all possible trajectories are in the past
    raise error

# we want to hit it at the earliest possible time
if t1 > t2: t = t2
else: t = t1

# calculate point of collision
x = x_t0 + t * v_x
y = y_t0 + t * v_y
z = z_t0 + t * v_z

2
从技术上讲,不是“在过去放炮”,而是将炮弹射向过去的负t - Ben Jackson
如果t1 * t2> 0:如果两个解都是正数,则不应该在没有实际检查符号(或说明t1和t2为什么不能同时为正数)的情况下引发错误。我个人会使用if t1 <0 and t2 <0 - mokus
@mokus,我不知道当时在想什么。谢谢。 - Emil H
我猜你最近可能只是写了太多的根查找算法了 ;) - mokus
@EmilH - 我了解代码的一般流程,但是你能更好地解释示例代码中的方程式吗?例如数学上正在做什么以及为什么要这样做?我无法完全理解abcpq变量;虽然我认为我已经理解了其余的大部分内容,但是它并不是很容易跟随。 - CosmicGiant

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接