I'm trying to predetermine if a projectile will hit a target (before the projectile has been fired). The projectile settings include an Initial Speed, Acceleration, Max Speed, and a Turn Rate. I'm only concerned about heading currently, but hoping any solution can also apply to pitch as well if I change my axis.
So far, this is what I've attempted:
- Calculate "time to target" using a direct distance and speed calculations
- I know this isn't perfect since the projectile isn't pointed at the target, but represents a "best case" scenario I thought
- Calculate the angle to target, using the projectile forward and the direction to the target
- Check if time to target * turn rate >= angle to target
The hope was that if the check was true, then the projectile could steer to the target successfully. But that's not what I'm experiencing... instead the angles I'm calculating seem to far exceed the actual projectile movement.
I'm working in Lua, using a vector library (but no matrices).
Here's the pertinent bit of the code I'm using, simplified a bit for readability:
function checkAim()
-- Get Distance to target
local distToTarget = getDistance(targetPos, fireLocation)
-- Get seconds to target (needs to consider acceleration)
-- solve for dist to max velocity
local accelTime, accelDist = timeToMaxSpeed(initialSpeed, maxSpeed, acceleration)
-- if distToMax < distToTarget, use time = (-b +- sqrt(b^2-4ac)) / 2a
if( accelDist > distToTarget ) then
timeToTarget = math.max(quadraticEquation(0.5*acceleration, initialSpeed, -distToTarget))
else
-- else solve for time to max velocity, solve for time at max to travel distToTarget - distToMax
-- and add time to max velocity + time at max
local remainingDist = distToTarget - accelDist
local remainingTime = remainingDist / maxSpeed
timeToTarget = accelTime + remainingTime
end
local headingDiff = math.deg(math.atan2(DotProduct(CrossProduct(gunFwd, dirToTarget), {x=0,y=1,z=0}), DotProduct(gunFwd,dirToTarget)))
if( math.abs(headingDiff) > headingTurnRate * timeToTarget ) then
aimInvalid = true
else
local pitchDiff = math.deg(math.atan2(DotProduct(CrossProduct(gunFwd, dirToTarget), {x=1,y=0,z=0}), DotProduct(gunFwd,dirToTarget)))
if( math.abs(pitchDiff) > pitchTurnRate * timeToTarget ) then
aimInvalid = true
end
end
end
function timeToMaxSpeed(initialSpeed, maxSpeed, acceleration)
local t = (maxSpeed - initialSpeed) / acceleration
local d = (initialSpeed * t) + (0.5 * acceleration * t * t)
return t, d
end
function quadraticEquation(a, b, c)
local fourAC = 4 * a * c
local twoA = 2 * a
local bSquared = b * b
local pos = (-b + math.sqrt(bSquared- fourAC)) / (twoA)
local neg = (-b - math.sqrt(bSquared- fourAC)) / (twoA)
return pos, neg
end