Does anyone know or have a link to a formula for solving the angle at which you should shoot a projectile with constant velocity in order to hit a target with constant velocity?
I only need to solve this in two dimensions.
Does anyone know or have a link to a formula for solving the angle at which you should shoot a projectile with constant velocity in order to hit a target with constant velocity?
I only need to solve this in two dimensions.
The simplest way is to:
Take the speed and location of the target.
Calculate the distance to the location the target will be at the time of impact.
Multiply the targetâs speed by the time and add to its current position.
Step 2 is the tricky part, because you donât know how long this distance will be until you know the point, but you canât find the point until you know the distance. The easiest solution is to guess - just use the distance to the current target position. This is usually âgood enoughâ. If itâs imperative to be deadly accurate, you can use that estimate to estimate again, closer and closer⌠ultimately, if youâve taken calculus, you might arrive at a method where you could find the limit of this guessing formula. Thatâs beyond my current skill level, though, and Iâm pretty good at math.
Just guess. ![]()
Look up the Pursuit steering behaviour for some code for what StarManta is talking about. It basically does the same thing to pick a point to move to.
-Jeremy
I was hoping for an exact calculation. Googling only dragged up a lot of high school math help pages talking about finding x-intercepts, and pages for actual missile guidance systems, which are a little more complicated.
This page might be useful: https://www.math.ubc.ca/~cass/courses/m308-05b/projects/knox/John%20P%20Knox%20Math%20308%20Project.htm#flightime
Thatâs about ballistics and drag, which get much more complicated because they introduce (negative) acceleration into the equation.
Anyway, I think Iâve almost worked it out. Once I do, Iâll post it on the wiki, because itâs probably a common problem.
Canât you just set acceleration to zero? That simplifies quite a bit.
This would depend heavily on wether youâre using the Physics-Engine of Unity, since you canât really know how their calculating the flight path of your projectile.
If you know the flight path of your projectile, you could do the following:
Add a 3rd dimension to your calculations: time. Now you should be able to make a formula for the flight path of your projectile in dependance on the time and the angle at which you fire.
This is what it looks like, if you disregard drag.
[ x, y, 0] + [ cos( alpha ) * vel, sin( alpha ) * vel, 1 ] * t + [ 0, -9.81*0.5, 0] t^2
The first summand represents the starting point, the second the direction and the velocity in which the projectile is initially fired. The third one represents gravity.
Your other object could be represented like this:
[x2, y2, 0] + [ cos( beta ) * vel2, sin( beta ) * vel2, 1 ] * t
So equate these two equations:
[ x, y, 0] + [ cos( alpha ) * vel, sin( alpha ) * vel, 1 ] * t + [ 0, -9.81*0.5, 1/t] t^2 = [x2, y2, 0] + [ cos( beta ) * vel2, sin( beta ) * vel2, 1 ] * t
Solve this linear system of euqations and you should get your angle, in which you have to fire.
Hope this helps.
Der Dude:
Let me guess what your skillset is ![]()
Way beyond me.
-Jeremy
+Color me impressed.
Why donât you copy the original trajectory and whotnot and then duplicate that for the intercepting projectile, adding adjustments for differences in the velocity and X Y.
If they are both at a constant velocity (whatever the âdifferencesâ) then you can get the ratio of their angular differences from this. Assuming the projectiles are not arcing.
Cheers,
Luis.
Iâll admit, this wasnât for a Unity project, which is why it was in 2D. Itâs in C, but thereâs nothing fancy in it. alienDumbShoot() just fires straight at one location from another. I realize itâs sort of hobo-ish to just drop a bunch of C on this forum, but Iâm too busy at the moment to do any better. Iâd like to do it properly in JS at some point. I can probably adapt it to three dimensions, as well.
void alienSmartShoot( double alienX,
double alienY,
double targetX,
double targetY,
double targetDX,
double targetDY) {
double a, b, c, t1, t2, determinant, sqrtDeterminant;
a = pow(targetDX, 2.0) + pow(targetDY, 2.0) - pow(ALIEN_PHOTON_SPEED, 2.0);
if (a == 0.0) {
return;
}
b = 2.0*(targetDX*(targetX - alienX) + targetDY*(targetY - alienY));
c = pow(targetX-alienX, 2.0) + pow(targetY-alienY, 2.0);
determinant = pow(b, 2.0) - 4.0*a*c;
if (determinant < 0.0) { //determinant < 0; no intercept path
return;
} else if (determinant == 0.0) { //determinant = 0; one intercept path
t1 = -b/(2.0*a);
if (t1 < 0.0) t1 = 0.0;
} else { //determinant > 0; two intercept paths
sqrtDeterminant = sqrt(determinant);
t1 = (-b+sqrtDeterminant)/(2.0*a);
t2 = (-b-sqrtDeterminant)/(2.0*a);
//choose shortest path
if (t1 > 0.0 t2 > 0.0) {
if (t2 < t1) t1 = t2;
} else {
if (t2 > t1) t1 = t2;
if (t1 < 0.0) t1 = 0.0;
}
}
//set collision location
targetX = targetDX*t1 + targetX;
targetY = targetDY*t1 + targetY;
//fire
alienDumbShoot(alienX, alienY, targetX, targetY);
}
After looking through the (surprisingly) very few posts found on this subject, I wanted to give it a shot. After scratching my head for awhile trying to figure out how to calculate the future position of the target I stumbled on to the fact that rigidbody.velocity is a Vector3.
I donât know what gave me the idea to try adding Target.rigidbody.velocity to Target.transform.position but I tried it, thinking it wouldnât work. After creating an empty game object to represent the new target position I noticed it did indeed work, and well too.
Of course being so simple, it didnât calculate distance and projectile speed. So a little math trial-and-error and presto!
var Target : GameObject;
var projectileSpeed = 500;
var turnSpeed = 5;
function FixedUpdate () {
if (Target) {
// actual distance to target
var distTarget = Vector3.Distance(Target.transform.position, transform.position);
// first calculation, using actual distance
velocityPosition1 = Target.transform.position + ( (Target.rigidbody.velocity*1.15) * (distTarget/projectileSpeed) );
velocityDist1 = Vector3.Distance(velocityPosition1, transform.position);
// second calc., using distance from first calc.
velocityPosition2 = Target.transform.position + ( (Target.rigidbody.velocity*1.15) * (velocityDist1/projectileSpeed) );
velocityDist2 = Vector3.Distance(velocityPosition2, transform.position);
// third calc., using distance from second calc.
TargetInterceptPosition = Target.transform.position + ( (Target.rigidbody.velocity*1.15) * (velocityDist2/projectileSpeed) );
// aim turret/ship at intercept position
rotationTarget = Quaternion.LookRotation((TargetInterceptPosition) - transform.position);
transform.rotation = Quaternion.Lerp(transform.rotation, rotationTarget, Time.deltaTime * turnSpeed);
}
else {
Target = GameObject.FindWithTag("ship");//or find closest object in seperate function, etc.
}
}
While this script is on an AI spaceship, which fires the projectile, I donât see any reason why it wouldnât work on a projectile, (just translate position.) other than it may get costly if there are many, many projectiles.
I tested this script running on 10 ships (with rigidbodies of their own) and 10 target ships. I didnât notice any performance loss over the old LookAt and shoot method I was using.
Itâs simple, and FAST! Did someone here say that it wasnât possible?
This uses 3 calculations, testing with 4 made no difference, in fact it was a bit off lol. Not sure why I had to multiply Target.rigidbody.velocity*1.15 but it seems to be the magical number.
I am excited to share this freely, so enjoy! If anyone would like to make a donation to my food fund for the effort I certainly wonât decline
and my name being mentioned on credits is appreciated as well!
Iâve posted something like this on the Unity Answers page. While what i have is not for the AI but for the Player. A visual Target Lead Indicator. Do you think this could be adapted to the Player for this purpose? What i have kind of works but iâm missing some element be it relative velocity, and or angular velocity, because my target indicator stays in the same place in front of the target relative to my distance. Iâm not sure how to incorporate them into the equation.
Yes it will work perfectly for a lead target indicator. When I was testing this I had 3 sphere meshes, red, yellow, and green that represented the the 3 calculationsâŚ
var Target : GameObject;
var projectileSpeed = 500;
private var Sphere1 : GameObject;
private var Sphere2 : GameObject;
private var Sphere3 : GameObject;
function Start () {
Sphere1 = GameObject.Find("Sphere1");
Sphere2 = GameObject.Find("Sphere2");
Sphere3 = GameObject.Find("Sphere3");
}
function FixedUpdate () {
if (Target) {
// actual distance to target
var distTarget = Vector3.Distance(Target.transform.position, transform.position);
// first calculation, using actual distance
velocityPosition1 = Target.transform.position + ( (Target.rigidbody.velocity*1.15) * (distTarget/projectileSpeed) );
velocityDist1 = Vector3.Distance(velocityPosition1, transform.position);
// second calc., using distance from first calc.
velocityPosition2 = Target.transform.position + ( (Target.rigidbody.velocity*1.15) * (velocityDist1/projectileSpeed) );
velocityDist2 = Vector3.Distance(velocityPosition2, transform.position);
// third calc., using distance from second calc.
TargetInterceptPosition = Target.transform.position + ( (Target.rigidbody.velocity*1.15) * (velocityDist2/projectileSpeed) );
// aim turret/ship at intercept position
}
raySphere1.transform.position = velocityPosition1;
raySphere2.transform.position = velocityPosition2;
raySphere3.transform.position = TargetInterceptPosition;
else {
Target = GameObject.FindWithTag("ship");//or find closest object in seperate function, etc.
}
}
Of course you donât actually need 3 spheres, just the last one. Rather than a sphere you could use a mesh shaped like an X or + and have it always look at the player and update the rotation to keep it from looking like an object and more like a HUD, etc.
The only thing I see that needs to be done is how you will handle selecting a target. Good luck and have fun!
Starcruiser,
This is perfect. Sorry itâs been a while, got busy doing other Unity stuff. I already had a targeting system so i just plugged in your code and presto. Iâm using a GUI for my target lead.
Thanks so much for this code!
var TLI : GUITexture;
function TargetLeadIndicator(){
// actual distance to target
var distTarget = Vector3.Distance(Target.transform.position, transform.position);
Debug.Log(distTarget);
// first calculation, using actual distance
velocityPosition1 = Target.transform.position + ( (Target.rigidbody.velocity*1.15) * (distTarget/projectileSpeed) );
velocityDist1 = Vector3.Distance(velocityPosition1, transform.position);
// second calc., using distance from first calc.
velocityPosition2 = Target.transform.position + ( (Target.rigidbody.velocity*1.15) * (velocityDist1/projectileSpeed) );
velocityDist2 = Vector3.Distance(velocityPosition2, transform.position);
// third calc., using distance from second calc.
TargetInterceptPosition = Target.transform.position + ( (Target.rigidbody.velocity*1.15) * (velocityDist2/projectileSpeed) );
// aim turret/ship at intercept position
var relPos : Vector3;
relPos = Target.transform.position - camera.main.transform.position;
if (Vector3.Dot(camera.main.transform.TransformDirection(Vector3.forward), relPos) > 0){
TLI.transform.position=camera.main.WorldToViewportPoint(TargetInterceptPosition);
}
}
Firstly, my first Unity game, Starcruiser, is VERY close to first release!
I have spent nearly 1000 hours, or more, over the course of 4 years on this game. I have worked another few hundred hours on a few other projects that are âhalfwayâ through completion, which has taught me a many great things! All content is %100 percent original, I have modeled, scripted, tweaked and tweaked and tweaked game objects, created textures, music and sounds, all me! ![]()
Now to my point, there is a part of Starcruiser with laser turrets, I applied this âtargeting leadâ script to my turrets and discovered it made them VERY dangerous
but they werenât dangerous enough
Now being shot at by this script revealed the mystery of the 1.15, they are more accurate without this number! (or using 1 instead)
Then it dawned on me that this could be used as a variable to simulate inaccuracy⌠so replacing âTarget.rigidbody.velocity1.15â with 'Target.rigidbody.velocityaccuracyâ and of course adding âvar accuracy = 1.0;â gives you this option.
Now seems to me that 1.0 is the most accurate, making it a higher number causes it to shot too far ahead, lower too far behind. One could go as far as to use a random number between say .9 and 1.1 to give it some variation. Or, use some other variable to adjust accuracy based on distance or angular velocityâŚ
Vector3.distance uses a square root. 3 times a frame for your 10 ships, is going to eat up a lot of juice.
You might want to use a coroutine to spread out the calculations over time. No real need to do it every physics frame, unless youâre writing an Aimbot! An update frequency for each ship might be a better inaccuracy metric in that regard.
Bumping this old thread because Iâm at this stage in my project.
I did a 2d version of this ages ago, and what I found is that if all you wanted was the correct angle to shoot, then the calculation is quite simple and the distance to target does not matter at all. Here is a little flash mockup that I did back then: leadtarget
If you click OK, youâll see some ships flying around that show it is dead accurate.
It hinges on the fact that you know the speed of the target and the bullet, and you know the angle from the line that connects the objects, to the direction of movement of the target. When you know these things, the angles are always the same regardless of distance. Distance just scales the size of the triangle.
In pseudocode:
deltaAngle = angleFromTargetToGun - angleOfTargetMotion
aimOffsetAngle = aSin(( bulletSpeed / targetSpeed )* Sin ( deltaAngle ))
angleToAim = angleFromGunToTarget + aimOffsetAngle;
Now, in my current project, I donât just want the correct angle, I want the exact point of impact for a HUD element, and this is where my simple solution above becomes not so simple anymore.
Extending the above, instead of solving for angleToAim, I have to solve for distanceTargetTravels, so that I can place the HUD element at target.position + target.velocity.normalized * distanceTargetTravels
using the deltaAngle and aimOffsetAngle calculated above, together with distanceToTarget and the sine rule a/sin(A) = b / sin(B), I get this:
distanceTargetTravels = distanceToTarget * Sin ( aimOffsetAngle ) / Sin ( 180 - aimOffsetAngle - deltaAngle )
thatâs 3 Sines and an arcSine, it works but I am sure it can be done much more efficiently, if only my trigonometry werenât so rusty.
edit: a note on accuracy, the trigger for my AI to actually fire is:
if (Vector3.Dot(transform.forward, angleToAim) > accuracy) //shoot
When accuracy is just a little less than 1.0 it is dead accurate, though lower accuracy means they spam a lot of shots which can be just as deadly.
Create a child transform that is slightly forward offset from the target and aim at that?