Tutorial · Gameplay

How to make a pet that follows you in Roblox Studio

A pet that floats beside its owner is the heart of every simulator. The smooth way to build one is not a loop that teleports the pet every frame, which stutters, but two physics constraints that pull it toward a point next to the player. This guide builds that with one server script and any pet model, then adds a bob so it feels alive.

Updated 23 October 20266 min readIntermediateLuau

In short

Clone the pet model, make its parts unanchored, non-colliding and Massless, and parent it to the character. Put an Attachment in the pet and another in the player's HumanoidRootPart, offset to where the pet should float. An AlignPosition and an AlignOrientation between the two attachments pull the pet there. Then call petRoot:SetNetworkOwner(player), so the player's device moves the pet along with the character.

  • Massless and CanCollide off, or the pet drags the player or knocks them over.
  • Network ownership to the player removes the lag where the pet trails a step behind on the owner's screen.
  • Parent the pet to the character and it is cleaned up when the character respawns.

Get the script

Pet follow script with AlignPosition

One Script in ServerScriptService gives every player a copy of ServerStorage.Pet that floats beside them and follows smoothly.

Script in ServerScriptService named PetFollow
-- Script in ServerScriptService named PetFollow.
-- Gives every player a copy of ServerStorage.Pet that floats beside them.
local Players = game:GetService("Players")
local ServerStorage = game:GetService("ServerStorage")

local OFFSET = Vector3.new(3, 1, 2) -- studs right, up and behind the player
local RESPONSIVENESS = 15 -- higher follows more tightly, lower drifts behind more

local petTemplate = ServerStorage:WaitForChild("Pet") :: Model

local function givePet(player: Player, character: Model)
	local root = character:WaitForChild("HumanoidRootPart") :: BasePart
	local pet = petTemplate:Clone()
	local petRoot = pet.PrimaryPart
	if not petRoot then
		warn("PetFollow: set the PrimaryPart of ServerStorage.Pet")
		return
	end

	-- The pet must not push the player around or weigh them down
	for _, part in pet:GetDescendants() do
		if part:IsA("BasePart") then
			part.Anchored = false
			part.CanCollide = false
			part.Massless = true
		end
	end

	-- Two attachments: one in the pet, one beside the player where the pet should be
	local petAttachment = Instance.new("Attachment")
	petAttachment.Parent = petRoot
	local target = Instance.new("Attachment")
	target.Name = "PetTarget"
	target.Position = OFFSET
	target.Parent = root

	local align = Instance.new("AlignPosition")
	align.Attachment0 = petAttachment
	align.Attachment1 = target
	align.Responsiveness = RESPONSIVENESS
	align.MaxForce = 100000
	align.Parent = petRoot

	local turn = Instance.new("AlignOrientation")
	turn.Attachment0 = petAttachment
	turn.Attachment1 = target
	turn.Responsiveness = RESPONSIVENESS
	turn.MaxTorque = 100000
	turn.Parent = petRoot

	pet.Name = player.Name .. "Pet"
	pet:PivotTo(root.CFrame * CFrame.new(OFFSET))
	pet.Parent = character -- destroyed with the character on respawn
	-- The player's device already simulates their character, so let it move the pet too
	petRoot:SetNetworkOwner(player)
end

Players.PlayerAdded:Connect(function(player: Player)
	player.CharacterAdded:Connect(function(character: Model)
		givePet(player, character)
	end)
end)

RoCode builds it in the place you have open in Roblox Studio, on Windows or macOS with a desktop browser; on a phone the button sends the prompt and the link to your computer. Free to start, with free credits every day. More Roblox Studio scripts

Where to put it

Explorer: a pet model and one Script
ServerStorage
  Pet Model (PrimaryPart set)
ServerScriptService
  PetFollow Script new

Settings you can change

OFFSETIn the script
Where the pet floats: studs to the right, up, and behind the player. Default: Vector3.new(3, 1, 2).
RESPONSIVENESSIn the script
How tightly the pet follows. Higher snaps to its spot, lower drifts behind like it is floating. Default: 15.

The pet model needs its PrimaryPart set, and a pet made of several parts needs them welded together (WeldConstraints), or only the PrimaryPart follows.

Type-checked with luau-lsp strict against Roblox's API definitions on 23 October 2026. Not playtested in Studio yet: press Play and watch the Output window before you publish.

Prompt for RoCode

