Unity C# 在移动轴周围旋转的目标上发射抛射物

6
我是一名有用的助手,可以为您进行翻译。以下是需要翻译的内容:

我正在开发一个3D太空游戏,其中相机处于恒定的2D(俯视)状态。我能够以速度(s)向移动的目标发射抛射物并每次都能击中它。很好!那么如果该目标围绕父对象具有角速度呢?我注意到,如果目标具有旋转的父对象,我的投影就不正确,因为它没有考虑到角速度。

我的初始代码是建立在这样一个假设上的:

Position_target + Velocity_target * t = Position_shooter + Velocity_shooter * t + Bulletspeed * t

我假设射手是静止的(或潜在移动的),需要发射一颗具有恒定速度的子弹。
我将上述简化为以下内容。
Delta_Position = Position_target - Position_shooter
Delta_Velocity = Velocity_target - Velocity_shooter

Delta_Position + Delta_Velocity * t = BulletSpeed * t

平方两边后,我得到一个二次方程,在这个方程中,我可以通过计算行列式的结果或零点来解出t。这很完美。我返回一个t值,然后将目标位置和当前速度投射到那个t上,然后我有炮塔脚本,可以以给定的角速度朝着那个点旋转。如果炮塔在所有轴上看向该点的偏差小于1%,它就会以速度(s)发射子弹,如果目标不改变其航向或速度,就会100%命中。
我开始在我的飞船/小行星上添加组件,这些组件是父对象的子级,例如连接到飞船上的炮塔,其中炮塔本身是一个目标。如果飞船围绕一个轴旋转(例如Y轴),而炮塔不在x=0和z=0处,我的投影就不再起作用了。我认为使用r * sin(theta + omega * t)作为X位置的角速度分量,使用r * cos(theta + omega * t)作为Z位置的角速度分量可能有效。Theta是当前旋转(相对于世界坐标系),omega是绕y轴的欧拉角旋转。
我很快意识到这仅适用于绕y轴旋转,并且我无法将sin放入二次方程中,因为我无法从中提取t,所以我无法适当地进行投影。我尝试使用双曲线函数,但情况是一样的。我可以创建一个任意的t,比如t=2,并计算物体在2秒后的位置。但是我正在努力找到一种实现子弹速度投射的方法。
Position_targetparent + Velocity_targetparent * t + [ANGULAR VELOCITY COMPONENT] = Position_shooter + Velocity_shooter * t + Bulletspeed * t

Delta_Position_X + Delta_Velocity_X * t + S * t = r * sin (theta + Omegay * t)
Delta_Position_Z + Delta_Velocity_Z * t + S * t = r * cos (theta + Omegay * t)

我一直在不停地努力寻找一个可行的解决方案。我正在使用eulerAngle.y作为omega,这个方法效果很好。最终,我只需要那个瞬时点的位置,这个位置是子弹速度和投射距离的乘积,然后我的炮塔瞄准脚本就会处理剩下的事情。

我一直在研究以父对象位置(旋转中心)为基础的球面坐标系。

Vector3 deltaPosition = target.transform.position - target.transform.root.position;
r = deltaPosition .magnitude;
float theta = Mathf.Acos(deltaPosition.z / r);
float phi = Mathf.Atan2(deltaPosition.y,deltaPosition.x);

float xPos = r * Mathf.Sin(theta) * Mathf.Cos(phi)
float yPos = r * Mathf.Sin(theta) * Mathf.Sin(phi)
float zPos = r * Mathf.Cos(theta)

Vector3 currentRotation = transform.root.gameObject.transform.rotation.eulerAngles * Mathf.Deg2Rad;
Vector3 angularVelocity = transform.root.gameObject.GetComponent<Rigidbody>().angularVelocity;

我可以根据这些角度计算物体的位置...但是我很难将其转化为可以与ω * t(角速度)方法一起使用的东西。

我想知道是否有一种更优雅的方法来解决这个问题,或者是否有人可以给我指引一个公式来帮助我思考?我对四元数和欧拉角不是很熟悉,但我正在慢慢学习它们。也许我可以用它们做点聪明的事情?


