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
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
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.
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
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)
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…
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.
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.