Calculate Projectile's Velocity Based on Targets Future Position Give The Targets Current Velocity And Travel Time of Projectile

I don’t know what exactly you want, so I proposed this solution that will hopefully help:


As mentioned before, the future position the target will travel is p = u + vt, where p is the future position, u being the initial position, v being the velocity of the target and t being the time. Time can vary, but in this case, assume it’s 10 seconds, so the future position is u + v(10)

For them to intersect, the position along the line must be equal and since the projectile is being affected by a velocity too, then we get this equation:
p = StartingPosition + vt

Knowing the distance and the speed, we can substitute for time (t), since t = distance / speed:
p = StartingPosition + v * (|p - StartingPosition| / 200)

Rewriting the equation to make v the subject, we get:
v = (p - StartingPosition) / (|p - StartingPosition| / 200)

Putting it in code:

local t = 10
local p = Target_Initial + Target_Velocity * t
BodyVelocity.Velocity = (p - StartingPosition) / ((p - StartingPosition).Magnitude / 200)

Forgot to mention, but “|v|” means the magnitude of the vector, if that wasn’t clear already in the code above

1 Like

Ok, I’ll take a look at that tomorrow and see if that works. I think I understand it at face value though.

Alright so now that we got the math out of the way, lets try to implement this in code

-- Im gonna make a function called solveQuadratic
-- We are always gonna take the positive root
local function solveQuadratic(a,b,c)
 -- https://en.wikipedia.org/wiki/Quadratic_formula
 local discriminant = b^2 - 4*a*c;
 local numerator = -b + discriminant^0.5;
 local denom = 2 * a;
 return numerator / denom;
end

local function solvePrediction()
 local speed = -- speed of proj
 local targetPosition = -- position of target
 local targetVelocity = --velocity of target
 local shooterPosition = -- position of shooter
 local relative = targetPosition - targetPosition; 
 local angle = math.acos(relative.Unit:Dot(targetVelocity.Unit))

 local a,b,c;
 a = targetVelocity.magnitude^2 - speed^2
 b = -2 * relative.magnitude * math.cos(angle) * targetVelocity.magnitude;
 c = relative.magnitude^2;

 local t = solveQuadratic(a,b,c);
 return targetPosition + targetVelocity * t;
end
1 Like

Damn nice this looks promising. Lol my brain is fried and I’m tired but I will definitely check this out tomorrow and test it out.

Ok, so I think your code has a problem:

local function solvePrediction(speed, targetPosition, targetVelocity, shooterPosition)
	local relative = targetPosition - targetPosition; 
	local angle = math.atan2(relative, targetVelocity);

	local a,b,c;
	a = targetVelocity.magnitude^2 - speed^2
	b = -2 * relative.magnitude * math.cos(angle) * targetVelocity.magnitude;
	c = relative.magnitude^2;

	local t = solveQuadratic(a,b,c);
	return targetPosition + targetVelocity * t;
end

Specifically local relative = targetPosition - targetPosition;

Also, when you do local angle = math.atan2(relative, targetVelocity); you get an exception:

ProjectileIntercept:33: invalid argument #1 to 'atan2' (number expected, got Vector3

I went ahead and coded up some random targets that shoot off at random directions and another script that is supposed to shoot the projectiles off towards that future position (using the code you whipped up). They can be found in StarterPlayerScripts.

PredictiveProjectileShootIntercept.rbxl (22.6 KB)

Whoops I was not thinking straight whwn I wrote that. My goal there was to get rhe anlge between the relative position vector and the velocity vector.

Obviously we dont use atan2 as that does not take in vectors as inputs. Instead to get the angle between do vectors we can use this formula which ill derive here…

-- we are solving for x
a • b = |a| * |b| * cos(x)
( a • b ) / |a| * |b| = cos(xl
acos( (a •b ) /  ( |a| * |b| ) ) = x

Ive updated the code in the original code to comply with this fix

Are you sure that local relative = targetPosition - targetPosition; is correct?

Wouldnt that just always equal Vector3.new(0,0,0) ?

Target - shooter. Typos sorry but that one shouls be pretty self explanatory

Hmm no dice. Its not erroring anymore and is shooting now but not in the right direction:

local function solvePrediction(speed, targetPosition, targetVelocity, shooterPosition)
	local relative = targetPosition - shooterPosition; 
	local angle = math.acos(relative.Unit:Dot(targetVelocity.Unit))

	local a,b,c;
	a = targetVelocity.magnitude^2 - speed^2
	b = -2 * relative.magnitude * math.cos(angle) * targetVelocity.magnitude;
	c = relative.magnitude^2;

	local t = solveQuadratic(a,b,c);
	return targetPosition + targetVelocity * t;
end

workspace.TargetParts.ChildAdded:Connect(function(TargetPart)
	-- Add a small delay to allow our target to travel a bit
	wait(math.random())
	local ProjectileClone = Projectile:Clone()
	local FuturePosition = solvePrediction(ProjectileSpeed, TargetPart.Position, TargetPart.BodyVelocity.Velocity, ProjectileClone.Position)
	ProjectileClone.BodyVelocity.Velocity =( FuturePosition - ProjectileClone.Position ).Unit * ProjectileSpeed
	ProjectileClone.Parent = workspace
	ProjectileClone.Touched:Connect(function(Part)
		if Part.Name ~= "BasePlate" then
			print(Part.Name)
		end
	end)
	Debris:AddItem(ProjectileClone, 30)
end)

If your curious, here is the updated demo:

PredictiveProjectileShootIntercept.rbxl (22.7 KB)

Ill check it out when I get home alright. There could be some things I got wrong or missed

1 Like

Awesome, thanks man. Yea no rush. Think its close.

Ok I Got it to work. The embarrassing part about it is that it had nothing to due with the actual derivation of the solution its-self. But rather with my implementation of the quadratic formula. You might have noticed that the balls were actually going in the right direction, but going downwards instead of upwards towards the target. A simple misplace of a negative can change everything.

Also do note that there are some edge cases and by far this implementation is not perfect. Though, in most cases it should be able to take a pretty accurate guess about the future position of the target.

Also i got rid of the small delay you had in there to make sure it was accurate enough, you can add it back. Here is the download link to the rbx file…

ProjectileLead.rbxl (22.6 KB)

Edit: You might notice that the projectile’s are a little off the target, this can be due to many things such as: the projectile isnt fast enough, the projectile gets the information of where to travel to late, etc etc. Just note that a higher projectile speed or calculating the prediction as soon as position updates will lead to more accurate predictions.

3 Likes

Nice! Yep that did the trick! Great work dude. See I knew I could solve this by iterating over points on the TargetParts trajectory but that felt bad and would result in extra computations. I knew there was a formulaic method to calculate this. @AntiSocial_Taco I think this will do it for you.

Edit: I added a base plate back to your final solution so people could stand with their character and see.

Also the projectiles destroy the TargParts on hit now too.

ProjectileLead.rbxl (24.6 KB)

1 Like

Aight thanks. Will be useful for my future projects.