You are handling a very large number of bullets, you can try these few optimizations to reduce the load on the server, you can maybe try to reduce the expensive operations that run inside the loops, you can either move RaycastParams creation outside the loop, you are currently creating a new RaycastParams object inside the loop, which is really expensive since you’re doing it for every bullet, instead you can create the RaycastParams object once outside the loop and reuse it
local prams = RaycastParams.new()
prams.FilterType = Enum.RaycastFilterType.Exclude
and inside the loop you update only what changes
prams.FilterDescendantsInstances = {v}
another way is the cache CollectionService:GetTagged("Bullets"), everytime you call GetTagged("Bullets") it retrieves the tagged instances which causes overhead, instead cache it at the start of the loop, like this
local bulletsList = game:GetService("CollectionService"):GetTagged("Bullets")
for i, v: Part in pairs(bulletsList) do
in this way you wouldn’t be calling GetTagged repeatedly through each frame, just once every heartbeat, now another way is that the meter_to_stud(10 calculation runs every time, but since it’s a constant you can compute it once outside the loop
local gravityMultiplier = meter_to_stud(10) -- Precompute this
and this inside the loop
gravity += dt * gravityMultiplier
once this is setup, it should run better. And when you are updating the bullet’s Cframe, you call the CFrame.lookAt(v.Position, v.Position + old_pas) and it seems like you’re passing old_pas without adjusting its value for a new direction, you should make sure it’s properly calculating. that’s if the intended behavior is for the bullet to face a new position, this might work
v.CFrame = CFrame.lookAt(v.Position, v.Position + (v.Position - old_pas).Unit)
this will make sure the bullet is facing the right direction, and will reduce issues with the direction calculation, other than this you can also check the “raycasts” they are really expensive, and you are performing them for each bully on each frame, you could optimize this by performing racasts conditionally, you can skip raycast if the distance moved is too small, throttle raycasts by running them every dew frames rather than every frame this script should hep with that
local deltaPosition = v.Position - old_pas
if deltaPosition.Magnitude > 0.1 then -- Only raycast if the bullet moved enough
local resault = workspace:Raycast(old_pas, deltaPosition, prams)
if resault then
bullets[bulletType](resault, v)
end
end
you can also throttle raycasts using a simple country to run them every few frames. now the last way is to parallelize the bullet calculations, that’s if the server load still becomes a concern, you can consider splitting the bullets calculations across multiple threads, or you can use Roblox’s new Parallel luau feature, this is a bit more advanced but can improve the performance significantly for really high workloads in general, now this script has everything I talked about in one, and its moreso how the script might look like after applying all those changes
local function meter_to_stud(meters: number)
return meters * 3.571
end
local bullets = require(script.Parent.ModuleScript)
-- Precompute gravity multiplier
local gravityMultiplier = meter_to_stud(10)
-- Create RaycastParams once outside the loop
local prams = RaycastParams.new()
prams.FilterType = Enum.RaycastFilterType.Exclude
game:GetService("RunService").Heartbeat:Connect(function(dt)
local bulletsList = game:GetService("CollectionService"):GetTagged("Bullets")
for i, v: Part in pairs(bulletsList) do
-- Cache attributes for performance
local maxDistance = v:GetAttribute("Max_Distance")
local distance = v:GetAttribute("Distance")
local direction = v:GetAttribute("Direction")
local speed = v:GetAttribute("Speed")
local speedLossPerSecond = v:GetAttribute("Speed_Loss_Per_Second")
local gravity = v:GetAttribute("Gravity")
local bulletType = v:GetAttribute("TYPE")
-- Check if the bullet has reached max distance
if maxDistance < distance then
v:Destroy()
continue
end
-- Save the old position
local old_pas = v.Position
-- Calculate gravity
gravity += dt * gravityMultiplier
-- Move the bullet
v.Position += ((direction * speed * dt) - Vector3.new(0, gravity, 0))
-- Make the bullet look at the direction it's moving
local deltaPosition = v.Position - old_pas
v.CFrame = CFrame.lookAt(v.Position, v.Position + deltaPosition.Unit)
-- Update gravity
v:SetAttribute("Gravity", gravity)
-- Reduce speed to make it more realistic
if speed > 0 then
v:SetAttribute("Speed", speed - speedLossPerSecond * dt)
else
v:SetAttribute("Speed", 0)
end
-- Update distance
local distanceTraveled = deltaPosition.Magnitude
v:SetAttribute("Distance", distance + distanceTraveled)
-- Only raycast if the bullet moved enough
if distanceTraveled > 0.1 then
prams.FilterDescendantsInstances = {v}
local result = workspace:Raycast(old_pas, deltaPosition, prams)
if result then
bullets[bulletType](result, v)
end
end
end
end)
Now all these changes should hopefully reduce the load on your server, especially under a very high bullet count