Trying to come up with a good way of calculating lead for aerial gunnery.
I have components that can be added to an aircraft to define a turret and it’s weapons.
From the parameters in this I calculate a view frustum.
In the update the component calls the target manager to get any hostile aircraft near it, and checks them against this view frustum.
This works well as you can see below.
The next stage is to calculate lead. Aim not at where the target is now, aim at where it will be.
My first thought was that it is simple.
Just store two positions and two velocities for each target as vector3
You can calculate a simple instantaneous acceleration from the change in velocity and a simple instantaneous velocity from the change in position.
Just translate these into a local coordinate system , calculate a velocity for the bullet and use gravity as the acceleration for the bullet.
Then use standard equations of motion S = Vt + 0.5at*t and solve for t.
t = (v1 - v2)/(0.5 * (a1 -a2))
Seemed simple and the code is trivial, except the result is a vector3
Time is 3 dimensional???
Okay I may have screwed this up…
Any ideas guys?
That can’t be right. If there’s t*t in the formula, the solution needs to contain a square root. And where did the S go? Not sure what you are doing here.
Can’t you just do this:
Assuming the bullet is much faster than the planes you can calculate the time to impact:
timeToImpact = distanceToTarget / bulletSpeed
Then plug that time into your formula and calculate where the target will be:
futureTargetPosition = currentTargetPosition + targetVelocity * timeToImpact + 0.5 * targetAcceleration * timeToImpact^2
Left and side of the equation, bullet
Right hand side of the equation, aircraft
Distance goes because we are solving for the TIME when bullet and plane will be the same distance from the origin
The square root goes when you divide both sides of the equation by T
I don’t want the distance to target, as the target will not be at that point when the bullet gets there.
Pointless shooting at where the plane was.
I thought about it more last night and I think I am on the correct track. At this point in the solver I don’t care where the convergence point is, just how long it will take to reach convergence.
So the 3 dimensional nature of the equation is meaningless. I am just solving for two changing velocities converging at a distance.
Ah I see, I understand your approach now. You started with this and solved for t:
v1*t + 0.5*a1*t*t = v2*t + 0.5*a2*t*t
You are missing the starting positions though, don’t you? I think it needs to be:
s1 + v1*t + 0.5*a1*t*t = s2 + v2*t + 0.5*a2*t*t
Which makes it more difficult to solve, as you are not getting rid of the t^2 anymore.
Why? There’s 4 unknown variables: time and bullet velocity.xyz, right? So you need all 3 dimensions, if you want to solve this.
In case this get’s too complicated I think you could use the method I proposed and run multiple iterations of it. It should converge to the same result. So futureTargetPosition from the first iteration would be the first rough approximation. You can then use this position to get a better estimate for distanceToTarget and calculate a more accurate futureTargetPosition. With every iteration you should get closer to the correct result.
Hmmm I was ignoring the target position as I had transformed it into the gunners local coordinate system, but this is not valid.
The relative position has to be taken into account…
I have found source code for a solution, but it is of little use …
Here is part of it, sadly I recognise the code and can read it
Well this is what I came up with
/// From target manager
/// Aircraft velocity
/// From BulletPropertiesComponent
/// Time step
/// Inverted local world
public static Vector3 GetAimDirection(AITarget target, Vector3 velocity, float muzzlevelocity, float dt, Matrix inWorld)
// First work out the time it will take to get to the targets current position
Vector3 relative_current_position = Vector3.Transform(target.Position, inWorld);
float bullet_velocity = velocity.Length() + muzzlevelocity;
float time1 = relative_current_position.Length() / bullet_velocity;
// work out instantaneous acceleration and velocity for target
Vector3 ivel = (target.Position - target.OldPosition) / dt;
Vector3 iaccel = (target.Velocity - target.OldVelocity) / dt;
// move instantaneous values into local world space
ivel = Vector3.Transform(ivel, inWorld);
iaccel = Vector3.Transform(iaccel, inWorld);
// move target to where it will be after time1
Vector3 predict1 = relative_current_position + (ivel * time1) + (0.5f * iaccel * time1 * time1);
// work out time a bullet would take to go there
float time2 = predict1.Length() / bullet_velocity;
// work out how far the bullet will go in time2
float dist1 = bullet_velocity * time2;
// how far is the bullet from the predicted position
float dist2 = predict1.Length() - dist1;
// adjust the time based on this distance to get our time estimation.
float time3 = time2 + (dist2 / bullet_velocity);
// work out bullet drop due to gravity
float drop = 0.5f * 9.81f * time3 * time3;
// Adjust time for bullet drop
time3 += drop / bullet_velocity;
// work out the target position using time3
Vector3 predict2 = relative_current_position + (ivel * time3) + (0.5f * iaccel * time3 * time3);
drop = 0.5f * 9.81f * time3 * time3;
// move aim point up to compensate
predict2.Y += drop;
ivel = (target.Position - target.OldPosition) / dt;
iaccel = (target.Velocity - target.OldVelocity) / dt;
Vector3 dp = target.Position + (ivel * time3) + (0.5f * iaccel * time3 * time3);
dp.Y += drop;
DebugLineDraw.DrawTarget(dp, Color.Red, 1);
Seems to be close to a workable solution
This is a 2d quadratic solution i keep.
Making it 3d is trivial just add in the z.
This finds the normalized directional vector a bullet that has a given speed must head in to intercept a target with a given velocity from it’s current position.
Note there exists the possibility that you cannot actually hit the target say its moving away from the bullet faster then the bullet, this accounts for that.
/// Given a object with a speed we will find what direction it must head (in normalized velocity form)
/// to intercept the given target with a given velocity from its current position
/// <returns>The normal vector the object or bullet must head in to intercept</returns>
public static Vector2 QuadricIntercept(Vector2 obj_position, float obj_speed, Vector2 target_position,Vector2 target_velocity)
float tvx = target_velocity.X;
float tvy = target_velocity.Y;
float pdx = target_position.X - obj_position.X;
float pdy = target_position.Y - obj_position.Y;
float d = pdx * pdx + pdy * pdy;
float s = (tvx * tvx + tvy * tvy) - obj_speed * obj_speed;
float q = (tvx * pdx + tvy * pdy);
float disc = (q * q) - s * d; // simplify get rid of the fluff
float disclen = (float)Math.Sqrt(disc);
float t = (-q + disclen) / s;
float t2 = (-q - disclen) / s;
if (t < 0.0f)
t = t2;
Vector2 aimpoint = Vector2.Zero;
if (t > 0.0f)
aimpoint.X = t * tvx + target_position.X;
aimpoint.Y = t * tvy + target_position.Y;
return aimpoint; // returns Vector2.Zero if no positive future time to fire exists
Yes it’s a lot simpler for fixed velocity.
I have decided to ignore the Magnus effect for this part of the code and instead do that in a separate pass for elite gunners.
Rookies will use the code above with an error term.
Average will use the above unadjusted.
Interesting problem, I would like a better solution, but everything I can think of involves expensive loops
You may apply any complex calculation velocity : ) for sure you can have your bullet last position and bullet current position at frame-update.
In may case, I just use Ray Intersection from bullet last-position and bullet current-position if hit the current target BBox/BSphere/Etc. ^_^y
If bullet life has reached without hitting the target mark it as free or remove it if you want from your bullet collection.
The actual collision code is trivial, simple ray cast for broadphase , then a more detailed raytrace for actual location.
The aim off angle for gunners is a really complex and interesting problem.
The things I am ignoring that I would like to include are …
- Bullet drag
- Magnus effect
- Altitude effects
Bullet drag is obvious, but quite expensive to code
Magnus effect is actually quite cheap to calculate, but makes the cheap prediction code above fall apart
Altitude effects drag and the magnus effect and should be included really
I would be very interested to see how other people have modelled this, but so far all I can find is that Fortran code from 1974 which has the nasty assumption that bullet and target are on a fixed plane
Ah i would say the forces like drag and even gravity in that context that cause a fall off can be considered from a different point of view such that for a distance the angle of aim is adjusted as a constant with the distance.
Think about it like in a real life example say in ww1 or ww2 for aa gunners or artillery.
Remember they didn’t have calculators or computers back then on the ground.
They had a simple table to adjust for these things like wind and distance.
That told them the amount of lead aim angle required.
They adjusted for a lead angle and height depending on distance wind and the enemy’s estimated target velocity basically they had to guess the intercept then adjust for wind drag and gravity.
Here once we have the direct intercept, the other two are trivial in comparison simple linear functions of drag and or gravity ect on the bullet over distance you could even just make a table to adjust the gun aim for those forces over distance.