Pet follow module [Simulator style]

Adding pets should only be done from the server now, adding them from the client was an exploitable mess.

2 Likes

I tried this on a server script but it returns an error:

game.Players.PlayerAdded:Connect(function(plr)
	wait(5)
	local module =require(workspace.PetFollowModule)
	module:AddPet(plr, 'Spikey Balloon')
end)

I’m I doing it right?
Thank you!

You need to parse a model as the second argument

2 Likes

That’s what I did 'Spikey Balloon' Is a pet inside of a folder in replicatedStorage and I did configure it:


Thank you!

The module does not work anymore, for some reason the UpdatePetPositions function is not being called from the BindToRenderStep, I tried printing “called” every time the function is called and it does not print anything:
image
Edit: It does equip the pet and puts it in the player’s folder but the pet does not follow the player and there are no errors, here is how I call the function:


And here is how the pet is set up:
image
Thank you!

Hello! I have experienced the same issue as I was testing out the module but quickly came to fix this. If your still interested this is the fixed script:

local PetFollowModule = {}
local ModuleFunctions = {}

local Folders = {}

local Settings = require(script.Settings)
local LocalPlayer = game.Players.LocalPlayer

local YPoint = 0
local Addition = Settings.YDriftSpeed
local FullCircle = 2 * math.pi

local RunService = game:GetService("RunService")
local IsServer = LocalPlayer

local MasterFolder
local ReplicatedStorageFolder

MasterFolder = game.Workspace:WaitForChild('PetFolder')
ReplicatedStorageFolder = game.ReplicatedStorage:WaitForChild('ReplicatedStorageFolder')

function SetPetCollisions(Model)
	for _, Part in pairs(Model:GetDescendants()) do
		if Part:IsA("BasePart") then
			Part.CanCollide = false
			Part.Anchored = true
		end
	end
end

function getXAndZPositions(Angle, Radius)
	local x = math.cos(Angle) * Radius
	local z = math.sin(Angle) * Radius

	return x, z
end

function UpdatePetPositions()
	YPoint = YPoint + Addition
	if YPoint > Settings.YDrift or YPoint < -1 * Settings.YDrift then Addition = -1 * Addition end 


	for _, Folder in pairs(MasterFolder:GetChildren()) do
		local Player = game.Players[Folder.Name]
		local Character = Player.Character

		if Character  then
			local PetTable = Folder:GetChildren()
			local PetTableLength = #PetTable
			local Count = 0

			for _, Pet in pairs(PetTable) do
				Count += 1

				local Angle = Count * (FullCircle / PetTableLength)
				local X, Z = getXAndZPositions(Angle, Settings.Radius + (PetTableLength / 2))
				local Position = (Character.PrimaryPart.CFrame * CFrame.new(X, YPoint, Z)).p
				local LookAt = Character.PrimaryPart.Position
				local TargetCFrame = CFrame.new(Position, LookAt)

				local NewCFrame = Pet.PrimaryPart.CFrame:Lerp(TargetCFrame, Settings.Responsivness)
				Pet:SetPrimaryPartCFrame(NewCFrame)
			end
		end
	end
end

function ModuleFunctions:HideOthersPets()
	assert(IsServer, 'This function can only be called from the client')

	Settings.OtherPetsVisible = false

	for Id, Folder in pairs(MasterFolder:GetChildren()) do
		if Id ~= LocalPlayer.UserId then
			Folder.Parent = ReplicatedStorageFolder
		end
	end
end

function ModuleFunctions:HideAllPets()
	assert(IsServer, 'This function can only be called from the client')

	Settings.AllPetsVisible = false

	for Id, Folder in pairs(MasterFolder:GetChildren()) do
		Folder.Parent = ReplicatedStorageFolder
	end
end

function ModuleFunctions:ShowPets(Player)
	assert(IsServer, 'This function can only be called from the client')

	if ReplicatedStorageFolder:FindFirstChild(Player.Name) then
		ReplicatedStorageFolder[Player.Name].Parent = game.Workspace
	end
