Choppy NPC ragdolls on the server?

Hello! My game has NPCs (every limb’s NetworkOwner is set to nil) that you can run into to ragdoll.

However, the NPC ragdolls appear choppy, while the players’ appears fine. I tried removing the code where it sets the NetworkOwners to nil, but to no avail.


(In the video, you can see the NPCs ragdoll when they trip over me.)

Ragdoll module, if it helps
local module = {}

local Players = game:GetService("Players")
local Debris = game:GetService("Debris")
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local PhysicsService = game:GetService("PhysicsService")

local Modules = ReplicatedStorage.Modules
local ImpactSoundModule = require(Modules.ImpactSoundModule)

local ragParts = script.RagdollParts:GetChildren()

local sounds = script.Sounds:GetChildren()
local slip = script.Slip

local defaultMinZoomDistance = game.StarterPlayer.CameraMinZoomDistance

local function playSound(part, s, vol)
	local sound = s:Clone()
	
	sound.Parent = part
	sound.Volume = vol or 0.5
	sound:Play()

	task.wait(0.1)
	Debris:AddItem(sound, sound.TimeLength + 0.1)
end

local function playRandomSound(char)
	local sound = sounds[math.random(1, #sounds)]
	local head = char and char:FindFirstChild("Head")
	
	if not head or not sound then return end

	playSound(head, slip)
	playSound(head, sound, 0.2)
end

local function createCollider(limb : BasePart) -- Not used
	if not limb then return end
	if limb.Name == "HumanoidRootPart" then return end
	
	local collider = Instance.new("Part")
	collider.CFrame = limb.CFrame
	collider.Massless = true
	collider.Size = limb.Size * 0.5
	collider.Transparency = 1
	
	local weld = Instance.new("WeldConstraint")
	weld.Part0 = collider
	weld.Part1 = limb
	weld.Parent = collider
	
	collider:AddTag("Collider")
	collider.Name = limb.Name .. " Collider"
	collider.Parent = limb.Parent
	
	return collider
end

function module:Setup(char : Model, isPlayer, canGrab)
	if not char.Humanoid then return end

	local humanoid = char:FindFirstChild("Humanoid")
	assert(humanoid, "Can only set-up ragdoll on R6 humanoid rigs")
	assert(humanoid.RigType == Enum.HumanoidRigType.R6, "Can only set-up ragdoll on R6 humanoid rigs")
	assert(humanoid.RootPart ~= nil, "No RootPart was found in the provided rig")
	assert(char:FindFirstChild("HumanoidRootPart"), "No HumanoidRootPart was found in the provided rig")

	if not isPlayer then
		for _, v: BasePart in ipairs(char:GetDescendants()) do
			if v:IsA("BasePart") then
				v:SetNetworkOwner(nil)
			end
		end
	end

	-- Setup ragdoll
	char.Head.Size = Vector3.new(1, 1, 1)
	humanoid.BreakJointsOnDeath = false
	humanoid.RequiresNeck = true
	
	char:SetAttribute("CanGrabWhenRagdolled", canGrab)

	local clones = {}
	for _, v in ipairs(ragParts) do
		clones[v.Name] = v:Clone()
	end

	local folder1 = Instance.new("Folder")
	folder1.Name = "RagdollConstraints"
	for _, v in pairs(clones) do
		if v:IsA("Attachment") then
			v.Parent = char[v:GetAttribute("Parent")]
		elseif v:IsA("BallSocketConstraint") then
			v.Attachment0 = clones[v:GetAttribute("0")]
			v.Attachment1 = clones[v:GetAttribute("1")]
			v.Parent = folder1
		end
	end
	folder1.Parent = char

	local folder2 = Instance.new("Folder")
	folder2.Name = "Motors"
	local value
	for _, v in ipairs(char.Torso:GetChildren()) do
		if v:IsA("Motor6D") then
			value = Instance.new("ObjectValue")
			value.Value = v
			value.Parent = folder2
		end
	end
	folder2.Parent = folder1

	-- Ragdoll trigger
	local trigger = Instance.new("BoolValue")
	trigger.Name = "RagdollTrigger"
	trigger.Parent = char

	trigger.Changed:Connect(function(bool)
		if bool then
			module:Ragdoll(char)
		else
			module:Unragdoll(char)
		end
	end)
	
	humanoid.Died:Once(function()
		trigger.Value = true
	end)
end

function module:SetRagdollTrigger(char: Model, value)
	local trigger = char:FindFirstChild("RagdollTrigger")
	
	if trigger then
		trigger.Value = value
	end
	
	return trigger
end

function module:Ragdoll(char: Model)
	if not char.Humanoid then return end
	
	char.Humanoid:ChangeState(Enum.HumanoidStateType.Physics)
	char.Humanoid:SetStateEnabled(Enum.HumanoidStateType.Seated, false)
	char.Humanoid.AutoRotate = false
	
	local player = Players:GetPlayerFromCharacter(char)
	if player then
		player.CameraMinZoomDistance = player.CameraMaxZoomDistance
		playRandomSound(char)
	end
	
	if char:GetAttribute("CanGrabWhenRagdolled") == true then
		char:AddTag("CanGrab")
	end
	
	for _, v in ipairs(char.RagdollConstraints.Motors:GetChildren()) do
		v.Value.Enabled = false
	end

	for _, v in ipairs(char:GetDescendants()) do
		if v:IsA("BasePart") then
			v.CollisionGroup = "Ragdoll"
		end
	end
	
	local hrp = char:FindFirstChild("HumanoidRootPart")
	if not hrp then return end
	
	local rootJoint = hrp:FindFirstChildOfClass("Motor6D")
	if rootJoint then
		task.spawn(function()
			rootJoint.Enabled = false
			task.wait()
			rootJoint.Enabled = true
		end)
	end
	
	ImpactSoundModule.Enable(char)
end

function module:Unragdoll(char: Model)
	if not char.Humanoid then return end
	
	char.Humanoid:ChangeState(Enum.HumanoidStateType.GettingUp)
	char.Humanoid:SetStateEnabled(Enum.HumanoidStateType.Seated, true)
	
	local player = Players:GetPlayerFromCharacter(char)
	if player then
		player.CameraMinZoomDistance = defaultMinZoomDistance
	end
	
	char:RemoveTag("CanGrab")
	
	for _, v in ipairs(char.RagdollConstraints.Motors:GetChildren()) do
		v.Value.Enabled = true
	end

	for _, v in ipairs(char:GetChildren()) do
		if v:IsA("BasePart") then
			v.CollisionGroup = player and "Players" or "Default"
			
			--[[if v:HasTag("Collider") then
				v:Destroy()
			end]]--
		end
	end
	
	ImpactSoundModule.Disable(char)
	char.Humanoid.AutoRotate = true
end

return module

How can I make the server and client ragdolls appear the same, nice and smooth?

1 Like

This isn’t a bug — it’s a networking / physics limitation.

NPC ragdolls look choppy because they’re being simulated by the server. Server-side physics run at a lower frequency, while player characters are simulated client-side, which is why player ragdolls look smooth.

Setting NetworkOwner to nil forces server ownership and actually makes the issue more noticeable. Removing the line won’t help if the server still owns the NPC.

To make NPC ragdolls smooth, you need to assign network ownership to a nearby client or handle ragdoll physics client-side while syncing state from the server.

2 Likes

i tried setting all the NPC limb’s network owners to the player who bumped into them, and the issue is still noticeable

i did absolutely nothing to the module but now they run smoothly (my avatar textures didn’t load in the video)

1 Like

This topic was automatically closed 14 days after the last reply. New replies are no longer allowed.