简体   繁体   English

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

[英]Unity C# Firing a projectile at a target rotating around moving axis

I'm developing a 3D spacegame where the camera is in a constant 2D (top down) state. 我正在开发一个3D太空游戏,其中相机处于恒定的2D(自上而下)状态。 I am able to fire a projectile of speed (s) at a target moving at a given velocity and hit it every time. 我能够在以给定速度移动的目标上发射速度的射弹,并且每次击中它。 Great! 大! Okay so what if that target has an angular velocity around a parent? 那么如果那个目标在父母身边有一个角速度呢? I noticed that if the target has a parent object that is rotating, my projection isn't correct since it doesn't account for the angular velocity. 我注意到如果目标有一个旋转的父对象,我的投影不正确,因为它没有考虑角速度。

My initial code was built around the assumption that: 我的初始代码是围绕以下假设构建的:

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

I assume that the shooter is stationary (or potentially moving) and needs to fire a bullet with a constant magnitude. 我认为射手是静止的(或可能是移动的)并需要发射一个恒定大小的子弹。

I simplify the above to this 我将上述内容简化为此

Delta_Position = Position_target - Position_shooter
Delta_Velocity = Velocity_target - Velocity_shooter

Delta_Position + Delta_Velocity * t = BulletSpeed * t

Squaring both sides I come to a quadratic equation where I can solve for t given determinant outcomes or zeros. 平方双方我得到一个二次方程式,我可以求解给定的行列式结果或零。 This works perfect. 这很完美。 I return at value and then project the target's position and current velocity out to that t, and then I have turret scripts that rotate at a given angular velocity towards that point. 我返回值,然后将目标的位置和当前速度投射到那个t,然后我有炮塔脚本,以给定的角速度朝着该点旋转。 If the turret says its looking at that point within 1% on all axis, it fires the bullet at speed(s) and its a 100% hit if the target doesn't alter its course or velocity. 如果炮塔说它在所有轴上观察到的点在1%以内,它会以速度射击子弹,如果目标没有改变其航向或速度,它会100%命中。

I started adding components on my ships / asteroids that were a child of the parent object, like a turret attached to a ship where the turret itself is a target. 我开始在我的船只/小行星上添加组件,这些组件是父对象的子项,就像连接到炮塔本身为目标的船只的炮塔。 If the ship is rotating around an axis (for example Y axis) and the turret is not at x=0 and z=0 my projection no longer works. 如果船围绕轴旋转(例如Y轴)并且炮塔不在x = 0且z = 0,则我的投影不再起作用。 I thought that using r * sin ( theta + omega * t) as the angular velocity component for the X position and r * cos ( theta + omega * t) for the Z position could work. 我认为使用r * sin(theta + omega * t)作为X位置的角速度分量和使用Z位置的r * cos(theta + omega * t)可以起作用。 Theta is the current rotation (with respect to world coordinates) and the omega is the eulerAngle rotation around the y axis. Theta是当前旋转(相对于世界坐标),omega是围绕y轴的eulerAngle旋转。

I've quickly realized this only works with rotating around the y axis, and I can't put the sin into a quadratic equation because I can't extract the t from it so I can't really project this appropriately. 我很快就意识到这只适用于围绕y轴旋转,我不能将sin置于二次方程式中,因为我无法从中提取t因此我无法真正适当地投影。 I tried using hyperbolics but it was the same situation. 我尝试使用双曲线,但情况相同。 I can create an arbitrary t, let's say t=2, and calculate where the object will be in 2 seconds. 我可以创建一个任意的t,假设t = 2,并计算对象在2秒内的位置。 But I am struggling to find a way to implement the bullet speed projection with this. 但我正在努力寻找一种方法来实现子弹速度投射。

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)

From here I have been spinning my wheels endlessly trying to figure out a workable solution for this. 从这里开始,我一直在不停地旋转我的车轮,试图找到一个可行的解决方案。 I am using the eulerAngle.y for the omega which works well. 我正在使用eulerAngle.y作为效果很好的欧米茄。 Ultimately I just need that instantaneous point in space that I should fire at which is a product of the speed of the bullet and the distance of the projection, and then my turrets aiming scripts will take care of the rest. 最后,我只需要在空间中我需要射击的瞬间点,它是子弹速度和投影距离的乘积,然后我的炮塔瞄准脚本将照顾其余部分。

I have been looking at a spherical coordinate system based around the parents position (the center of the rotation) 我一直在寻找基于父母位置(旋转中心)的球面坐标系

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;

I can calculate the position of the object given these angles ... but I am struggling to turn this into something I can use with the omega * t (angular velocity) approach. 我可以根据这些角度计算物体的位置......但我正在努力将其转化为可以与omega * t(角速度)方法一起使用的东西。