end

function ModuleFunctions:ShowOthersPets()
	assert(IsServer, 'This function can only be called from the client')

	Settings.OtherPetsVisible = true

	for Id, Folder in pairs(ReplicatedStorageFolder:GetChildren()) do
		if Id ~= LocalPlayer.UserId then
			Folder.Parent = MasterFolder
		end
	end
end

function ModuleFunctions:ShowAllPets()
	assert(IsServer, 'This function can only be called from the client')

	Settings.AllPetsVisible = true

	for Id, Folder in pairs(ReplicatedStorageFolder:GetChildren()) do
		Folder.Parent = MasterFolder
	end
end

function ModuleFunctions:AddPet(Player, Pet, Key)
	assert(not IsServer, 'This function can only be called from the server')

	Key = Key and Key or Pet.Name

	local ClonedPet = Pet:Clone()
	ClonedPet.Name = Key

	SetPetCollisions(ClonedPet)

	ClonedPet.Parent = MasterFolder[Player.Name]
end

function ModuleFunctions:RemovePet(Player, Key)
	assert(not IsServer, 'This function can only be called from the server')

	local Pet = MasterFolder[Player.Name]:FindFirstChild(Key)

	if Pet then
		Pet:Destroy()
	end
end

function AddPlayer(Player)
	Folders[Player.UserId] = Instance.new("Folder")
	Folders[Player.UserId].Name = Player.Name
	Folders[Player.UserId].Parent = MasterFolder
end

function RemovePlayer(Player)
	MasterFolder[Player.Name]:Destroy()
end

game.Players.PlayerAdded:Connect(AddPlayer)
game.Players.PlayerRemoving:Connect(RemovePlayer)

for _, Player in pairs(game.Players:GetChildren()) do
	AddPlayer(Player)
end

RunService:BindToRenderStep('Update', 1, UpdatePetPositions)

setmetatable(PetFollowModule, {__index = ModuleFunctions})
return PetFollowModule
5 Likes

Thank you for this! I’m using this in one of my games now. What exactly did you fix? I’m too lazy to check lol

I optimized the code even more so the one I posted is outdated too but I fixed the pet position function sometimes not working.

1 Like

I used your script but it still won’t work; I’m having the issue of the pet clone being parented to the Player’s folder, but the pet still won’t move/follow the player at all. If you could, can you post the updated script?

1 Like

You can get my Working Updated Scripts here:

2 Likes

maybe i am stupid but i don’t understand how to use this

do you perhaps know why the pets take so much before starting to follow me?

I am kinda confused on how to use this, It seems great though. I am unsure on how to use the functions (such as AddPet(), etc.)

Can we see a tutorial on how to use this? Can the pets be seen by all players? It is unclear how we can use your model currently.

Yes it can be seen by all players.

Can I see an example of how the different functions would work? I’ve tried to use the add pet and the pets wouldn’t move. An example would be great

Fixed the issue where pets won’t show on other players at sometimes.
Here is the fixed one.

Use PetModule:AddPet(player, petName) to spawn pet to the specified player. Using PetModule:AddPet(PetName) will add it to localPlayer just like the author said.

local PetFollowModule = {}
local ModuleFunctions = {}
local ActivePets = {}

local Settings = require(script.Settings)
local LocalPlayer = game.Players.LocalPlayer

local YPoint = 0
local Addition = Settings.YDriftSpeed
local FullCircle = 2 * math.pi

local RunService = game:GetService("RunService")

function SetPetCollisions(Model)
	for _, Part in pairs(Model:GetDescendants()) do
		if Part:IsA("BasePart") then
			Part.CanCollide = false
			Part.Anchored = true
		end
	end
end

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

