Raycasting is not necessarily required, but it can help to predict a point where your projectile will stop/hit/bounce in the world. The basic premise is to create a function for the flight path dependant on starting location, starting velocity (usually the player’s character velocity plus their “arm strength”), and flight direction. Optionally you want a custom curve function for each projectile you make (heavier projectiles might fly less far, paper planes might fly very far). The ideal curve is a parabola (that’s how gravity works) but the more factors you implement the more it can differ. Here’s an example using only air resistance as a factor (projectile gets slower the longer it goes):
Flight path 45° arc: https://i.imgur.com/hPOYYHI.png
Same path but blocked by a wall: https://i.imgur.com/wPO3D7W.png
You can play around with the code if you want to increase the curve resolution.
local GLOBAL_GRAVITY = 2
local function path(steps, position, velocity, gravity)
local gravityVector = gravity*Vector3.new(0, -1, 0)
local d = velocity + gravityVector
local newVelocity = (velocity+gravityVector)*.8
if steps == 1 then
return position + d, newVelocity
elseif steps == 0 then
return position, velocity
end
return path(steps-1, position, newVelocity, gravity) + d, newVelocity
end
--Starting position for our indicator, and parent of beam attachments
local p = workspace.Part
--Variables
local position = Vector3.new() --Attachments are in part center
local velocity = Vector3.new(0, 1, -1).Unit * 20 --Length of vector is throwing speed in studs/s
local function makeBeam(a1, a2)
local b = Instance.new("Beam")
b.Attachment0 = a1
b.Attachment1 = a2
b.Color = ColorSequence.new(Color3.new(1, 0, 0))
b.Transparency = NumberSequence.new(0)
b.Width0 = 0.2
b.Width1 = 0.2
b.FaceCamera = true
b.Parent = a1
end
local function makeHitIndicator(pos)
local part = Instance.new("Part")
part.Name = "Indicator"
part.Anchored = true
part.CanCollide = false
part.Color = Color3.new(1, 0, 0)
part.Shape = Enum.PartType.Ball
part.Size = Vector3.new(0.4, 0.4, 0.4)
part.Position = pos
part.Parent = p
end
--We will draw the curve until it hits something, or 100 cycles pass
local lastAttachment
for i = 0, 100 do
local pos, vel = path(i, position, velocity, GLOBAL_GRAVITY)
local ray = Ray.new(p.Position+pos, vel)
local raycastResult, hitPos = workspace:FindPartOnRayWithIgnoreList(ray, {p})
local a = Instance.new("Attachment")
if raycastResult then
a.WorldPosition = hitPos - p.Position
if lastAttachment then
makeBeam(lastAttachment, a)
end
makeHitIndicator(hitPos)
a.Parent = p
break
else
a.Position = pos
end
a.Parent = p
if lastAttachment then
makeBeam(lastAttachment, a)
end
lastAttachment = a
end