Thoughts on my pet script?

Hello guys, I am working on adding a script to my game that moves pets to follow the player. The problem is that it is all running on the server, and with a “while true do” for up to 20 players. So I am scared that this will lag but I have no way to accurately test if it will. All of this runs on a server script.

game.Players.PlayerAdded:Connect(function(player)
player.CharacterAdded:Connect(function(character)
	local pet = game:GetService("ReplicatedStorage").Pets.Bodies.PetModel:Clone()
	local torso
	local dead = false
	local inProg = false
	local humanoid = character:WaitForChild("Humanoid")
	if humanoid.RigType == Enum.HumanoidRigType.R15 then
		torso = player.Character:WaitForChild("UpperTorso")
	elseif humanoid.RigType == Enum.HumanoidRigType.R6 then
		torso = player.Character:WaitForChild("Torso")
	end
	pet.Parent = character
	humanoid.Died:Connect(function()
		dead = true
	end)
	while true do
		if dead then pet:Destroy() break end
		local p = torso.CFrame * Vector3.new(2,2,2)
		pet.BodyGyro.CFrame = torso.CFrame * CFrame.Angles(0,(3*math.pi)/2,0)
		pet.BodyPosition.Position = Vector3.new(p.x,p.y,p.z)
		wait()
	end
end)
4 Likes

Remove the wait() and replace while true do with while wait() do

1 Like

you can replace this with

p.Position
1 Like

Thanks for you help, but I don’t think that solves the issue I described.

Thanks, but I don’t think that fixes my issue.

It’s always worth commenting your code so that when you go back to it a month later, you can quickly look through to know exactly what it is doing.

Never put a while loop inside CharacterAdded because if another character is loaded without the humanoid dying, say the character falls off the map for example, then the loop will never end, and you’ll end up with two infinite loops, whilst since the Character would be destroyed by debris service the pet wouldn’t exist, you’d get errors from attempting to index it.

Instead, I’d connect to Heartbeat for any pet movement code.

I’d also store a table of pets by player, and at the start of character added check the table for any pets already owned by that player and destroy them there.

local PetDictionary = {}

game.Players.PlayerAdded:Connect(function(Player)
    Player.CharacterAdded:Connect(function(Character)

        local OldPet = PetDictionary[Player]
        if OldPet then OldPet:Destroy() end
        --When a character spawns, if there is a pet from their previous life, it is removed

        local Humanoid = Character:WaitForChild("Humanoid")

        local Pet = game:GetService("ReplicatedStorage").Pets.Bodies.PotModel:Clone()
        Pet.Parent = Character
        --Load the pet Instance, and parent it to the Character.
        
        Humanoid.Died:Connect(function()
            Pet:Destroy()
            PetDictionary[Player] = nil
        end)
        --If the humanoid dies, Destroy the pet and remove the dictionary entry

    end)
end)

game:GetService("RunService").Heartbeat:Connect(function(dT)
    for Player,Pet in pairs(PetDictionary) do
        if Player and Player.Character then
            local Root = Player.Character.PrimaryPart
            --Will get the HumanoidRootPart of the Character
            Pet.BodyPosition.Position = (Root.CFrame * CFrame.new(2,2,2)).p
            --Move to Root position + (2,2,2) in relative space

            Pet.BodyGyro.CFrame = (Root.CFrame-Root.Position) * CFrame.Angles(0, 3*math.pi/2, 0)
        end
    end
end)

Using the Player as the key of the table is fine even if a player leaves, the Player object is destroyed regardless of the reference and becomes nil. The pet is a descendant of the Character and will be destroyed too, but it just leaves us with a nice clean slate with no memory risks whilst giving us an easy tool to get a Pet from a Player object.

Edits: Grammar and clarification in explanation.

1 Like

The way he’s doing it is just as correct of a way, if anything while wait() do is worse.

2 Likes

Actually, I ended up coming up with a different solution and I am curious how do you think it compares to yours. I ended up using SetNetworkOwner of the pet brick and just used the same script as before but in a local script.

--SERVER SCRIPT
game.Players.PlayerAdded:Connect(function(player)
player.CharacterAdded:Connect(function(character)
	local rootPart = game:GetService("ReplicatedStorage").Pets.Bodies.PetModel:Clone()
	rootPart.Parent = character
	print(rootPart.Parent.Name)
	while not rootPart:IsDescendantOf(workspace) do
		wait()
	end
	rootPart:SetNetworkOwner(game.Players:GetPlayerFromCharacter(character))
end)
end)

--LOCAL SCRIPT
local plr = game.Players.LocalPlayer
repeat wait() until plr.Character
local chr = plr.Character
plr.CharacterAdded:connect(function(char)
chr = char
local pet = chr:WaitForChild("PetModel")
local torso
local dead = false
local inProg = false
local humanoid = chr:WaitForChild("Humanoid")
if humanoid.RigType == Enum.HumanoidRigType.R15 then
	torso = plr.Character:WaitForChild("UpperTorso")
elseif humanoid.RigType == Enum.HumanoidRigType.R6 then
	torso = plr.Character:WaitForChild("Torso")
end
humanoid.Died:Connect(function()
	dead = true
end)
while wait() do
	if dead then pet:Destroy() break end
	local p = torso.CFrame * Vector3.new(2,2.4,2)
	pet.BodyGyro.CFrame = torso.CFrame * CFrame.Angles(0,(3*math.pi)/2,0)
	pet.BodyPosition.Position = Vector3.new(p.x,p.y,p.z)
end

end)

pet = chr:WaitForChild("PetModel")
local torso
local dead = false
local inProg = false
local humanoid = chr:WaitForChild("Humanoid")
if humanoid.RigType == Enum.HumanoidRigType.R15 then
torso = plr.Character:WaitForChild("UpperTorso")
elseif humanoid.RigType == Enum.HumanoidRigType.R6 then
torso = plr.Character:WaitForChild("Torso")
end
humanoid.Died:Connect(function()
dead = true
end)
while wait() do
if dead then pet:Destroy() break end
local p = torso.CFrame * Vector3.new(2,2.4,2)
pet.BodyGyro.CFrame = torso.CFrame * CFrame.Angles(0,(3*math.pi)/2,0)
pet.BodyPosition.Position = Vector3.new(p.x,p.y,p.z)
end

If the local script was a CharacterScript then you could just do LocalPlayer.Character or script.Parent rather than connecting to the CharacterAdded event, and it also means the script would be destroyed when a new Character loads so the while loop wouldn’t be a problem in that case.

1 Like

Don’t run it on the server, it will build up server lag.