我正在开发一个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我不是最好的,但我慢慢学习它们。也许我能用聪明的东西做些什么?
答案 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资源。
同样,这只是一个近似值,行进时间越远,结果的精度就会越差。
注意:
我从头开始写了这个算法。
我不保证伪代码的正确性,但是该算法应该可以工作。