Humnoid takes ages to load

I am currently making a Pet System with Rigs that contains a Humanoid, the Pets are getting moved with MoveTo(). If i add a Pet to the Pet Folder over the Client everything is fine. But if i add one over the Server the pet takes ages to Load? not like 1 sec more like 50 Seconds. i tried to remove every asset and use a fresh new generatet Rig still the same problem. Tryed to use Attachment and AlignPosition istead of MoveTo(). Same thing it takes ages to load.

Video:
Video Streamable

my Current Move Script

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

local player = Players.LocalPlayer
local FOLLOW_DISTANCE = 6
local WALK_SPEED = 16
local RADIUS = 5
local RADIUS_STEP = 3
local ARC_DEGREES = 90
local UPDATE_INTERVAL = 0.05
local WORKERS_PER_RING = 4
local MAX_DISTANCE = 50
local WORKER_FOLDER_NAME = "PlayerWorkers"

local humanoidRootPart
local workerFolder = workspace:WaitForChild(WORKER_FOLDER_NAME)
local playerWorkerFolder = workerFolder:WaitForChild(player.Name)
local activeWorkers = {}
local lastUpdate = 0

local function positionWorkerBehindPlayer(worker)
	local rootPart = worker:WaitForChild("HumanoidRootPart")
	if humanoidRootPart and rootPart then
		local behindPlayer = humanoidRootPart.CFrame.Position - humanoidRootPart.CFrame.LookVector * FOLLOW_DISTANCE
		rootPart.CFrame = CFrame.new(behindPlayer, humanoidRootPart.Position)
	end
end

local function refreshActiveWorkers()
	activeWorkers = {}
	for _, worker in ipairs(playerWorkerFolder:GetChildren()) do
		local humanoid = worker:FindFirstChildOfClass("Humanoid")
		if humanoid then
			humanoid.WalkSpeed = WALK_SPEED
			table.insert(activeWorkers, worker)
			positionWorkerBehindPlayer(worker)
		end
	end
end

