function Pets:FollowChar(Pet,PetOwner,Pos)
if not PetOwner then return end
local HumanoidRootPart = PetOwner:FindFirstChild("HumanoidRootPart")
local Bp = Pet:FindFirstChildOfClass("BodyPosition") or Instance.new("BodyPosition")
Bp.P = 3000
Bp.D = 250
Bp.Position = Pos
if not Pet:FindFirstChildOfClass("BodyPosition") then
Bp.Parent = Pet
end
print(Pet)
end
function Pets:GetPetPosition(Pet,PetOwner)
local HumanoidRootPart : BasePart = PetOwner:FindFirstChild("HumanoidRootPart")
local PetPositionCFrame = HumanoidRootPart.CFrame.LookVector - Vector3.new(0,0,5)
return PetPositionCFrame
end
I believe the issue is that the force is too small, the part is not massless and is also collidable which could make it have friction.
However, I really don’t recommend using physics at all for a pet system. Not only it can be exploited to fling players around, it is going to lag the server with many pets.
Could you provide the server script too so we can do a better system, such as moving the pet’s around the player?
yes, but the server is completely unfinished and i only have it for testing. so dont mind the bad code.
wait(3)
local petMod = require(game.ServerScriptService.Pets)
local pet = petMod:new(script.Parent,game.Players:FindFirstChildOfClass("Player").Character) -- i doing this because i wanted to test without making a whole script lol
game["Run Service"].Heartbeat:Connect(function()
local pos = petMod:GetPetPosition(pet.Pet,pet.PetOwner)
petMod:FollowChar(pet.Pet,pet.PetOwner,pos)
end)
My approach was using a system that automatically set’s the position of pet’s around the player. It’s not the best solution because it is server dependant but it can be developed further so player’s are the ones that render pets for themselves and other player’s reducing the strain on the server and removing the local delay.
Here’s the code (added a bunch of comments with AI so you understand what each part does)
I’ve also reformatted it so it’s more readable and usable.
Server script
local Pet = require(game.ServerScriptService.Pets) -- Require the Pets module.
-- Services
local Players = game:GetService("Players")
-- when a player joins, give him a test pet.
Players.PlayerAdded:Connect(function (player)
local playerPet = Pet.new(player) -- Give the player a pet when they join.
-- small test loop that gives a pet to the player every few seconds
task.spawn(function() -- Use task.spawn to run this in a new thread so it doesn't yield the PlayerAdded connection.
local lastWait = 5
while true do
local newPet = Pet.new(player) -- Give the player another pet.
print(Pet) -- Print the Pet module (for debugging).
task.wait(lastWait) -- Wait before giving another pet.
lastWait += 5 -- Increase the wait time.
end
end)
end)
– Module script
-- module
local Pets = {}
Pets.__index = Pets
-- variables
-- players and their owned pets with a type so we understand the structure.
-- This type definition helps with understanding the structure of serverPets.
type PlayerPetArray = { [Player]: {typeof(setmetatable({}, Pets))} }
-- serverPets stores a table of pets for each player. The keys are Player objects, and the values are arrays of pet objects.
local serverPets: PlayerPetArray = {}
-- This function creates a new entry in serverPets for a player if one doesn't exist.
local function createEntry(player: Player)
serverPets[player] = {} -- Initialize an empty table for the player's pets.
return {} -- Return an empty table. This isn't strictly necessary, but it mirrors how Pets.new works.
end
local methods = {} -- This table will hold the methods for the Pet objects.
function Pets.new(player: Player)
-- get the player's owned pets or create a new empty table of pets
-- If the player already has pets, retrieve them; otherwise, create a new entry for the player.
local theirPets = serverPets[player] or createEntry(player)
-- Create a new part to represent the pet.
local newPet = Instance.new("Part")
newPet.Anchored = true
newPet.CanCollide = false
newPet.Size = Vector3.new(2, 2, 2)
newPet.Name = `{player.Name}'s Pet ({#theirPets})` -- Give the pet a name based on the player and pet count.
newPet.Parent = workspace -- Parent the pet to the workspace.
table.insert(serverPets[player], newPet) -- Add the new pet's part to the player's pet array.
-- pet properties (example)
-- Create a new pet object using a metatable to give it methods.
local self = setmetatable({}, Pets)
self.petName = "john" -- Example pet name.
self.petRarity = "ultra rare glowy" -- Example pet rarity.
self.instance = newPet -- Store the pet's part instance.
return self -- Return the new pet object.
end
-- Method to move the pet.
function Pets:Move(newCFrame: CFrame)
local instance: Part = self.instance -- Get the pet's part instance.
instance.CFrame = newCFrame -- Set the pet's CFrame.
end
local RunService = game:GetService("RunService")
-- This function is called every heartbeat to update the pet positions.
local function onHeartbeat(deltaTime: number)
for player, petArray in pairs(serverPets) do -- Iterate through each player and their pets.
local character = player.Character -- Get the player's character.
if character then
local hrp = character:FindFirstChild("HumanoidRootPart") -- Get the HumanoidRootPart.
if hrp then
local baseCFrame = hrp.CFrame -- Get the HRP's CFrame.
local position = baseCFrame.Position -- Get the HRP's position.
local rightVec = baseCFrame.RightVector -- Get the HRP's right vector.
local petCount = #petArray -- Get the number of pets the player has.
for i, pet in ipairs(petArray) do -- Iterate through each of the player's pets.
local targetPos: Vector3 -- Declare the target position.
local offsetDistance = 5 -- Distance between the player and the pets.
if petCount == 2 then -- Special case for two pets: one on each side.
if i == 1 then
targetPos = position + rightVec * offsetDistance
else -- i == 2
targetPos = position - rightVec * offsetDistance
end
else -- For more than two pets, arrange them in a circle.
local angle = (2 * math.pi / petCount) * (i - 1) -- Calculate the angle for each pet.
local offset = Vector3.new(math.cos(angle) * offsetDistance, 0, math.sin(angle) * offsetDistance) -- Calculate the offset.
targetPos = position + baseCFrame:VectorToWorldSpace(offset) -- Apply the offset relative to the player's CFrame.
end
local targetCFrame = CFrame.new(targetPos) -- Create the target CFrame.
local lerpAlpha = math.clamp(10 * deltaTime, 0, 1) -- Calculate the interpolation alpha.
pet.CFrame = pet.CFrame:Lerp(targetCFrame, lerpAlpha) -- Smoothly move the pet to the target position.
end
end
end
end
end
RunService.Heartbeat:Connect(onHeartbeat) -- Connect the onHeartbeat function to RunService.Heartbeat. This makes the pets follow the player.
return Pets -- Return the Pets module.