I am wondering if there is a more elegant approach to this problem, or if someone can point me in the right direction of a formula to help me think this through? 我想知道是否有一个更优雅的方法解决这个问题,或者是否有人可以指出我正确的方向指导我帮助我思考这个问题? I am not the best with Quaternions and EulerAngles but I am learning them slowly. Quaternions和EulerAngles我不是最好的,但我慢慢学习它们。 Maybe there's something clever I can do with those? 也许我能用那些聪明的东西做些什么?

Although the math is likely still tough, I suspect you can simplify the math substantially by having the "target" calculate its future position in local space. 虽然数学可能仍然很难,但我怀疑你可以通过让“目标”计算其在当地空间的未来位置来大大简化数学。 And then having it call that location to its parent, have that calculate it in local space, and so on up the hierarchy until you reach world space. 然后让它将该位置调用其父级,让它在本地空间中计算,依此类推,直到达到世界空间。 Once you have its future position in world space you can aim your turret at that target. 一旦你拥有了未来在世界空间中的位置,你就可以将你的炮塔对准那个目标。

For example an orbiting ship should be able to calculate its future orbit easily. 例如,轨道船应该能够轻松地计算其未来轨道。 This is an equation for an ellipse. 这是椭圆的方程。 Which can then send that local position to its parent (planet) which is presumably also orbiting and calculate that position relative to itself. 然后,可以将该本地位置发送到其父级(行星),该父级(行星)可能也在轨道运行,并相对于自身计算该位置。 The planet will then send this local position to its own parent (Star) and so on. 然后行星将这个本地位置发送给它自己的父母(Star),依此类推。 Until you get to world space. 直到你到达世界空间。

You can further simplify this math by making the bullet's travel time constant (flexible speed), so you can simplify figuring out the future position at a specific time. 您可以通过使子弹的行程时间保持恒定(灵活的速度)来进一步简化此数学运算,这样您就可以简化在特定时间确定未来的位置。 Depending on the scale of your game, the actual difference in speed might not be that different. 根据游戏规模的不同,速度的实际差异可能不同。

Another idea: Instead of doing all the calculations from brute force, you could "simulate" the target object forward in time. 另一个想法:您可以及时“模拟”目标对象,而不是通过强力进行所有计算。 Make sure all the code that affects is position can be run separate from your actual update loop. 确保影响位置的所有代码都可以与实际更新循环分开运行。 Simply advance the clock way ahead, and see its future position without actually moving it. 简单地推进时钟前进,并在不实际移动它的情况下查看其未来位置。 Then go back to the present and fire the gun at its future position. 然后回到现在,将枪射向未来的位置。

I suggest to solve this problem approximately. 我建议大约解决这个问题。
If you can describe the position of your target by a function over time, f(t), then you can approximate it using an divide and conquer strategy like this: 如果你可以用函数描述你的目标在一段时间内的位置f(t),那么你可以使用这样的分而治之策略来近似它:

Algorithm (pseudo code): 算法(伪代码):
Let f(t:float):Vector3 be a function that calculates the position of the target at time t f(t:float):Vector3是计算目标在时间t的位置的函数
Let g(p:Vector3):float be a function that calculates how long the bullet would need to reach p 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...

You need to experiment with the value for [precision]. 您需要尝试[precision]的值。 It should be as small as possible, but large enough for the bullet to always hit the target. 它应该尽可能小,但足够大,以便子弹始终击中目标。
You can also use another break condition, like restricting the absolute error (distance of the bullet to the target at the finalTravelTime). 您还可以使用另一个中断条件,例如限制绝对误差(在finalTravelTime处子弹到目标的距离)。
In case that the target can travel faster than the bullet, you need to add a break condition on the upper bounds loop, otherwise it can become an infinite loop. 如果目标的行进速度比子弹快,则需要在上限循环中添加中断条件,否则它可能成为无限循环。

Why this is useful: 为什么这很有用:
Instead of calculating a complex equality function to determine the time of impact, you can approximate it with a rather simple position function and this method. 不是计算复杂的相等函数来确定影响的时间,而是可以使用相当简单的位置函数和此方法来近似它。
This algorithm is independent of the actual position function, thus works with various enemy movements, as long as the future position can be calculated. 该算法独立于实际位置函数,因此只要可以计算未来位置,就可以处理各种敌人的运动。

Downsides: 缺点:
This function calculates f(t) many times, this can be CPU intensive for a complex f(t). 此函数多次计算f(t),对于复数f(t),这可能是CPU密集型的。
Also it is only an approximation, where the precision of the result gets worse the further the travel time is. 此外,它只是一种近似值,结果的精度越高,行程时间越长。

Note: 注意:
I wrote this algorithm from the top of my head. 我从头顶编写了这个算法。
I don't guarantee the correctness of the pseudo code, but the algorithm should work. 我不保证伪代码的正确性,但算法应该有效。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM