How can I make this script performant ? (I'm getting terrible frame rates)

Hello, so recently I have been trying to make a script that equips infinite pets and other players can see the player’s pets but when the player equips more than 20 pets I start getting terrible frame rates (from 60 to 15) here is my script:
Module script:

local module = {}

local rs = game:GetService('RunService')
local petFolder = game.ReplicatedStorage.Pets
local pets = {}
local fullCircle = 2 * math.pi
local radius = script.Distance.Value

function module.EquipPet(player, petName)
    local character = player.Character or player.CharacterAdded:Wait()
    local pet = petFolder:FindFirstChild(petName):Clone()
    pet.Parent = workspace:FindFirstChild("Pets"):FindFirstChild(player.Name..' s pets')
    local hrp = character:WaitForChild("HumanoidRootPart")
    pet:SetPrimaryPartCFrame(hrp.CFrame)
    table.insert(pets, pet)
    local function getXAndZPositions(angle)
        local x = math.cos(angle) * radius
        local z = math.sin(angle) * radius
        return x, z
    end

    rs.Heartbeat:Connect(function()
        for i, pet in pairs(pets) do
            local angle = i * (fullCircle / #pets)
            local x, z = getXAndZPositions(angle)
            local position = (hrp.CFrame * CFrame.new(x, 2, z)).p
            local lookAt = hrp.Position
            pet:SetPrimaryPartCFrame(pet.PrimaryPart.CFrame:Lerp(CFrame.new(position, lookAt), script.Smoothness.Value/#pets))
        end
    end)
    if #pets > 1 then
        radius += pet.PrimaryPart.Size.X/2
    end
    game.ReplicatedStorage.PetHandlerClient:FireClient(player, petName)
end

return module

Local script:


local player = game.Players.LocalPlayer
local character = player.Character or player.CharacterAdded:Wait()
local hrp = character:WaitForChild("HumanoidRootPart")
local rs = game:GetService('RunService')
local petFolder = game.ReplicatedStorage.Pets
local module = game.ReplicatedStorage.PetHandler
local pets = {}
local fullCircle = 2 * math.pi
local radius = module.Distance.Value


game.ReplicatedStorage.PetHandlerClient.OnClientEvent:Connect(function(p)
    for i, v in pairs(workspace:FindFirstChild('Pets'):FindFirstChild(player.Name..' s pets'):GetChildren()) do
        v:Destroy()
    end
    local pet = petFolder:FindFirstChild(p):Clone()
    pet.Parent = workspace:FindFirstChild('PetsClient'):FindFirstChild(player.Name..' s pets')
    pet:SetPrimaryPartCFrame(hrp.CFrame)
    table.insert(pets, pet)
    local function getXAndZPositions(angle)
        local x = math.cos(angle) * radius
        local z = math.sin(angle) * radius
        return x, z
    end

    rs.RenderStepped:Connect(function()
        for i, pet in pairs(pets) do
            local angle = i * (fullCircle / #pets)
            local x, z = getXAndZPositions(angle)
            local position = (hrp.CFrame * CFrame.new(x, 2, z)).p
            local lookAt = hrp.Position
            pet:SetPrimaryPartCFrame(pet.PrimaryPart.CFrame:Lerp(CFrame.new(position, lookAt), module.Smoothness.Value/#pets))
        end
    end)
    if #pets > 4 then
        radius += pet.PrimaryPart.Size.X/2
    end
end)

Server script:

local petts = {
    'Evolved Hyperion',
    'Radioactive Serpent'
}
local module = require(game.ReplicatedStorage.PetHandler)


game.Players.PlayerAdded:Connect(function(plr)
    
    local pets = Instance.new('Folder', workspace:WaitForChild('Pets'))
    pets.Name = plr.Name..' s pets'
    local petsClient = Instance.new('Folder', workspace:WaitForChild('PetsClient'))
    petsClient.Name = plr.Name..' s pets'
    
    plr.CharacterAdded:Connect(function(char)

    end)
end)

game.ReplicatedStorage.EquipPet.OnServerEvent:Connect(function(plr)
    local randomPet = petts[math.random(1, #petts)]
    game.ReplicatedStorage.PetHandlerClient:FireClient(plr, randomPet)
end)

Here is the file: Pet following system.rbxl (39.0 KB)
Thank you!

Also the reason why I’m using a client script too is to make lerping smooth because it’s lagging from the server: Pet not smooth on server
I also tried with just the client script and it’s still lagging so it’s not because of the switching from client to server I think.

you are binding heartbeat a WHOLE LOT bruh, When they unequip pet, the conneted function will still run, I would suggest looping through all the pets and positioning them accordingly in one heartbeat… Binding a 60 times per second event 20 times is tuff… The least you can do is unbind the event on unequip

Edit: All pets are updating at the exact same time, you need to space it out by looping through and doing the mafs for each individual one every frame

Edit2: You can fix this by making a table with every pet in the game (currently) and looping through the table every frame… Also you should try following @myaltaccountsthis solution of BodyMovers, Then set network ownership to the client of all the pets (to give the client control of the physics and remove laggy stutteryness on their end)… Network Ownership | Documentation - Roblox Creator Hub

2 Likes

It is probably because you are just doing a lot of math and setting stuff every frame.
Consider using BodyMovers and updating less frequently, they can move things smoothly

1 Like

I don’t really understand what you mean by ‘making a table with every pet in the game (currently) and looping through the table every frame’ I think I’m already using a table.

I tried using them but they don’t work with certain pets like giant ones.

That means the .P property which means Power was too low. You can set it based on how heavy the pet is, using Part:GetMass(), and multiply that by a big number

1 Like

Oh, thank you I will try that and see if it works.

But how can I use :GetMass() with a model?

I think you can set the biggest part as the Model.PrimaryPart, and then use the .Mass property. It might consider other welded parts, but if something is off, then try BasePart | Documentation - Roblox Creator Hub

Use .Mass instead of :GetMass()
“This function predates the Mass property. It remains supported for backward-compatibility; you should use the Mass property directly.”

1 Like
local petTable = {}
local runService = game:GetService("RunService")

local function AddPet(petModel)
	table.insert(petTable, petModel)
end

local function removePet(petModel)
	local PetIndex = table.find(petTable, petModel)
	petTable[PetIndex] = nil
	petModel:Destroy()
end

runService.Heartbeat:Connect(function()
	for i,v in pairs(petTable) do
		--Update Pet Position
		--also you could have the client update everyones pets (so that it doesn't lag)
	end
end)

is what i mean

1 Like

Still very laggy:
Module:

local module = {}

local rs = game:GetService('RunService')
local petFolder = game.ReplicatedStorage.Pets
local fullCircle = 2 * math.pi
local radius = script.Distance.Value
local petTable = {}

function module.EquipPet(player, petName)
    local character = player.Character or player.CharacterAdded:Wait()
    local pet = petFolder:FindFirstChild(petName):Clone()
    pet.Parent = workspace:FindFirstChild("Pets"):FindFirstChild(player.Name..' s pets')
    local hrp = character:WaitForChild("HumanoidRootPart")
    pet:SetPrimaryPartCFrame(hrp.CFrame)
    
    
    local function addPet(petModel)
        table.insert(petTable, petModel)
    end
    
    addPet(pet)

    local function getXAndZPositions(angle)
        local x = math.cos(angle) * radius
        local z = math.sin(angle) * radius
        return x, z
    end

    local function removePet(petModel)
        local PetIndex = table.find(petTable, petModel)
        petTable[PetIndex] = nil
        petModel:Destroy()
    end

    rs.Heartbeat:Connect(function()
        for i,v in pairs(petTable) do
            local angle = i * (fullCircle / #petTable)
            local x, z = getXAndZPositions(angle)
            local position = (hrp.CFrame * CFrame.new(x, 2, z)).p
            local lookAt = hrp.Position
            v:SetPrimaryPartCFrame(v.PrimaryPart.CFrame:Lerp(CFrame.new(position, lookAt), script.Smoothness.Value/#petTable))
        end
    end)
    
    
    game.ReplicatedStorage.PetHandlerClient:FireClient(player, petName)
end






return module

Client:


local player = game.Players.LocalPlayer
local character = player.Character or player.CharacterAdded:Wait()
local hrp = character:WaitForChild("HumanoidRootPart")
local rs = game:GetService('RunService')
local petFolder = game.ReplicatedStorage.Pets
local module = game.ReplicatedStorage.PetHandler
local petTable = {}

local fullCircle = 2 * math.pi
local radius = module.Distance.Value


game.ReplicatedStorage.PetHandlerClient.OnClientEvent:Connect(function(p)
    for i, v in pairs(workspace:FindFirstChild('Pets'):FindFirstChild(player.Name..' s pets'):GetChildren()) do
        v:Destroy()
    end
    local pet = petFolder:FindFirstChild(p):Clone()
    pet.Parent = workspace.PetsClient
    
    pet:SetPrimaryPartCFrame(hrp.CFrame)


    local function addPet(petModel)
        table.insert(petTable, petModel)
    end

    addPet(pet)

    local function getXAndZPositions(angle)
        local x = math.cos(angle) * radius
        local z = math.sin(angle) * radius
        return x, z
    end

    local function removePet(petModel)
        local PetIndex = table.find(petTable, petModel)
        petTable[PetIndex] = nil
        petModel:Destroy()
    end

    rs.Heartbeat:Connect(function()
        for i,v in pairs(petTable) do
            local angle = i * (fullCircle / #petTable)
            local x, z = getXAndZPositions(angle)
            local position = (hrp.CFrame * CFrame.new(x, 2, z)).p
            local lookAt = hrp.Position
            v:SetPrimaryPartCFrame(v.PrimaryPart.CFrame:Lerp(CFrame.new(position, lookAt), .05/#petTable))
        end
    end)

end)

Okay I will try that but I prefer the lerping effect.

Hey, I tried body positions but they don’t have a CFrame proprety and I am usin a CFrame to position the pet, is there a way to fix this ?

They use position, and you can use a BodyGyro as well and give it the CFrame for orientation.