Is there something i can do to make the script less expensive on the server?

local  function meter_to_stud(meters:number)
	return meters*3.571 
end
--this module has types of the bullets and their functions this is way more optomized than what eas does this one actually works with pointers meanwhile eas's connects it to each bullet 
local bullets = require(script.Parent.ModuleScript)
game:GetService("RunService").Heartbeat:Connect(function(dt)
	for i,v:Part in pairs(game:GetService("CollectionService"):GetTagged("Bullets")) do
		--checking if bullet has reached max distance 
		if v:GetAttribute("Max_Distance")<v:GetAttribute("Distance") then
			v:Destroy()
			continue
		end
		--getting bullet first position to adjust the direction its looking at 
		local old_pas = v.Position
		--getting the type of the bullet
		local Bullet_Type = v:GetAttribute("TYPE")
		--getting gravity
		local gravity = v:GetAttribute("Gravity")
		--calculating the gravity 
		gravity +=dt*meter_to_stud(10)
		--moving the bullet
		v.Position += ((v:GetAttribute("Direction")*v:GetAttribute("Speed")*dt)-Vector3.new(0,gravity,0))
		--making the bullet look at the direction it moved to
		v.CFrame = CFrame.lookAt(v.Position,v.Position+old_pas)
		--updating gravity 
		v:SetAttribute("Gravity",gravity)
		--lowering the speed to make it more realisitc duh 
		if v:GetAttribute("Speed")>0 then 
			v:SetAttribute("Speed",v:GetAttribute("Speed")-v:GetAttribute("Speed_Loss_Per_Second")*dt)
		else
			v:SetAttribute("Speed",0)
		end 
		--updating distance 
		v:SetAttribute("Distance",v:GetAttribute("Distance")+(v.Position-old_pas).Magnitude)
		local prams = RaycastParams.new()
		prams.FilterDescendantsInstances = {v}
		prams.FilterType = Enum.RaycastFilterType.Exclude
		local resault = workspace:Raycast(old_pas,v.Position-old_pas,prams)
		if resault then
			bullets[Bullet_Type](resault,v)
		end
	end
end)

the script is running fine with 3k bullets a second but i believe there are stuff that can be changed to make the script more performance friendly


1 Like

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

2 Likes

thank you very much i didnt notice those mistakes while making the script

1 Like

No worries, Glad I could help!

1 Like

This topic was automatically closed 14 days after the last reply. New replies are no longer allowed.