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.
-- 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
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
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.
| Approach | How 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 server | Jumps in steps on other screens, and costs server time for every pet. |
| A Weld to the player | Perfectly 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
- Build or insert a small pet: a few parts, or a mesh from the Creator Store. Keep it about 2 studs across.
- Group it into a Model (select the parts, Ctrl+G) and name it
Pet. - 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.
- Select the Model and set its PrimaryPart to the main body part.
- Drag the Model into ServerStorage.
Step 2: Add the follow script
Insert a Script named PetFollow in ServerScriptService:
-- 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.
-- 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
- Press Play. A pet appears beside you and follows as you walk, jump and turn.
- Reset your character: a fresh pet appears with the new character.
- 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.
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.
- 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
- Roblox Creator Docs: AlignPosition. attachments, Responsiveness and MaxForce
- Roblox Creator Docs: AlignOrientation. matching the player's facing
- Roblox Creator Docs: Network ownership. who simulates a part and SetNetworkOwner
- Roblox Creator Docs: BasePart. Massless and CanCollide
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.