1
最简单的方法是让你的子弹快速移动,并使用lookat使你的子弹始终指向目标,以确保命中目标。 - Tyler S. Loeper
1
我同意@TylerS.Loeper的观点。你必须知道子弹还有多少时间(t1)才能击中目标。然后你必须找出在t1时它击中的位置(p1),并瞄准那个位置,然后t1可能会变成t2,导致p1变成p2等等。 - Bizhan
1
你需要解决一个动态系统方程! - Bizhan
你可能可以用导数来解决这个问题。但是要记住,由于炮塔随着时间缓慢旋转以便移动到射击位置(而不是瞬间移动),所以它会变得更加复杂。这真的很像天网。 - Tyler S. Loeper
1
@TylerS.Loeper 正确,我在我的初始帖子中简要添加了这一点。Ps+Vs * t + B*t = Pt + Vt * t。我只知道目标侧面有一个角度分量,我错过了。这是一个很好的想法,也要将其转化为数学方面,我会这样做! - Chris
显示剩余11条评论
2个回答

0
尽管数学可能仍然很难,但我认为您可以通过使“目标”在本地空间中计算其未来位置来大大简化数学。然后让它调用其父位置,在本地空间中进行计算,直到达到世界空间。一旦您在世界空间中具有其未来位置,就可以瞄准炮塔攻击该目标。
例如,绕行的飞船应该能够轻松地计算出其未来轨道。这是一个椭圆方程。然后可以将该本地位置发送到其父级(行星),并相对于自身计算该位置。然后,该行星将将此本地位置发送到其自己的父级(星球)等。直到到达世界空间。
通过使子弹的飞行时间恒定(速度灵活),您可以进一步简化此数学问题,因此可以简化在特定时间计算未来位置。根据您的游戏规模,实际速度的差异可能并不那么大。
另一个想法:您可以“模拟”目标对象在未来时间内前进,而不是从头开始进行所有计算。确保影响其位置的所有代码都可以从实际的更新循环中分离运行。只需提前时钟,查看其未来位置,而不实际移动它。然后返回到现在,朝着其未来位置开火。

0

我建议用近似方法解决这个问题。
如果您可以通过时间函数f(t)描述目标的位置,则可以使用分而治之策略来近似它,如下所示:

算法(伪代码):
f(t:float):Vector3是一个计算目标在时间t时位置的函数
g(p:Vector3):float是一个计算子弹到达p所需时间的函数

float begin = 0    // Lower bound of bullet travel time to hit the target
float end = g(target.position)    // Upper bound

// Find an upper bound so that the bullet can hit the target between begin and end time
while g(f(end)) > end:
    begin = end
    end = end * 2    // Exponential growth for fast convergence
    // Add break condition in case the target can't be hit (faster than bullet)
end    

// Narrow down the possible aim target, doubling the precision in every step
for i = 1...[precision]:
    float center = begin + (end - begin) / 2
    float travelTime = g(f(center))

    if travelTime > center:    // Bullet can't reach target
        begin = center
    else    // Bullet overtook target
        end = center
    end
end

float finalTravelTime = begin + (end - begin) / 2
Vector3 aimPosition = f(finalTravelTime)    // You should aim here...

你需要尝试不同的[precision]值。它应该尽可能小,但足够大,使子弹始终能击中目标。
你也可以使用另一个中断条件,比如限制绝对误差(子弹在最终旅行时间时到目标的距离)。
如果目标可以比子弹更快地移动,你需要在上限循环中添加一个中断条件,否则它可能会变成无限循环。

为什么这很有用:
与计算复杂的相等函数以确定撞击时间相比,你可以用一个相当简单的位置函数和这种方法来近似计算。
该算法独立于实际的位置函数,因此适用于各种敌人的移动,只要未来的位置可以计算出来。

缺点:
该函数多次计算f(t),这对于复杂的f(t)可能会占用CPU资源。
而且它只是一个近似值,随着旅行时间的增加,结果的精度会变得越来越差。

注意:
我是凭空想出这个算法的。
我不能保证伪代码的正确性,但该算法应该有效。


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