local function getWorkerRingPositions(count)
	local positions = {}
	local totalRings = math.ceil(count / WORKERS_PER_RING)

	for ring = 1, totalRings do
		local ringRadius = RADIUS + (ring - 1) * RADIUS_STEP
		local workersInRing = math.min(WORKERS_PER_RING, count - #positions)
		local angleStep = math.rad(ARC_DEGREES / math.max(workersInRing - 1, 1))
		local baseAngle = math.rad(-ARC_DEGREES / 2)

		for i = 0, workersInRing - 1 do
			local angle = baseAngle + (i * angleStep)
			local x = math.sin(angle) * ringRadius
			local z = math.cos(angle) * ringRadius - FOLLOW_DISTANCE
			table.insert(positions, Vector3.new(x, 0, z))
		end
	end

	return positions
end

local function getOreRingPositions(count, radius)
	radius = radius or 6
	local positions = {}

	for i = 1, count do
		local angle = ((i - 1) / count) * 2 * math.pi
		local x = math.cos(angle) * radius
		local z = math.sin(angle) * radius
		table.insert(positions, Vector3.new(x, 0, z))
	end

	return positions
end

local function updateWorkerPositions()
	if not humanoidRootPart or not humanoidRootPart.Parent then return end

	local positions = getWorkerRingPositions(#activeWorkers)
	local orePositions = getOreRingPositions(#activeWorkers)

	for i, worker in ipairs(activeWorkers) do
		if worker and worker.Parent then
			local humanoid = worker:FindFirstChildOfClass("Humanoid")
			local rootPart = worker:FindFirstChild("HumanoidRootPart") or worker:FindFirstChild("Torso")
			local targetOre = worker:FindFirstChild("TargetOre")

			if humanoid and rootPart then
				if targetOre and targetOre.Value and targetOre.Value:IsA("Model") and targetOre.Value.PrimaryPart then
					local center = targetOre.Value.PrimaryPart.Position
					local offset = orePositions[i]
					local targetPos = center + offset
					local lookDirection = (center - rootPart.Position).Unit
					local lookCFrame = CFrame.new(rootPart.Position, rootPart.Position + lookDirection)
					rootPart.CFrame = CFrame.new(rootPart.Position) * CFrame.Angles(0, lookCFrame.LookVector:Angle(Vector3.new(0, 0, -1)), 0)
					humanoid:MoveTo(targetPos)
				else
					local offset = humanoidRootPart.CFrame.LookVector * -FOLLOW_DISTANCE
					local targetPos = humanoidRootPart.Position + offset + humanoidRootPart.CFrame:VectorToWorldSpace(positions[i])
					local distance = (rootPart.Position - targetPos).Magnitude

					if distance > MAX_DISTANCE then
						rootPart.CFrame = CFrame.new(targetPos)
					else
						humanoid:MoveTo(targetPos)
					end
				end
			end
		end
	end
end

local function startWorkerUpdateLoop()
	RunService.Heartbeat:Connect(function(dt)
		lastUpdate += dt
		if lastUpdate >= UPDATE_INTERVAL then
			lastUpdate = 0
			updateWorkerPositions()
		end
	end)
end

local function onWorkerAdded(worker)
	print("[WorkerSystem] New worker added: ", worker.Name)
	task.spawn(function()
		local humanoid = worker:WaitForChild("Humanoid")
		local rootPart = worker:WaitForChild("HumanoidRootPart")

		if humanoid and rootPart then
			humanoid.WalkSpeed = WALK_SPEED
			table.insert(activeWorkers, worker)
			positionWorkerBehindPlayer(worker)
			refreshActiveWorkers()
		end
	end)
end

local function init()
	local character = player.Character or player.CharacterAdded:Wait()
	humanoidRootPart = character:WaitForChild("HumanoidRootPart")

	refreshActiveWorkers()
	startWorkerUpdateLoop()

	playerWorkerFolder.ChildAdded:Connect(onWorkerAdded)

	player.CharacterAdded:Connect(function(newChar)
		character = newChar
		humanoidRootPart = character:WaitForChild("HumanoidRootPart")
		refreshActiveWorkers()
	end)
end

init()
2 Likes

I believe at some point you should grab the Worker character and apply this code to it:

for _, v in workerCharacter:GetChildren() do
    if not v:IsA("BasePart") then continue end
    v:SetNetworkOwner()
end

This makes it so the server gains ownership over the workerCharacter so that it runs smoother.

Isnt the server already the owner because it get cloned from the ReplicatedStorage?

So i tried it now before and after cloning and it did change nothing

1 Like

Not entirely sure, though I haven’t necessarily tested it. In all cases of me cloning a Humanoid character from ReplicatedStorage, I’ve had to apply NetworkOwnership to the server to get it running smoothly.

so i change it before cloning looking something like this

task.wait(10)
local clone = game.ReplicatedStorage.Rig:Clone()
clone.Parent = workspace.Pet.AlphaBjarne
for _, v in clone:GetChildren() do
	if not v:IsA("BasePart") then continue end
	v:SetNetworkOwner()
end
print(clone:GetPivot())

and it did not help with the problem

To be honest, its a bad idea to use humanoids, it can cause performance issues with many of them. I think you can use CFrame lerp instead

yea but that doesn´t look as good as MoveTo:() i try to make it look like this game:
“Arise Crossover - Roblox”
i dont think they use Lerp

Clone the pet, set its PrimaryPart and important parts, then assign Parent IMMEDIATELY.
You actually don’t want to set NetworkOwner at this time. You can after it’s spawned and there.
Ensure pets spawn within streaming range if streaming.

In fact, pre-set the PrimaryPart manually on the model you’re cloning.

what do you mean by “assign Parent IMMEDIATELY”

Like try not to pause between… task.wait, a lot setting up. Clone it and sent it asap.

The task.wait is just for delaying the Script. It hase nothing to do with the function

I see that and wasn’t talking about that task.wait. Just general good practice when cloning off the server. Set it up as you will but after the clone sent it. There is a good reason for this…

I am confused. i dont get what you telling me.

Well, the whole explanation is even more complicated. If you truly are just looking for why, do some research on it. How is “Just general good practice when cloning” confusing…