Hey everyone!
Here’s a simple and realistic way to aim artillery/mortars in Roblox using physics-based projectile prediction.
If you treat 20 studs as 1 meter, Roblox’s gravity of 196.2 studs/s² perfectly matches real-world Earth gravity of 9.81 m/s². That means you can use real-world projectile speeds directly, without any scaling
Real gravity = 9.81 m/s²
Roblox gravity = 196.2 studs/s²
= 9.81 m/s² * 20 studs/m
Formula Used in Script
local underRoot = v^4 - g * (g * d² + 2 * h * v²)
θ = atan( (v² ± √(v⁴ − g(gd² + 2hv²))) / (g * d) )
-
θ
= launch angle (in radians) -
v
= projectile speed -
g
= gravity -
d
= horizontal distance to target -
h
= vertical distance to target
the script for the High-Arc Prediction
local part = script.Parent
local target = workspace.Target
local projectileSpeed = 3000
local gravity = workspace.Gravity
function GetLaunchAngle(origin, targetPos, speed, gravity)
local displacement = targetPos - origin
local horizontal = Vector3.new(displacement.X, 0, displacement.Z).Magnitude
local vertical = displacement.Y
local speed2 = speed ^ 2
local rootPart = speed2 ^ 2 - gravity * (gravity * horizontal ^ 2 + 2 * vertical * speed2)
if rootPart < 0 then return nil end
local root = math.sqrt(rootPart)
local angle1 = math.atan((speed2 + root) / (gravity * horizontal))
local angle2 = math.atan((speed2 - root) / (gravity * horizontal))
return math.max(angle1, angle2) -- high arc
end
while true do
task.wait(0.1)
local angle = GetLaunchAngle(part.Position, target.Position, projectileSpeed, gravity)
if angle then
local flatDir = Vector3.new(target.Position.X, part.Position.Y, target.Position.Z) - part.Position
local yaw = math.atan2(-flatDir.X, -flatDir.Z)
part.CFrame = CFrame.new(part.Position) * CFrame.Angles(0, yaw, 0) * CFrame.Angles(angle, 0, 0)
end
end
the script for the lower/flatter arc
Use
math.min(...)
instead ofmath.max(...)
for a lower/flatter arc.
local part = script.Parent
local target = workspace.Target
local projectileSpeed = 150 -- adjust as needed
local gravity = workspace.Gravity
function GetLaunchAngle(origin, targetPos, speed, gravity)
local displacement = targetPos - origin
local horizontal = Vector3.new(displacement.X, 0, displacement.Z)
local horizontalDistance = horizontal.Magnitude
local verticalDistance = displacement.Y
local speedSquared = speed ^ 2
local g = gravity
local underRoot = speedSquared ^ 2 - g * (g * horizontalDistance ^ 2 + 2 * verticalDistance * speedSquared)
if underRoot < 0 then return nil end
local root = math.sqrt(underRoot)
local angle1 = math.atan((speedSquared + root) / (g * horizontalDistance))
local angle2 = math.atan((speedSquared - root) / (g * horizontalDistance))
return math.min(angle1, angle2) -- Use flatter arc
end
while true do
task.wait(0.1)
local origin = part.Position
local targetPos = target.Position
local angle = GetLaunchAngle(origin, targetPos, projectileSpeed, gravity)
if angle then
local flatDir = Vector3.new(targetPos.X, origin.Y, targetPos.Z) - origin
local yaw = math.atan2(-flatDir.X, -flatDir.Z)
local launchCFrame = CFrame.new(origin) * CFrame.Angles(0, yaw, 0) * CFrame.Angles(angle, 0, 0)
part.CFrame = launchCFrame
print("Pitch angle:", math.deg(angle))
else
warn("Target out of range")
end
end
Mortar{ 150–300 m/s }
Howitzer (155mm){ ~800 m/s }
tank gun(M1 abrams){1500 m/s}