- Home /
target tracking with lead time
Hello all -
I am making a simple turret system that fires at a target as it flies by. I currently have the code set up so that the turret tracks the target, and fires at it whenever it detects a "hit."
The problem is, that when the target is moving in a straight line or something like that, my bullets will never actually hit the target. The turret fires, but the moment it fires and the projectiles are on their way, the target has already moved to a new position. Therefore unless the target turns in toward the turret or away or some such action, the projectiles will simply miss. As the target moves in a circle around the turret, the bullets will never hit. Ever. Thats just the nature of the beast :)
However, I want to add some sort of leading on the turret so that it can hit the target. Now, I know with random movement my odds of hitting it are the same whether I shoot at where it is or if I lead the target. However, even with random movement as I am going to be using it, there are still going to be situations where the target will be going in a straight line long enough for projectiles to hit it if they are aimed properly.
So my question is, what is the best way to "lead" my target so that providing there are no additional forces acting on the object, my bullets will hit?
My current idea, though I don't know how to implement this in code, is :
Have some sort of variable (float, maybe vector3), speed, that gives me the speed and direction of the target.
Then, have the turret take into account on any given frame the speed of the target, the distance of the target from the turret, and the projectile speed. With that information, I should be able to solve for how much "lead" space there needs to be. Then just take the current position of the target, add the "lead" space ahead of it where it is moving to, and fire there. In theory, if the target is then not acted upon by any forces, the bullets should hit. Any ideas on how this could be implemented?
Answer by skovacs1 · Nov 30, 2010 at 06:34 PM
I know I've answered a question identical to this a while back, but I can't seem to find it.
The idea is that you know how fast your target is moving in what direction and you aim at where they are going to be when your projectile reaches the distance that they are located away from you. targetPosition + targetDistance targetDirection TargetSpeed / projectileSpeed. This is not perfect, but sufficient for most purposes. A more complex approach will actually calculate the exact point at the distance that they will be at rather than using the current distance.
I'm not sure how your targets are being moved, but I'll assume you can figure out how to get that information.
Of course, simply calculating the point to look at won't be enough. If you look at that point and the target suddenly stops or changes direction, you're going to get some jerky movement. You'd best consider using some kind of rotation smoothing. Something like this may work for you:
var target : Transform;
//Get these using your target: //TargetDirection can have magnitude of targetSpeed //in which case you only need one variable for both of them. var targetDirection : Vector3; // Normalized direction. var targetSpeed : float; //World units.
var projectileSpeed : float = 200.0f; //World units var maxDegreeRotation : float = 30.0f; //var someFactor : float;
function Update() { if(!target) return; var leadDirection : Vector3 = target.position - transform.position; leadDirection += leadDirection.magnitude *targetDirection *targetSpeed /projectileSpeed; transform.rotation = Quaternion.RotateTowards(transform.rotation, Quaternion.LookRotation(leadDirection, transform.up), maxDegreeRotation); /or transform.rotation = Quaternion.Slerp(transform.rotation, Quaternion.LookRotation(leadDirection, transform.up), Time.deltaTime*someFactor); / }
If you wanted to add in things like inertial movement, you would have to maintain additional information and adjust your input parameters accordingly, something like: check if old target lead position is in the same direction as the current lead position and if not, subtract from the maxDegreeRotation while still rotating towards the old target lead until the maxDegreeRotation is <=0 at which point you'd flip the sign and set the old target lead to be the new target lead and then add to maxDegreeRotation up to some maximum.
If you want to constrain the rotations to only be within a certain number of degrees, you could do something to clamp in a circle like this:
var target : Transform;
//Get these using your target: //TargetDirection can have magnitude of targetSpeed //in which case you only need one variable for both of them. var targetDirection : Vector3; // Normalized direction. var targetSpeed : float; //World units.
var projectileSpeed : float = 200.0f; //World units var maxDegreeRotation : float = 30.0f; //var someFactor : float;
var maxRotationRange : float = 30.0f;
private var startDirection : Vector3 = Vector3.forward;
function Start() { startDirection = transform.forward; }
function Update() { if(!target) return; var leadDirection : Vector3 = target.position - transform.position; leadDirection += leadDirection.magnitude *targetDirection targetSpeed /projectileSpeed; leadDirection = Vector3.RotateTowards(startDirection, leadDirection, maxRotationRange Mathf.Deg2Rad, 0.0f); transform.rotation = Quaternion.RotateTowards(transform.rotation, Quaternion.LookRotation(leadDirection,transform.up), maxDegreeRotation); }
To clamp on set axes would be done a bit differently, calculating your rotation yourself and constraining the axes.
Awesome! Thank you so much. This is exactly what I was looking for, especially coupled with the fact you included some sort of rotational limits code which I was also looking for. And, bar some small compiling errors everything worked as expected right from the code you gave me. A quick question on usage. Is there a difference between using the rigidbodyVelocity as targetDirection and taking targetSpeed out of the equation; or normalizing the targetDirection and including targetSpeed as the magnitude of the targetDirection?
Sorry about the compiling errors. I type faster than I think sometimes. Fixed now. No, there shouldn't be a difference if you use rigidbody velocity or even a character controller's velocity, combining the two variables (hence the comment), but beware of gravity pulling your shots down as I'm not sure if Rigidbody.velocity is calculated before or after collisions.
Don't worry about the compiling errors, I was able to fix it all up easily enough. Again, thank you! Yeah, the leading works fine when I use the speed of the object but tends to miss when gravity gets involved. Since this is space based its not a big problem as gravity is going to be turned off. Besides, missing sometimes when an object turns or decelerates is good :)
I'll add 2 more years to your statement. ;)