Thoughts on my pet follow player script

Hello!

I am making a pet follow player script (I used a YouTube tutorial to see how they do it). It’s using a while true do loop and I’m afraid that in big servers this may cause lag. I am looking into Network Ownership and then using a LocalScript to control the movement, but I’m not sure how to start. Anyways, here is my code:

local char = script.Parent.Parent.Parent.Parent
local pet = script.Parent

local hum = char:FindFirstChild("Humanoid")
local torso = char:FindFirstChild("HumanoidRootPart")
local pet = script.Parent
 
local maxFloat = 1
local floatInc = 0.025
local sw = false
local fl = 0
 
while true do
    wait()
    if not sw then
        fl = fl + floatInc
        if fl >= maxFloat then
            sw = true
        end
    else
        fl = fl - floatInc
        if fl <=-maxFloat then
            sw = false
        end
    end
    if pet ~= nil and hum ~= nil and torso ~= nil then
        if hum.Health >= 0 then
            local cf = torso.CFrame * CFrame.new(3,2+fl,3)
            pet.BodyPosition.Position = Vector3.new(cf.x,cf.y,cf.z)
            pet.BodyGyro.CFrame = torso.CFrame * CFrame.new(3,0,-3)
        else
            break
        end
    end
end
2 Likes

I’m not sure, but I think the while loop is the only way.

Maybe do wait(.2), this would decrease lag and the pet wouldn’t be so far behind.

Just a side note.

While wait() do

end

Will work just as well as

While true do
wait()
end 
1 Like

Well depending on how many players are in the game you could simulate the movement of the pet on the client then update it to the server and tween it from the last updated position to the new one so it looks like it is being simulated on the server. This may reduce lag but i’m not sure.

2 Likes

The loop is not the only way. I have seen people using RunService or Network Ownership and handling movement on the client. I want to do it the Network Ownership way but I’m not sure how I’d go about it.

2 Likes

I’ll do some research, since I’m also in middle of a game with pets and don’t want it to lag.

Make it more modular. I personally find it redundant to place individual scripts in all parts of my workspace. It’s a messy style of coding in my opinion.

If it’s possible to avoid loops, it’s best to do so, both AlignPosition and AlignOrientation allow you to minimize changes and avoid loops and client side controlling via local script (assuming it’s just a basic follow pet system, with more advanced ones you will need to do client side scripting too, but it’s still minimized)

here is an example of this in action, I tried to explain everything in comments, but if you have any questions let me know

Module named PetService in ServerScriptService

local Players = game:GetService("Players")
local CollectionService = game:GetService("CollectionService")


local petTemplate = Instance.new("Model")
petTemplate.Name = "Pet"

local petPrimary = Instance.new("Part")
petPrimary.Material = Enum.Material.SmoothPlastic
petPrimary.CanCollide = false
petPrimary.Anchored = false
petPrimary.Size = Vector3.new(3, 3, 3)
petPrimary.Shape = Enum.PartType.Ball
petPrimary.BrickColor = BrickColor.Green()
petPrimary.Parent = petTemplate

petTemplate.PrimaryPart = petPrimary

local petInfos = {
	Pet1 = {
		Model = petTemplate,
		PosOffset = Vector3.new(3, 3, 3), -- the attachment offset
		AlignPosMaxForce = 20000,
		AlignPosResponsiveness = 15,
		AlignOriResponsiveness = 20
	}
}

local PetService = {Pets = {}, PetInfos = petInfos}

-- returns a CollectionService tag for a player, for tagging and deleting pets
local function getPetTag(player)
	return player.Name .. "Pet"
end

-- creates a new pet model, and sets up the constraints
local function createPet(player, character, petInfo)
	local petTag = getPetTag(player)
	
	local pet = petInfo.Model:Clone()
	local petPrimary = pet.PrimaryPart
	local characterPrimary = character.PrimaryPart
	
	local alignPosAttachment0 = Instance.new("Attachment", petPrimary)
	local alignPosAttachment1 = Instance.new("Attachment", characterPrimary)
	alignPosAttachment1.Position = petInfo.PosOffset
	
	local alignOriAttachment0 = Instance.new("Attachment", petPrimary)
	local alignOriAttachment1 = Instance.new("Attachment", characterPrimary)
	
	local alignPosition = Instance.new("AlignPosition")
	alignPosition.MaxForce = petInfo.AlignPosMaxForce
	alignPosition.Responsiveness = petInfo.AlignPosResponsiveness
	alignPosition.Attachment0 = alignPosAttachment0
	alignPosition.Attachment1 = alignPosAttachment1
	alignPosition.Parent = petPrimary
	
	local alignOrientation = Instance.new("AlignOrientation")
	alignOrientation.Responsiveness = petInfo.AlignOriResponsiveness
	alignOrientation.Attachment0 = alignOriAttachment0
	alignOrientation.Attachment1 = alignOriAttachment1
	alignOrientation.Parent = petPrimary
	
	CollectionService:AddTag(pet, petTag) -- to delete the pet when needed using :GetTagged()
	petPrimary.CFrame = characterPrimary.CFrame -- moves the pet to the player initially so it doesnt have to fly across the map
	pet.Parent = workspace
	petPrimary:SetNetworkOwner(player) -- gives client control
end

-- deletes a player's pet model, using CollectionService to retrieve any existing tagged pet
local function deletePet(player)
	local petTag = getPetTag(player)
	for _, pet in ipairs(CollectionService:GetTagged(petTag)) do
		pet:Destroy()
	end
end

-- sets a player's pet, calls createPet() if they have a character
function PetService:SetPet(player, petInfo)
	if self.Pets[player] then
		PetService:UnsetPet(player)
	end
	
	self.Pets[player] = petInfo
	local character = player.Character
	
	if character then
		local humanoid = character:FindFirstChild("Humanoid")
		if humanoid and humanoid.Health > 0 then -- creates pet model if player has an alive character
			createPet(player, character, petInfo)
		end
	end
end

function PetService:UnsetPet(player)
	if self.Pets[player] then
		deletePet(player)
		self.Pets[player] = nil
	end
end

local function playerAdded(player)
	local petTag = getPetTag(player)
	
	PetService:SetPet(player, PetService.PetInfos.Pet1) -- set their pet, can be used outside of module, this is just here for testing
	
	local function characterAdded(character)
		local pet = PetService.Pets[player]
		
		if pet and #CollectionService:GetTagged(petTag) == 0 then -- if a pet is set and  no existing pet model exists, create one
			createPet(player, character, pet)
		end
		
		character.Humanoid.Died:Connect(function() -- player died, delete their pet
			deletePet(player)
		end)
	end
	characterAdded(player.Character or player.CharacterAdded:Wait()) -- gets the first character, either by getting it directly if it exists, or waiting for it
	player.CharacterAdded:Connect(characterAdded) -- connects the CharacterAdded event for any future characters
end

local function playerRemoving(player)
	PetService:UnsetPet(player)
end

-- calls the playerAdded() function for all player's that exist before we set up the PlayerAdded event
for _, player in ipairs(Players:GetPlayers()) do
	playerAdded(player)
end

Players.PlayerAdded:Connect(playerAdded)
Players.PlayerRemoving:Connect(playerRemoving)

return PetService

you will need to require() this module for the code to run, you can do this by having a script and writing

local PetService = require(game:GetService("ServerScriptService").PetService)
2 Likes