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

时间:2018-05-24 18:27:40

标签: c# unity3d physics projection kinematics

我正在开发一个3D太空游戏,其中相机处于恒定的2D(自上而下)状态。我能够在以给定速度移动的目标上发射速度的射弹,并且每次击中它。大!那么如果那个目标在父母身边有一个角速度呢?我注意到如果目标有一个正在旋转的父对象,我的投影不正确,因为它没有考虑角速度。

我的初始代码是围绕以下假设构建的:

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,然后我有一个转塔脚本,以给定的角速度朝这个点旋转。如果炮塔说它在所有轴上观察到的点在1%以内,它会以速度射击子弹,如果目标没有改变其航向或速度,它会100%命中。

我开始在我的船只/小行星上添加组件,这些组件是父对象的子项,就像连接到炮塔本身为目标的船只的炮塔。如果船围绕轴旋转(例如Y轴)并且炮塔不在x = 0且z = 0,则我的投影不再起作用。我认为使用r * sin(theta + omega * t)作为X位置的角速度分量和使用Z位置的r * cos(theta + omega * t)可以起作用。 Theta是当前旋转(相对于世界坐标),omega是围绕y轴的eulerAngle旋转。

我很快就意识到这只适用于围绕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作为效果很好的欧米茄。最后,我只需要那个我应该射击的瞬间点,它是子弹的速度和投射距离的乘积,然后我的炮塔瞄准脚本将照顾其余部分。

我一直在寻找一个基于父母位置(旋转中心)的球面坐标系

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;

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

我想知道是否有一个更优雅的方法解决这个问题,或者是否有人能指出我正确的方向指导我帮助我思考这个问题? Quaternions和EulerAngles我不是最好的,但我慢慢学习它们。也许我能用聪明的东西做些什么?

2 个答案:

答案 0 :(得分:0)

尽管数学可能仍然很困难,但我怀疑您可以通过让“目标”计算其在本地空间中的未来位置来大大简化数学。然后让它将该位置称为其父对象,让它在本地空间中进行计算,依此类推,直到到达世界空间为止。一旦您在世界空间中拥有了未来的地位,就可以将炮塔瞄准那个目标。

例如,轨道飞船应该能够轻松计算其未来轨道。这是椭圆的方程式。然后它可以将该局部位置发送到其父体(行星),该父体也可能绕轨道运行并计算相对于自身的位置。然后,行星将把这个本地位置发送给它自己的父代(恒星),依此类推。直到进入世界空间。

您可以通过使子弹的行进时间恒定(灵活的速度)来进一步简化此数学运算,从而可以简化确定特定时间的将来位置的过程。根据游戏的规模,实际的速度差异可能不会那么大。

另一个想法:您可以及时地“模拟”目标对象,而不必通过蛮力进行所有计算。确保所有影响位置的代码都可以与实际更新循环分开运行。只需提前将时钟向前移动,即可查看其未来位置,而无需实际移动它。然后回到现在,向未来的位置开火。

答案 1 :(得分:0)

我建议大约解决这个问题。
如果您可以通过函数f(t)来描述目标的位置,则可以使用divide and conquer strategy来近似目标,如下所示:

算法(伪代码):
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]的值。它应该尽可能小,但要足够大,以使子弹始终能够击中目标。
您还可以使用其他中断条件,例如限制绝对错误(在finalTravelTime处,子弹到目标的距离)。
如果目标的移动速度比子弹快,则需要在上限循环上添加一个中断条件,否则它可能会变成无限循环。

为什么有用:
无需计算复杂的等式函数来确定影响时间,您可以使用相当简单的位置函数和此方法对其进行近似。
该算法与实际位置函数无关,因此只要可以计算出将来的位置,就可以处理各种敌人的动作。

缺点:
该函数多次计算f(t),对于复杂的f(t),这可能会占用大量CPU资源。
同样,这只是一个近似值,行进时间越远,结果的精度就会越差。

注意:
我从头开始写了这个算法。
我不保证伪代码的正确性,但是该算法应该可以工作。