How do I show path of projectile?

I’m making a mortar for my game. (Video of it in action down below so you can visualise is)
And you can adjust the angle and rotation of it to hit targets. The problem is Its hard to visualize the path of the projectile before it fires so I want to make some sort of trail using a ray or particles to show the path of the projectile. I don’t know where to start.
https://streamable.com/wiz4vu

2 Likes

Check out Bézier Curves as I’d approach this by calculating a curve from the cannon to where it hits, then represent it with beams or parts.

1 Like

Also here is the code for the turret if that helps:


while wait(sP.Delay.Value) do
–Creates and plays Fire
local sound = sP.Fire:Clone()
sound.Name = ‘SoundEffect’
sound.Parent = sP
sound:Play()
game.Debris:AddItem(sound, 5)

local bullet = Instance.new('Part')
bullet.Name = 'Bullet'
bullet.CanCollide = true
bullet.Color = sP.Color
bullet.Material = 'Plastic'
bullet.Shape = 'Ball'
bullet.Size = Vector3.new(holeSize, holeSize, holeSize)
bullet.CFrame = sP.CFrame

bullet.FrontSurface = 'Studs'
bullet.BackSurface = 'Studs'
bullet.RightSurface = 'Studs'
bullet.LeftSurface = 'Studs'
bullet.TopSurface = 'Studs'
bullet.BottomSurface = 'Studs'

bullet.Velocity = bullet.CFrame.lookVector * sP.Speed.Value
bullet.Parent = sP
game.Debris:AddItem(bullet, 30)

end


1 Like

I’ve done stuff with bezier curves before, but how does that work with gravity and the angle of the mortar?

In this application it may be tricky if the bullet needs physics simulation for the whole path. Bezier curves would replace the trajectory of the bullet and would need animating of some sort, but you’d get both the trajectory visual and the cannon’s hit position in one go.
Egomoose has an excellent tutorial here on the forums for projectiles involving Bezier curves and beams, which is how I’d go about it.

1 Like

The thing in the post shows an example where the endpoint is the players mouse. I don’t know where the projectile is going to land I just know the angle of mortar and the projectiles speed & mass

Calculating projectile motion for that purpose is possible but a bit beyond what I remember from my physics classes, but I’m positive a formula can be recreated in Roblox for it. It’d certainly work in place of using a bezier curve to animate the bullet’s motion, since the only downside I can think of is the unpredictability of the bullets’ path being clear, but if it’s only for helping aim it should work. Perhaps if the cannon’s variables are always constant you could write your own function to relate the angle and range?

You do not really need the end point, even then you could find it using the projectile motion equation.

As long as you have:

g, v0, x0, endTime
g, v0, x0, t1
gravity :Vector3, initialVelocity : Vector3, initialPosiion: Vector3, nextPointEndTime : number
--t1 can be anything, it's used to find the end position of where the projectile will end up and where the beam is being directed towards

It should be fine, here is the beam functions modularized:

Summary
local function createBeamInProjectileMotionPath()
	local attach0 = Instance.new("Attachment")
	local attach1 = Instance.new("Attachment")

	local beam = Instance.new("Beam")
	beam.Color = ColorSequence.new(Color3.new(1, 0.035294, 0.035294))
	beam.Attachment0 = attach0
	beam.Attachment1 = attach1

	beam.Parent = workspace.Terrain
	attach1.Parent = workspace.Terrain
	attach0.Parent = workspace.Terrain

	return beam, attach0, attach1
end

local function beamProjectile(g, v0, x0, t1)
	-- calculate the bezier points
	local c = 0.5 * 0.5 * 0.5
	local p3 = 0.5 * g * t1 * t1 + v0 * t1 + x0
	local p2 = p3 - (g * t1 * t1 + v0 * t1) / 3
	local p1 = (c * g * t1 * t1 + 0.5 * v0 * t1 + x0 - c * (x0 + p3)) / (3 * c) - p2

	-- the curve sizes
	local curve0 = (p1 - x0).magnitude
	local curve1 = (p2 - p3).magnitude

	-- build the world CFrames for the attachments
	local b = (x0 - p3).unit
	local r1 = (p1 - x0).unit
	local u1 = r1:Cross(b).unit
	local r2 = (p2 - p3).unit
	local u2 = r2:Cross(b).unit
	b = u1:Cross(r1).unit

	local cf1 = CFrame.new(x0.x, x0.y, x0.z, r1.x, u1.x, b.x, r1.y, u1.y, b.y, r1.z, u1.z, b.z)

	local cf2 = CFrame.new(p3.x, p3.y, p3.z, r2.x, u2.x, b.x, r2.y, u2.y, b.y, r2.z, u2.z, b.z)

	return curve0, -curve1, cf1, cf2
end

local function createAndSetBeam(g, v0, x0, endTime, color)
	local t1 = endTime or 1 -- projectile time when in expires, or any number you want tbh
	local curve0, curve1, cf1, cf2 = beamProjectile(g, v0, x0, t1)
	local beam, attach0, attach1 = createBeamInProjectileMotionPath()
	beam.CurveSize0 = curve0
	beam.CurveSize1 = curve1
	beam.FaceCamera = true
	beam.Segments = 10 * math.round(t1 * 3)
	if color then
		beam.Color = ColorSequence.new(color)
	end
	-- convert world space CFrames to be relative to the attachment parent
	attach0.CFrame = attach0.Parent.CFrame:Inverse() * cf1
	attach1.CFrame = attach1.Parent.CFrame:Inverse() * cf2
