6
votes

I'm developing a 3D spacegame where the camera is in a constant 2D (top down) state. 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 a t 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. 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.

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. 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. Theta is the current rotation (with respect to world coordinates) and the omega is the eulerAngle rotation around the y axis.

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. 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. 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. 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.

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. Maybe there's something clever I can do with those?

2
For reference, you can add images using the image upload button in the editor. AFAIK, you can't embed it until you have 10 rep, but any editors with > 10 rep can do that in an edit.Zoe
The easiest way is to make your bullets fast, and use lookat to make your bullets always be pointed at the target if you want them to hit for sure.Tyler S. Loeper
I agree with @TylerS.Loeper. you have to know how much time (t1) the bullet has until it hits. then you have to find out where (p1) it hits at t1 and aim for that, then t1 will probably change into t2 and causes p1 to change into p2 and so on.Bizhan
you will need to solve a dynamic system equation for that!Bizhan
@TylerS.Loeper Correct, which I briefly added that in my initial post. Ps+Vs * t + B*t = Pt + Vt * t. I just know that there is an angular component on the side of the target that I'm missing ugh. That's a good idea to take it to the math side too I will do that!Chris

2 Answers

0
votes

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. 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.

0
votes

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:

Algorithm (pseudo code):
Let f(t:float):Vector3 be a function that calculates the position of the target at time t
Let g(p:Vector3):float be a function that calculates how long the bullet would need to reach 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]. 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).
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).
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.