Calculating Lead For Projectiles

From Unify Community Wiki
(Difference between revisions)
Jump to: navigation, search
m (Fixed external link)
(Added my version)
Line 83: Line 83:
  
 
</csharp>
 
</csharp>
 +
 +
== Alternate version ==
 +
Author: [[User:Danielbrauer|Danielbrauer]]
 +
 +
This is the intercept code I use, based on solving the same first order (velocity, no acceleration) version of the problem. It properly handles the situation where the target's relative velocity and the shot velocity are equal.
 +
 +
<csharp>//first-order intercept using absolute target position
 +
public static Vector3 FirstOrderIntercept( Vector3 shooterPosition,
 +
Vector3 shooterVelocity,
 +
float shotSpeed,
 +
Vector3 targetPosition,
 +
Vector3 targetVelocity) {
 +
Vector3 targetRelativeVelocity = targetVelocity - shooterVelocity;
 +
float t = FirstOrderInterceptTime( shotSpeed,
 +
targetPosition - shooterPosition,
 +
targetRelativeVelocity);
 +
return targetPosition + t*(targetRelativeVelocity);
 +
}
 +
//first-order intercept using relative target position
 +
public static float FirstOrderInterceptTime(float shotSpeed,
 +
Vector3 targetRelativePosition,
 +
Vector3 targetRelativeVelocity) {
 +
float a = targetRelativeVelocity.sqrMagnitude - shotSpeed*shotSpeed;
 +
//handle similar velocities
 +
if (Mathf.Abs(a) < 0.001f) {
 +
float t = -targetRelativePosition.sqrMagnitude
 +
/(2f*Vector3.Dot( targetRelativeVelocity,
 +
targetRelativePosition));
 +
return Mathf.Max(t, 0f); //don't shoot back in time
 +
}
 +
 +
float b = 2f*Vector3.Dot(targetRelativeVelocity, targetRelativePosition),
 +
c = targetRelativePosition.sqrMagnitude,
 +
determinant = b*b - 4f*a*c;
 +
 +
if (determinant > 0f) { //determinant > 0; two intercept paths (most common)
 +
float t1 = (-b + Mathf.Sqrt(determinant))/(2f*a),
 +
t2 = (-b - Mathf.Sqrt(determinant))/(2f*a);
 +
if (t1 > 0f) {
 +
if (t2 > 0f)
 +
return Mathf.Min(t1, t2); //both are positive
 +
else
 +
return t1; //only t1 is positive
 +
} else
 +
return Mathf.Max(t2, 0f); //don't shoot back in time
 +
} else if (determinant < 0f) //determinant < 0; no intercept path
 +
return 0f;
 +
else //determinant = 0; one intercept path, pretty much never happens
 +
return Mathf.Max(-b/(2f*a), 0f); //don't shoot back in time
 +
}</csharp>

Revision as of 14:34, 8 December 2008

Since this is a snippet this isn't a cut and paste job. It's meant as a pointer so you can integrate this easily into your own code. All the examples are in C#.

These script fragments allow you to add some lead ahead of the target allowing projectiles/missiles to collide with the target - A quadratic rather than an iterative solution.

Aiming Code

<csharp>

// how fast the projectile will go (this might be the magnitude of targetQ also) float projectileSpeed = 40;

// aliases for where we are Vector3 selfPosition = transform.position; Vector3 targetPosition = target.transform.position;

// aliases for where we are going Vector3 targetQ = (target.rigidbody!=null) ? target.rigidbody.velocity : Vector3.zero; Vector3 selfQ = (rigidbody!=null) ? rigidbody.velocity : Vector3.zero;

// assuming that the projectile moves, and the target is moving add some lead to the target if(targetQ.sqrMagnitude !=0 && projectileSpeed > 0)

 targetPosition = targetPosition + targetQ * relativeTimeToTarget(selfPosition, selfQ, targetPosition, targetQ, projectileSpeed);

// from here on in you assume that by the time your projectile collides with // the target, the target will be at the location stored in targetPosition!

// ... your tracking/movement code here </csharp>


Calculating the time to target

