Torso on other screens appears to separate from limbs when Rag-dolling

Hi all, I am having some problems with this rag-doll system for one of my games. This module is fired on the server, and humanoid states are also changed on the client.

The rag-doll system performs smoothly on the clients screen, but when viewing another player, their limbs lag as they fall with their torso appearing to “float”.

Does anybody have any ideas on how to mitigate this, or is this just a byproduct of networking that cannot be avoided when using this method?

P.S. Massless on any of the collision acts exactly the same and does not fix this.

Thanks!

local Ragdoll = {}

local Players = game:GetService("Players")

local AttachmentCFrames = {
	Neck = {CFrame.new(0, 1, 0) * CFrame.Angles(0, math.rad(-90), 0), CFrame.new(0, -0.5, 0) * CFrame.Angles(0, math.rad(-90), 0)},
	["Left Shoulder"] = {CFrame.new(-1.3, 0.75, 0), CFrame.new(0.2, 0.75, 0)},
	["Right Shoulder"] = {CFrame.new(1.3, 0.75, 0), CFrame.new(-0.2, 0.75, 0)},
	["Left Hip"] = {CFrame.new(-0.5, -1, 0), CFrame.new(0, 1, 0)},
	["Right Hip"] = {CFrame.new(0.5, -1, 0), CFrame.new(0, 1, 0)},
}

local function createCollider(part)
	local collider = Instance.new("Part")
	collider.Size = part.Size * 0.6
	collider.CFrame = part.CFrame
	collider.Transparency = 1
	collider.CanCollide = true
	collider.Massless = false
	collider.Anchored = false
	collider.Name = "ColliderPart"
	collider.Parent = part

	local weld = Instance.new("WeldConstraint")
	weld.Part0 = collider
	weld.Part1 = part
	weld.Parent = collider
end

function Ragdoll.Rag(character, humanoid)
	if not (character and humanoid and character:FindFirstChild("HumanoidRootPart")) then return end
	humanoid.PlatformStand = true
	humanoid.AutoRotate = false
	character.HumanoidRootPart.CanCollide = false
	humanoid:SetStateEnabled(Enum.HumanoidStateType.Physics, true)
	humanoid:SetStateEnabled(Enum.HumanoidStateType.Ragdoll, false)
	humanoid:SetStateEnabled(Enum.HumanoidStateType.Climbing, false)
	humanoid:SetStateEnabled(Enum.HumanoidStateType.Swimming, false)
	humanoid:SetStateEnabled(Enum.HumanoidStateType.FallingDown, true)
	humanoid:SetStateEnabled(Enum.HumanoidStateType.GettingUp, false)
	humanoid:ChangeState(Enum.HumanoidStateType.FallingDown)
	

	for _, motor in character:GetDescendants() do
		if motor:IsA("Motor6D") then
			local cf = AttachmentCFrames[motor.Name]
			if cf then
				motor.Enabled = false

				local a0 = Instance.new("Attachment")
				local a1 = Instance.new("Attachment")
				a0.CFrame = cf[1]
				a1.CFrame = cf[2]
				a0.Parent = motor.Part0
				a1.Parent = motor.Part1

				createCollider(motor.Part1)

				local socket = Instance.new("BallSocketConstraint")
				socket.Attachment0 = a0
				socket.Attachment1 = a1
				socket.Radius = 0.15
				socket.LimitsEnabled = true
				socket.MaxFrictionTorque = 0
				socket.Restitution = 0
				if motor.Name == "Neck" then
					socket.TwistLimitsEnabled = true
					socket.UpperAngle = 45
					socket.TwistLowerAngle = -70
					socket.TwistUpperAngle = 70
				else
					socket.TwistLimitsEnabled = false
					socket.UpperAngle = 90
					socket.TwistLowerAngle = -45
					socket.TwistUpperAngle = 45
				end
				socket.Parent = motor.Parent
			end
		end
	end
	
	character:SetAttribute("Ragdoll", true)
	character:SetAttribute("LastRagdoll", os.clock())
end

function Ragdoll.Fix(humanoid)
	
	
	local character = humanoid.Parent
	if not (character and humanoid and character:FindFirstChild("HumanoidRootPart")) then return end
	humanoid.PlatformStand = false
	humanoid.AutoRotate = true
	
	--humanoid.EvaluateStateMachine = true
	
	humanoid:SetStateEnabled(Enum.HumanoidStateType.Physics, false)
	humanoid:SetStateEnabled(Enum.HumanoidStateType.Ragdoll, false)
	humanoid:SetStateEnabled(Enum.HumanoidStateType.Climbing, false)
	humanoid:SetStateEnabled(Enum.HumanoidStateType.Swimming, false)
	humanoid:SetStateEnabled(Enum.HumanoidStateType.FallingDown, false)
	humanoid:ChangeState(Enum.HumanoidStateType.GettingUp)

	character.HumanoidRootPart.CanCollide = true

	if humanoid.Health <= 0 then return end

	for _, obj in character:GetDescendants() do
		if obj:IsA("Attachment") or obj:IsA("BallSocketConstraint") or obj.Name == "ColliderPart" then
			obj:Destroy()
		elseif obj:IsA("Motor6D") then
			obj.Enabled = true
		end
	end

	character:SetAttribute("Ragdoll", false)
end

return Ragdoll

Heres a video:

1 Like

iirc, there’s actually multiple games that have this exact problem.

That said, here’s how I might solve this problem:

  1. Create a clone of the character, store it somewhere.
  2. Have the client ragdoll, but have the server created clone be the one shown to everyone.
  3. Remove the clone and replace it with the player’s character when everything is done.