end

return createAndSetBeam
2 Likes

Wait why do you need gravity as a parameter? Isn’t Gravity always the same?

Yeah, it’s the same but you will still need to tell the beam the shape of the projectile and where it’s going to which it will calculate using those 4 parameters.

Here is an example tool I took from an old topic I helped out a long time ago.

Create a tool in starter pack, and insert this as a local script
local tool = script.Parent
tool.RequiresHandle = false

local Players = game:GetService("Players")
local player = Players.LocalPlayer
local mouse = player:GetMouse()

local DB = false

local function createBeamInProjectileMotionPath()
	local attach0 = Instance.new("Attachment")
	local attach1 = Instance.new("Attachment")

	local beam = Instance.new("Beam")
	beam.Color = ColorSequence.new(Color3.new(1, 0.035294, 0.035294))
	beam.Attachment0 = attach0
	beam.Attachment1 = attach1

	beam.Parent = workspace.Terrain
	attach1.Parent = workspace.Terrain
	attach0.Parent = workspace.Terrain

	return beam, attach0, attach1
end

local function beamProjectile(g, v0, x0, t1)
	-- calculate the bezier points
	local c = 0.5 * 0.5 * 0.5
	local p3 = 0.5 * g * t1 * t1 + v0 * t1 + x0
	local p2 = p3 - (g * t1 * t1 + v0 * t1) / 3
	local p1 = (c * g * t1 * t1 + 0.5 * v0 * t1 + x0 - c * (x0 + p3)) / (3 * c) - p2

	-- the curve sizes
	local curve0 = (p1 - x0).magnitude
	local curve1 = (p2 - p3).magnitude

	-- build the world CFrames for the attachments
	local b = (x0 - p3).unit
	local r1 = (p1 - x0).unit
	local u1 = r1:Cross(b).unit
	local r2 = (p2 - p3).unit
	local u2 = r2:Cross(b).unit
	b = u1:Cross(r1).unit

	local cf1 = CFrame.new(x0.x, x0.y, x0.z, r1.x, u1.x, b.x, r1.y, u1.y, b.y, r1.z, u1.z, b.z)

	local cf2 = CFrame.new(p3.x, p3.y, p3.z, r2.x, u2.x, b.x, r2.y, u2.y, b.y, r2.z, u2.z, b.z)

	return curve0, -curve1, cf1, cf2
end

local function createAndSetBeam(g, v0, x0, endTime, color)
	local t1 = endTime or 1 -- projectile time when in expires, or any number you want tbh
	local curve0, curve1, cf1, cf2 = beamProjectile(g, v0, x0, t1)
	local beam, attach0, attach1 = createBeamInProjectileMotionPath()
	beam.CurveSize0 = curve0
	beam.CurveSize1 = curve1
	beam.FaceCamera = true
	beam.Segments = 10 * math.round(t1 * 3)
	if color then
		beam.Color = ColorSequence.new(color)
	end
	-- convert world space CFrames to be relative to the attachment parent
	attach0.CFrame = attach0.Parent.CFrame:Inverse() * cf1
	attach1.CFrame = attach1.Parent.CFrame:Inverse() * cf2
	
	return beam, attach0, attach1
end


tool.Activated:Connect(function()
	local hrp = player.Character.HumanoidRootPart
	if not DB then
		local comet = Instance.new("Part")
		comet.Size = Vector3.new(2,2,2)
		comet.CanCollide = false
		comet.CFrame = hrp.CFrame
		comet.Parent = game.Workspace

		local antiGravity = Instance.new("BodyForce")
		local antiGravityFactor = 0.9--1 = gravity force counteracted, 0 = no force lol
		antiGravity.Force = comet:GetMass()*workspace.Gravity*antiGravityFactor *Vector3.new(0,1,0)
		antiGravity.Parent = comet
		local appliedVelocity = (CFrame.new(hrp.Position,mouse.Hit.Position).LookVector * 50)
		comet.AssemblyLinearVelocity = appliedVelocity
		
		local gravity = Vector3.new(0,-Workspace.Gravity*(1-antiGravityFactor),0)
		createAndSetBeam(gravity, appliedVelocity, hrp.CFrame.Position, 1)

		spawn(function()
			DB = false
			wait(5)
			comet:Destroy()
		end)
	end
end)

Also you will notice setting the end time as t1 = 1 will create a beam because thats where the projectile will be after one second with the beam will go towards and stop at that point, this is what happens if you set it to t1 = 30 from Ego Mooses beam formula.

4 Likes

well i suppose you could pre-fire a projectile with a trail every second and change the trail’s stuff to make it fit you, its a bit laggy but it works

I applied this to the best of my ability and it seems to be working halfway. The beam is not a smooth curve and I might need to move it down because it seems like it’s 2 studs too high. Here is a model and a video of the model:

If the beam is not a smooth curve then you might need to either reduce t1 and make the beam shorter or increase the number of beam segments

	beam.Segments = 10 * math.round(t1 * 3)
2 Likes

Everything works now, the only weird thing is that the beam starts at the top of the turret and not the middle.


Do you know why this would be so?


this works but Im not sure if this is a good way to do it

1 Like

im super confused on what v0 and x0 is, any help thanks