Give every player a pet that follows them: clone the model ServerStorage.Pet for each character, make its parts unanchored, massless and non-colliding, and use AlignPosition and AlignOrientation to keep it 3 studs to the right and 2 behind the player, with network ownership set to the player.

On this page
  1. Get the script
  2. How it works
  3. 1. Pet model
  4. 2. Follow script
  5. 3. Bob
  6. More pets
  7. Common errors
  8. With RoCode
  9. Questions
  10. Sources

How a following pet works

Two attachments mark two points: one inside the pet, and one on the player's root part, a few studs to the side. AlignPosition pushes the pet's attachment toward the player's, and AlignOrientation turns the pet to face the same way as the player. Because these are physics constraints, the pet accelerates and slows down smoothly, and it never needs a script running every frame.

Following by constraint or by teleport
ApproachHow it looks
AlignPosition and AlignOrientation (this guide)Smooth, with a slight float behind the player. No per-frame script.
Setting the pet's CFrame every frame on the serverJumps in steps on other screens, and costs server time for every pet.
A Weld to the playerPerfectly rigid, like a backpack. No floating at all.

The last piece is network ownership. By default the server simulates unanchored parts, and Roblox may hand a part to a nearby player's device. A player's own device always simulates their character. If the server simulated the pet, the owner would see it chase a character that is already a little ahead. Handing the pet to the same device as the character keeps them in step.

Step 1: Make the pet model

  1. Build or insert a small pet: a few parts, or a mesh from the Creator Store. Keep it about 2 studs across.
  2. Group it into a Model (select the parts, Ctrl+G) and name it Pet.
  3. Weld the parts together: for each part other than the main one, insert a WeldConstraint into it and set its Part0 to the main part and Part1 to that part.
  4. Select the Model and set its PrimaryPart to the main body part.
  5. Drag the Model into ServerStorage.

Step 2: Add the follow script

Insert a Script named PetFollow in ServerScriptService:

Script in ServerScriptService named PetFollow
-- Script in ServerScriptService named PetFollow.
-- Gives every player a copy of ServerStorage.Pet that floats beside them.
local Players = game:GetService("Players")
local ServerStorage = game:GetService("ServerStorage")

local OFFSET = Vector3.new(3, 1, 2) -- studs right, up and behind the player
local RESPONSIVENESS = 15 -- higher follows more tightly, lower drifts behind more

local petTemplate = ServerStorage:WaitForChild("Pet") :: Model

local function givePet(player: Player, character: Model)
	local root = character:WaitForChild("HumanoidRootPart") :: BasePart
	local pet = petTemplate:Clone()
	local petRoot = pet.PrimaryPart
	if not petRoot then
		warn("PetFollow: set the PrimaryPart of ServerStorage.Pet")
		return
	end

	-- The pet must not push the player around or weigh them down
	for _, part in pet:GetDescendants() do
		if part:IsA("BasePart") then
			part.Anchored = false
			part.CanCollide = false
			part.Massless = true
		end
	end

	-- Two attachments: one in the pet, one beside the player where the pet should be
	local petAttachment = Instance.new("Attachment")
	petAttachment.Parent = petRoot
	local target = Instance.new("Attachment")
	target.Name = "PetTarget"
	target.Position = OFFSET
	target.Parent = root

	local align = Instance.new("AlignPosition")
	align.Attachment0 = petAttachment
	align.Attachment1 = target
	align.Responsiveness = RESPONSIVENESS
	align.MaxForce = 100000
	align.Parent = petRoot

	local turn = Instance.new("AlignOrientation")
	turn.Attachment0 = petAttachment
	turn.Attachment1 = target
	turn.Responsiveness = RESPONSIVENESS
	turn.MaxTorque = 100000
	turn.Parent = petRoot

	pet.Name = player.Name .. "Pet"
	pet:PivotTo(root.CFrame * CFrame.new(OFFSET))
	pet.Parent = character -- destroyed with the character on respawn
	-- The player's device already simulates their character, so let it move the pet too
	petRoot:SetNetworkOwner(player)
end

Players.PlayerAdded:Connect(function(player: Player)
	player.CharacterAdded:Connect(function(character: Model)
		givePet(player, character)
	end)
end)

Each time a character spawns, the script clones the pet, makes every part unanchored, non-colliding and massless, and adds the two attachments and constraints. MaxForce and MaxTorque are high so the constraints can always win against gravity. The pet is placed at its spot with PivotTo before it is parented, so it does not fly in from the middle of the map.

