This is a proof of concept and should ideally be implemented on the client rather than the server. I’ve written it on the server for now as I currently do not have enough time to write the full thing.
Script Inside ServerScriptService
local RunService = game:GetService("RunService")
-- Testing projectile part
local Projectile = Instance.new("Part")
Projectile.Size = Vector3.new(1, 1, 3)
Projectile.Position = Vector3.new(0, 20, 0)
Projectile:SetAttribute("CreationTime", tick()) -- Track the creation time for the interpolation function
Projectile.Anchored = true
Projectile.Parent = workspace
local Force = 2 -- Horizontal movement speed of the projectile
local Gravity = 2 -- Fall speed of the projectile
--- Quadratic interpolation function to calculate the projectile's position
-- @param startPos The starting position of the projectile
-- @param force The force applied to the projectile (used for air resistance)
-- @param currentTime The current time
-- @return The new position of the projectile
local function interpolate(startPos, force, currentTime)
-- Calculate the vertical drop this frame, clamped to a maximum of 5 (terminal velocity)
local drop = math.clamp(currentTime^2 / Gravity / force, 0, 5)
-- Calculate the new position by subtracting the drop from the starting position
local newPos = startPos - Vector3.new(0, drop, 0)
return newPos
end
--- Function called every frame to update the projectile's position
local function heartbeat()
-- Get the creation time of the projectile
local creationTime = Projectile:GetAttribute("CreationTime")
-- Calculate how long the projectile has existed
local timeDifference = tick() - creationTime
-- Set the new position for the projectile
-- interpolate(Projectile.Position, Force, timeDifference) to get the new height
-- Projectile.CFrame.LookVector to get the horizontal movement
-- Projectile.Size.Z to avoid overlapping and skipping positions
-- Increase the part's Z size to make it move faster
Projectile.Position = interpolate(Projectile.Position, Force, timeDifference) + Projectile.CFrame.LookVector * Projectile.Size.Z
end
-- Connect the heartbeat function to the RunService.Heartbeat event
RunService.Heartbeat:Connect(heartbeat)
Unanchored parts would be bad for performance and have unreliable physics when used on the client. I used anchored parts and moved them with a RunService.Heartbeat connection.
Create a Region3 to represent the projectile on the server, then use the interpolation function I provided to move that region forwards. Use Workspace:GetPartBoundsInBox to check for collisions.
You could use the interpolation function to visualize the movement of the part by updating its position, or have the server send the Region3 location to the client, and use that to visualize the part.
Additionally, optimize the collision detection logic and part movement using something like batch processing before using it in production.
Instead of creating separate RunService.Heartbeat connections for each part’s collision detection, create a single connection that loops through a table of all the projectiles and processes them simultaneously. However, consider using BasePart.Touched first, as it may be more performant if it works correctly.
I know how do to everything you said except the equation.
This was what i was looking for but i dont know how to implement it i know the rest of what you said but i usually use roblox physics to move my projectile.