function ModuleFunctions:AddPet(Player, PetName)
	print(1)
	if not PetName then
		PetName = Player 
		Player = LocalPlayer 
	end
	
	print(1)
	local PetModel = Settings.PetFolder[PetName]:Clone()
	PetModel.Parent = ActivePets[Player.UserId]
	print(1)
	if Player.Character then PetModel:SetPrimaryPartCFrame(Player.Character.PrimaryPart.CFrame) end
	print(1)
	SetPetCollisions(PetModel)
	print(1)
	return PetModel
end

function UpdatePetPositions()
	YPoint = YPoint + Addition
	if YPoint > Settings.YDrift or YPoint < -1 * Settings.YDrift then Addition = -1 * Addition end 
	
	for Id, Folder in pairs(ActivePets) do
		if Folder.Parent == game.Workspace then
			local Player = game.Players:GetPlayerByUserId(Id)
			local Character = Player.Character
			
			if Character  then
				local PetTable = Folder:GetChildren()
				local PetTableLength = #PetTable
				local Count = 0
				
				for _, Pet in pairs(PetTable) do
					Count += 1

					local Angle = Count * (FullCircle / PetTableLength)
					local X, Z = getXAndZPositions(Angle, Settings.Radius + (PetTableLength / 2))
					local Position = (Character.PrimaryPart.CFrame * CFrame.new(X, YPoint, Z)).p
					local LookAt = Character.PrimaryPart.Position
					local TargetCFrame = CFrame.new(Position, LookAt)
								
					local NewCFrame = Pet.PrimaryPart.CFrame:Lerp(TargetCFrame, Settings.Responsivness)
					Pet:SetPrimaryPartCFrame(NewCFrame)
				end
			end
		end
	end
end

function ModuleFunctions:RemovePet(Player, PetId)
	if not PetId then PetId = Player Player = LocalPlayer end
	ActivePets[Player.UserId][PetId]:Destroy()
end

function ModuleFunctions:HidePets(Player)
	ActivePets[Player.UserId].Parent = game.ReplicatedStorage
end

function ModuleFunctions:HideOthersPets()
	Settings.OtherPetsVisible = false
	
	for Id, Folder in pairs(ActivePets) do
		if Id ~= LocalPlayer.UserId then
			Folder.Parent = game.ReplicatedStorage
		end
	end
end

function ModuleFunctions:HideAllPets()
	Settings.AllPetsVisible = false
	
	for Id, Folder in pairs(ActivePets) do
		Folder.Parent = game.ReplicatedStorage
	end
end

function ModuleFunctions:ShowPets(Player)
	ActivePets[Player.UserId].Parent = game.Workspace
end

function ModuleFunctions:ShowOthersPets()
	Settings.OtherPetsVisible = true
	
	for Id, Folder in pairs(ActivePets) do
		if Id ~= LocalPlayer.UserId then
			Folder.Parent = game.Workspace
		end
	end
end

function ModuleFunctions:ShowAllPets()
	Settings.AllPetsVisible = true
	
	for Id, Folder in pairs(ActivePets) do
		Folder.Parent = game.Workspace
	end
end

function AddPlayer(Player)
	if not ActivePets[Player.UserId] then
		print(Player)
		ActivePets[Player.UserId] = Instance.new("Folder")
		ActivePets[Player.UserId].Name = Player.Name
		

		if Settings.OtherPetsVisible and Settings.AllPetsVisible then
			ActivePets[Player.UserId].Parent = game.Workspace
		else
			ActivePets[Player.UserId].Parent = game.ReplicatedStorage
		end
	end
	
end

game.Players.PlayerAdded:Connect(AddPlayer)
for i,pl in pairs(game.Players:GetPlayers()) do
	AddPlayer(pl)
end

game.Players.PlayerRemoving:Connect(function(Player)
	ActivePets[Player.UserId]:Destroy()
end)

RunService:BindToRenderStep('Update', 1, UpdatePetPositions)

setmetatable(PetFollowModule, {__index = ModuleFunctions})
return PetFollowModule

Hi so is this still currently working and also could you give me an example on how to use this

Yes it does work. Read the documentation

could you give me an example code