SetNetworkOwner(player) has to be called from the server, after the pet is in the Workspace. Roblox's network ownership page describes it as the way to choose which device simulates a part.

Step 3: Make it bob

A pet that floats up and down looks alive. Since the owner's device simulates the pet, a LocalScript on that device can move the target attachment, and the constraint follows. Other players see the result. Add this LocalScript to StarterPlayerScripts:

LocalScript in StarterPlayerScripts named PetBob
-- LocalScript in StarterPlayerScripts named PetBob.
-- Makes the local player's pet bob up and down by moving the point it follows.
local Players = game:GetService("Players")
local RunService = game:GetService("RunService")

local BOB_HEIGHT = 0.5 -- studs up and down
local BOB_SPEED = 3 -- higher bobs faster (3 is about one bob every 2 seconds)

local player = Players.LocalPlayer
local baseY: number? = nil

RunService.RenderStepped:Connect(function()
	local character = player.Character
	local root = character and character:FindFirstChild("HumanoidRootPart")
	local target = root and root:FindFirstChild("PetTarget")
	if not (target and target:IsA("Attachment")) then
		baseY = nil
		return
	end
	local y = baseY or target.Position.Y
	baseY = y
	local p = target.Position
	target.Position = Vector3.new(p.X, y + math.sin(os.clock() * BOB_SPEED) * BOB_HEIGHT, p.Z)
end)

The attachment's new position stays on the owner's device, but because that device runs the pet's physics, the pet's movement reaches everyone.

Equipping, several pets and pets from eggs

For several pets, give each its own target attachment with a different OFFSET, for example in a circle around the player. For pets the player owns, keep the owned pet names in a folder on the player and clone the matching model from a Pets folder in ServerStorage. The egg hatching guide fills that folder, and the inventory guide shows how to equip and save items.

Test it

  1. Press Play. A pet appears beside you and follows as you walk, jump and turn.
  2. Reset your character: a fresh pet appears with the new character.
  3. Start a 2-player test and watch the other player's pet from your screen: it should move smoothly too.

Common errors and fixes

Watch the Output window for warnings from PetFollow.

PetFollow: set the PrimaryPart of ServerStorage.Pet

Select the Pet model, click PrimaryPart in the Properties window and pick its main part.

Only one part of the pet follows

The parts are not welded together. Add WeldConstraints between them.

The player walks slowly or falls over

A part of the pet is still colliding or has mass. The script sets CanCollide off and Massless on for every BasePart; check nothing else turns them back on.

The pet falls to the ground

The constraints are not attached: check the pet has a PrimaryPart and that nothing deletes the attachments. A very heavy mesh may also need a higher MaxForce.

Build it with RoCode

If you would rather not set this up by hand, RoCode can build it in the place you have open. RoCode is an independent AI agent for Roblox Studio, not made by Roblox: you describe the change in a web chat, and its Studio plugin creates the model and scripts in your Explorer.

You type

Make a small floating dog pet that follows every player 3 studs to their right, bobs up and down, and is replaced when they respawn.

RoCode does
  • Builds a simple pet model from parts, or uses a model you already have
  • Writes the follow script and the bob script, and is told to compile-check each with Check Script
  • Sets undo points around each batch of changes

For a pet game, RoCode can also insert a ready-made pet inventory and egg hatching screen from its premium UI kits in one step, in five themes (candy, neon, gold, blocky, pastel), with working server scripts. After a build its test robot can walk around in Studio and report PASS or FAIL, but it checks only what it chose to test. How RoCode connects to Studio.

Questions

How do I make a pet follow me in Roblox Studio?

Use AlignPosition and AlignOrientation between an attachment in the pet and one beside the player's HumanoidRootPart, and give the player network ownership of the pet. Step 2 has the full script.

Why does my pet lag behind?

The server is simulating it while the player's device simulates the character. Call SetNetworkOwner(player) on the pet's main part from the server.

Why does my pet push me around?

Its parts collide or have mass. Set CanCollide to false and Massless to true on every part.

Should I use BodyPosition for a pet?

No. Roblox marks BodyPosition deprecated and says not to use it for new work. AlignPosition does the same job.

Sources and further reading

How the code was checked: every script on this page passes Luau's strict type checker against Roblox's API definitions (luau-lsp, 23 October 2026). A type check catches misspelt APIs and wrong types, not game logic, so playtest in Studio before you publish.

Try RoCode on your own place.

Free every day, no card required. Install the plugin, describe the feature, and review what lands in Studio.