Here's the rocket science bit. But actually it isn't. I shan't bore you with the details, but a solution to the same problem is here which takes you through a thorough description of the problem, but solved in a rather more complex manner.


<csharp>

// if we're moving, and they're moving we need to and origin isn't at 0,0,0 we need to use the difference // of "this" and "target" position/velocities

public static float relativeTimeToTarget(

   Vector3 originPosn, Vector3 originVel,
   Vector3 targetPosn, Vector3 targetVel, float pVel)

{

 Vector3 diffPosn = originPosn - targetPosn;
 Vector3 diffVel  = originVel  - targetVel ;
 if(targetVel.sqrMagnitude == 0)
    diffVel =originVel;
 return timeToTarget(diffPosn, diffVel,pVel);

}

// This is the meat of the code - the quadratic solver // and to think you thought this was rocket science! Solved by high school maths! // thanks to Ed (idevgames) for the original idea, and JohnJ of JJFFE for fixing my lame attempt at 3difying it public static float timeToTarget(Vector3 vTargetPosn, Vector3 vTargetVelocity, float projectileVelocity) {

 float a = Vector3.Dot(vTargetVelocity,vTargetVelocity) - (projectileVelocity*projectileVelocity);
 float b = 2*Vector3.Dot(vTargetPosn, vTargetVelocity);
 float c = Vector3.Dot(vTargetPosn,vTargetPosn);
 float d = b*b - 4*a*c;
 float t = 0;
 float u = 0;
 float tt = 0;
 float tu = 0;
 float r = 1; // add one second of lead if we're unsolvable
 if (d >= 0)
 {
   tt = (-b + Mathf.Sqrt(d)) / (2*a);
   tu = (-b - Mathf.Sqrt(d)) / (2*a);
   // This portion picks the smallest nonnegative root.
   t = (tt < 0) ? System.Single.PositiveInfinity : tt;
   u = (tu < 0) ? System.Single.PositiveInfinity : tu;
   r = Mathf.Min(t,u); 
 }
 return r;

}


</csharp>

Alternate version

Author: Danielbrauer

This is the intercept code I use, based on solving the same first order (velocity, no acceleration) version of the problem. It properly handles the situation where the target's relative velocity and the shot velocity are equal.

<csharp>//first-order intercept using absolute target position public static Vector3 FirstOrderIntercept( Vector3 shooterPosition, Vector3 shooterVelocity, float shotSpeed, Vector3 targetPosition, Vector3 targetVelocity) { Vector3 targetRelativeVelocity = targetVelocity - shooterVelocity; float t = FirstOrderInterceptTime( shotSpeed, targetPosition - shooterPosition, targetRelativeVelocity); return targetPosition + t*(targetRelativeVelocity); } //first-order intercept using relative target position public static float FirstOrderInterceptTime(float shotSpeed, Vector3 targetRelativePosition, Vector3 targetRelativeVelocity) { float a = targetRelativeVelocity.sqrMagnitude - shotSpeed*shotSpeed; //handle similar velocities if (Mathf.Abs(a) < 0.001f) { float t = -targetRelativePosition.sqrMagnitude /(2f*Vector3.Dot( targetRelativeVelocity, targetRelativePosition)); return Mathf.Max(t, 0f); //don't shoot back in time }

float b = 2f*Vector3.Dot(targetRelativeVelocity, targetRelativePosition), c = targetRelativePosition.sqrMagnitude, determinant = b*b - 4f*a*c;

if (determinant > 0f) { //determinant > 0; two intercept paths (most common) float t1 = (-b + Mathf.Sqrt(determinant))/(2f*a), t2 = (-b - Mathf.Sqrt(determinant))/(2f*a); if (t1 > 0f) { if (t2 > 0f) return Mathf.Min(t1, t2); //both are positive else return t1; //only t1 is positive } else return Mathf.Max(t2, 0f); //don't shoot back in time } else if (determinant < 0f) //determinant < 0; no intercept path return 0f; else //determinant = 0; one intercept path, pretty much never happens return Mathf.Max(-b/(2f*a), 0f); //don't shoot back in time }</csharp>

Personal tools
Namespaces

Variants
Actions
Navigation
Extras
Toolbox