Ragdoll "explodes" at high velocitys

You can write your topic however you want, but you need to answer these questions:

  1. What do you want to achieve? Keep it simple and clear!

i just want a nice good ragdoll that works properly when being hit at any velocity

  1. What is the issue? Include screenshots / videos if possible!

as said in title, they “explode”, aka tend to seperate really far from the torso, instead of staying together as intended

  1. What solutions have you tried so far? Did you look for solutions on the Developer Hub?

theres only 2 posts on the forum, neither helping.
ive tried many ragdoll systems, but they all have the same issue.
ive also tried using rigid constraints to straighten them out, but that tends to fling the ragdoll.

After that, you should include more details if you have any. Try to make your topic as descriptive as possible, so that it’s easier for people to help you!

the code for my ragdoll module:

local module = {}

local attachmentCFrames = {
	["Neck"] = {CFrame.new(0, 1, 0, 0, -1, 0, 1, 0, -0, 0, 0, 1), CFrame.new(0, -0.5, 0, 0, -1, 0, 1, 0, -0, 0, 0, 1)},
	["Left Shoulder"] = {CFrame.new(-1.3, 0.75, 0, -1, 0, 0, 0, -1, 0, 0, 0, 1), CFrame.new(0.2, 0.75, 0, -1, 0, 0, 0, -1, 0, 0, 0, 1)},
	["Right Shoulder"] = {CFrame.new(1.3, 0.75, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1), CFrame.new(-0.2, 0.75, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1)},
	["Left Hip"] = {CFrame.new(-0.5, -1, 0, 0, 1, -0, -1, 0, 0, 0, 0, 1), CFrame.new(0, 1, 0, 0, 1, -0, -1, 0, 0, 0, 0, 1)},
	["Right Hip"] = {CFrame.new(0.5, -1, 0, 0, 1, -0, -1, 0, 0, 0, 0, 1), CFrame.new(0, 1, 0, 0, 1, -0, -1, 0, 0, 0, 0, 1)},
}

local ragdollInstanceNames = {
	["RagdollAttachment"] = true,
	["RagdollConstraint"] = true,
	["ColliderPart"] = true,
}
local function createColliderPart(part: BasePart)
	if not part then return end
	local rp = Instance.new("Part")
	rp.Name = "ColliderPart"
	rp.Size = part.Size/1.7
	rp.Massless = true			
	rp.CFrame = part.CFrame
	rp.Transparency = 1

	local wc = Instance.new("WeldConstraint")
	wc.Part0 = rp
	wc.Part1 = part

	wc.Parent = rp
	rp.Parent = part
end

function replaceJoints(Character,Humanoid)
	for _, motor: Motor6D in pairs(Character:GetDescendants()) do
		if motor:IsA("Motor6D") then
			if not attachmentCFrames[motor.Name] then return end
			motor.Enabled = false;
			local a0, a1 = Instance.new("Attachment"), Instance.new("Attachment")
			a0.CFrame = attachmentCFrames[motor.Name][1]
			a1.CFrame = attachmentCFrames[motor.Name][2]

			a0.Name = "RagdollAttachment"
			a1.Name = "RagdollAttachment"

			createColliderPart(motor.Part1)

			local b = Instance.new("BallSocketConstraint")
			b.Attachment0 = a0
			b.Attachment1 = a1
			b.Name = "RagdollConstraint"

			b.Radius = 0.15
			b.LimitsEnabled = true
			b.TwistLimitsEnabled = false
			b.MaxFrictionTorque = 9999
			b.Restitution = 0
			b.UpperAngle = 90
			b.TwistLowerAngle = -45
			b.TwistUpperAngle = 45

			if motor.Name == "Neck" then
				b.TwistLimitsEnabled = true
				b.UpperAngle = 45
				b.TwistLowerAngle = -70
				b.TwistUpperAngle = 70
			end

			a0.Parent = motor.Part0
			a1.Parent = motor.Part1
			b.Parent = motor.Parent
		elseif motor:IsA("BasePart") then
			--motor:SetNetworkOwner(nil)
		end
	end
	if Humanoid.Health > 0 then 
		Humanoid:ChangeState(Enum.HumanoidStateType.Ragdoll)
	end
	Humanoid.AutoRotate = false 
	Humanoid.JumpPower = 0
	Character:SetAttribute("LastRag", tick())
end


function resetJoints(Character,Humanoid)

	Humanoid.PlatformStand = false
	Humanoid.AutoRotate = true
	if Humanoid.Health > 0 then 
		Humanoid:ChangeState(Enum.HumanoidStateType.GettingUp)
	end

	if Humanoid.JumpPower == 0 then
		Humanoid.JumpPower = Character.PastJP.Value
	end

	if Character:FindFirstChild("Ragdolled") then
		Character:FindFirstChild("Ragdolled"):Destroy()

		for i,v in pairs(Character:GetChildren()) do
			if v:IsA("Folder") and v.Name == "Ragdolled" then
				v:Destroy() 
			end
		end
	end
	for _, instance in pairs(Character:GetDescendants()) do
		if ragdollInstanceNames[instance.Name] then
			instance:Destroy()
		end

		if instance:IsA("Motor6D") then
			instance.Enabled = true;
		elseif instance and instance:IsDescendantOf(workspace) and instance:IsA("BasePart") then
			if game.Players:GetPlayerFromCharacter(Character) then
				pcall(function()
					--instance:SetNetworkOwner(game.Players:GetPlayerFromCharacter(Character))
				end)
			end
		end
	end
end

function Ragdoll(value: boolean,Character,Humanoid)
	if value then replaceJoints(Character,Humanoid) else resetJoints(Character,Humanoid) end
end


module.Ragdoll = function(character, length)
	if  character:FindFirstChild("Ragdolled") then
		module.Unragdoll(character)
		task.wait(0.004)
	end

	local ragdolled = Instance.new("Folder", character)
	ragdolled.Name = "Ragdolled"
	local key = math.random(1,99999999) * math.random(1,5) / math.random(2,9)
	local HolderKey = Instance.new("NumberValue",ragdolled)
	HolderKey.Name = "Key"
	HolderKey.Value = key
	character.Humanoid.PlatformStand = true
	character.Humanoid.RequiresNeck = false
	Ragdoll(true,character,character.Humanoid)


	task.spawn(function()
		if length ~= 0 or not length and length then
			if length > 999 then
				return
			end
			task.wait(length)
			module.Unragdoll(character,key)
		end
	end)
end

module.Unragdoll = function(character,key)
	if character~=nil and character:FindFirstChild("HumanoidRootPart") then
		if key ~= nil and character:FindFirstChild("Ragdolled") and character:FindFirstChild("Ragdolled").Key.Value ~= key then return end
		local vel = character:FindFirstChild("HumanoidRootPart").Velocity
		Ragdoll(false,character,character.Humanoid)
		character:FindFirstChild("HumanoidRootPart").Velocity = vel
	end
end

return module

please help as this is a MAJOR part of the game

2 Likes

Had a similar issue with ragdolls “exploding”
My ragdolls “exploded” much slower than the video linked since my computer couldn’t handle the extreme physics as well.

Turns out I didn’t disconnect a .Touched connection that was constantly being fired due to the ragdoll parts touching the part connected to the function.

I would check for any connections that may unintentionally be slowing down your game. In this case that would be the hand touching the NPC.

Sorry if this answer was vague since a lot of factors could be slowing down the physic calculations of your ragdoll.

.touched does disconnect, ive made sure of that or else it would cause alot of problems

Another thing I did was make a collision group that couldn’t collide with itself and assign it to all the limbs which solves the twitching problem that some ragdolls have.

i have no problem with twitching, its just the ragdolls tending to randomly fling
with the very bare bones fix i did it made it more uncommon, but it more or else is still a big issue

I think the issue is to do with the method of flinging the ragdoll itself. Could you describe how a force/velocity is applied to the ragdoll? (Ex: BodyVelocity, LinearVelocity, Cframe tweening)

it uses a bodyvelocity, and sorry for taking so long to respond, i dont get notifs here for some reason

BodyVelocity is depreciated, I would recommend using linear velocity or vector force. This may not solve the issue but BodyVelocity has be depreciated for almost 3 years which may have incurred some issues since then.

ive tried switching before and there is no change, other then havign the recode my game for it to work

I think that the velocity being applied should have a cap to it because I think roblox struggles to calculate physics at such a powerful force. Also in the video it looks like you hit the dummy with 1 trillion “force” which probably could be lowered down to 1 billion since the amount of force you use seems excessive.

1 Like

it still happens with lower velocities too, jsut alot rarer

This seems like a network ownership issue, make sure the victim player has their network ownership set to the server before the hit.

1 Like

setting it to the server causes massive issues with the character, witch i personally dont remember at the time. but i believe it was somethign to do with the character falling in slow motion

Here is a checklist you can do to fix:

  1. Disable self-collisions
    Put all character parts in a Ragdoll collision group and set that group to not collide with itself (or add NoCollisionConstraints between adjacent limbs & torso). Self-collision is the #1 cause of explosions.
  2. Use constraints with limits
    For each limb joint, use BallSocketConstraint/HingeConstraint with:
  • LimitsEnabled = true
  • Reasonable angles (e.g., shoulders 80–120°, elbows/knees 0–90°)
  • For BallSocket: TwistLimitsEnabled = true, TwistLowerAngle/UpperAngle ~ ±45°
    This prevents limbs from hyperextending and spiking forces.
  1. Tame physics properties
    Set low bounce, normal friction:
-- run once per limb part
part.CustomPhysicalProperties = PhysicalProperties.new(1, 0.7, 0, 1, 1)
-- density, friction, elasticity(0), frictionWeight, elasticityWeight

Also set HumanoidRootPart.CanCollide = false.
4. Freeze humanoid attempts to “stand”
When ragdolled:

hum:ChangeState(Enum.HumanoidStateType.Physics)
hum.PlatformStand = true
  1. Clamp crazy velocities (safety net)
    Add a tiny server script while ragdolled to cap both linear and angular speed:
-- ServerScriptService snippet (call startClamp(ragdollRoot) when you ragdoll)
local RunService = game:GetService("RunService")
local MAX_LIN, MAX_ANG = 80, 40  -- tune for your game

local function startClamp(rootPart: BasePart)
    local conn
    conn = RunService.Heartbeat:Connect(function()
        if not rootPart or not rootPart.Parent then
            if conn then conn:Disconnect() end
            return
        end
        local v = rootPart.AssemblyLinearVelocity
        if v.Magnitude > MAX_LIN then
            rootPart.AssemblyLinearVelocity = v.Unit * MAX_LIN
        end
        local w = rootPart.AssemblyAngularVelocity
        if w.Magnitude > MAX_ANG then
            rootPart.AssemblyAngularVelocity = w.Unit * MAX_ANG
        end
    end)
    return conn
end

Call conn = startClamp(character.HumanoidRootPart) when you enter ragdoll; conn:Disconnect() when you exit.

Why this helps

  • No self-collision removes most explosive impulses.
  • Joint limits keep poses physically solvable.
  • Low elasticity prevents bounce amplification.
  • Velocity clamp saves you from rare edge cases (very high-speed impacts).

Adjusted Script:

local module = {}

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

-- ensure collision group
local RAG_GROUP = "Ragdoll"
pcall(function() PhysicsService:CreateCollisionGroup(RAG_GROUP) end)
PhysicsService:CollisionGroupSetCollidable(RAG_GROUP, RAG_GROUP, false)

local attachmentCFrames = {
	["Neck"] = {CFrame.new(0, 1, 0, 0, -1, 0, 1, 0, -0, 0, 0, 1), CFrame.new(0, -0.5, 0, 0, -1, 0, 1, 0, -0, 0, 0, 1)},
	["Left Shoulder"] = {CFrame.new(-1.3, 0.75, 0, -1, 0, 0, 0, -1, 0, 0, 0, 1), CFrame.new(0.2, 0.75, 0, -1, 0, 0, 0, -1, 0, 0, 0, 1)},
	["Right Shoulder"] = {CFrame.new(1.3, 0.75, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1), CFrame.new(-0.2, 0.75, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1)},
	["Left Hip"] = {CFrame.new(-0.5, -1, 0, 0, 1, -0, -1, 0, 0, 0, 0, 1), CFrame.new(0, 1, 0, 0, 1, -0, -1, 0, 0, 0, 0, 1)},
	["Right Hip"] = {CFrame.new(0.5, -1, 0, 0, 1, -0, -1, 0, 0, 0, 0, 1), CFrame.new(0, 1, 0, 0, 1, -0, -1, 0, 0, 0, 0, 1)},
}

local ragdollInstanceNames = {
	["RagdollAttachment"] = true,
	["RagdollConstraint"] = true,
	["ColliderPart"] = true,
}

local clamps = {} -- per-character Heartbeat connections

local function createColliderPart(part: BasePart)
	if not part then return end
	local rp = Instance.new("Part")
	rp.Name = "ColliderPart"
	rp.Size = part.Size / 1.7
	rp.Massless = true
	rp.CFrame = part.CFrame
	rp.Transparency = 1
	rp.CanCollide = true
	PhysicsService:SetPartCollisionGroup(rp, RAG_GROUP)

	part:SetAttribute("_PrevCanCollide", part.CanCollide)
	part.CanCollide = false
	PhysicsService:SetPartCollisionGroup(part, RAG_GROUP)

	local wc = Instance.new("WeldConstraint")
	wc.Part0 = rp
	wc.Part1 = part
	wc.Parent = rp
	rp.Parent = part
end

local function startClamp(character: Model)
	local root: BasePart? = character:FindFirstChild("HumanoidRootPart")
	if not root then return end
	local MAX_LIN, MAX_ANG = 80, 40
	clamps[character] = RunService.Heartbeat:Connect(function()
		if not root.Parent then return end
		local v = root.AssemblyLinearVelocity
		if v.Magnitude > MAX_LIN then
			root.AssemblyLinearVelocity = v.Unit * MAX_LIN
		end
		local w = root.AssemblyAngularVelocity
		if w.Magnitude > MAX_ANG then
			root.AssemblyAngularVelocity = w.Unit * MAX_ANG
		end
	end)
end

local function stopClamp(character: Model)
	local c = clamps[character]
	if c then c:Disconnect() end
	clamps[character] = nil
end

local function replaceJoints(Character: Model, Humanoid: Humanoid)
	for _, inst in ipairs(Character:GetDescendants()) do
		if inst:IsA("BasePart") then
			PhysicsService:SetPartCollisionGroup(inst, RAG_GROUP)
		end
	end

	for _, motor in pairs(Character:GetDescendants()) do
		if motor:IsA("Motor6D") and attachmentCFrames[motor.Name] then
			motor.Enabled = false

			local a0, a1 = Instance.new("Attachment"), Instance.new("Attachment")
			a0.CFrame = attachmentCFrames[motor.Name][1]
			a1.CFrame = attachmentCFrames[motor.Name][2]
			a0.Name = "RagdollAttachment"
			a1.Name = "RagdollAttachment"

			createColliderPart(motor.Part1)

			local b = Instance.new("BallSocketConstraint")
			b.Attachment0 = a0
			b.Attachment1 = a1
			b.Name = "RagdollConstraint"
			b.Radius = 0.15
			b.LimitsEnabled = true
			b.TwistLimitsEnabled = false
			b.MaxFrictionTorque = 1500
			b.Restitution = 0
			b.UpperAngle = 90
			b.TwistLowerAngle = -45
			b.TwistUpperAngle = 45

			if motor.Name == "Neck" then
				b.TwistLimitsEnabled = true
				b.UpperAngle = 45
				b.TwistLowerAngle = -70
				b.TwistUpperAngle = 70
			end

			a0.Parent = motor.Part0
			a1.Parent = motor.Part1
			b.Parent = motor.Parent
		end
	end

	local hrp: BasePart? = Character:FindFirstChild("HumanoidRootPart")
	if hrp then
		hrp:SetAttribute("_PrevCanCollide", hrp.CanCollide)
		hrp.CanCollide = false
	end

	if Humanoid.Health > 0 then
		Humanoid:ChangeState(Enum.HumanoidStateType.Ragdoll)
	end
	Humanoid.AutoRotate = false
	Humanoid.JumpPower = 0
	Character:SetAttribute("LastRag", tick())

	startClamp(Character)
end

local function resetJoints(Character: Model, Humanoid: Humanoid)
	stopClamp(Character)

	Humanoid.PlatformStand = false
	Humanoid.AutoRotate = true
	if Humanoid.Health > 0 then
		Humanoid:ChangeState(Enum.HumanoidStateType.GettingUp)
	end

	if Humanoid.JumpPower == 0 and Character:FindFirstChild("PastJP") then
		Humanoid.JumpPower = Character.PastJP.Value
	end

	if Character:FindFirstChild("Ragdolled") then
		Character.Ragdolled:Destroy()
		for _, v in pairs(Character:GetChildren()) do
			if v:IsA("Folder") and v.Name == "Ragdolled" then v:Destroy() end
		end
	end

	for _, instance in pairs(Character:GetDescendants()) do
		if ragdollInstanceNames[instance.Name] then
			instance:Destroy()
		end
		if instance:IsA("Motor6D") then
			instance.Enabled = true
		elseif instance:IsA("BasePart") then
			-- restore CanCollide if we toggled it
			local prev = instance:GetAttribute("_PrevCanCollide")
			if prev ~= nil then
				instance.CanCollide = prev
				instance:SetAttribute("_PrevCanCollide", nil)
			end
			-- keep group assignment or reset if you prefer:
			-- PhysicsService:SetPartCollisionGroup(instance, "Default")
		end
	end
end

local function Ragdoll(value: boolean, Character: Model, Humanoid: Humanoid)
	if value then replaceJoints(Character, Humanoid) else resetJoints(Character, Humanoid) end
end

module.Ragdoll = function(character: Model, length: number?)
	if character:FindFirstChild("Ragdolled") then
		module.Unragdoll(character)
		task.wait()
	end

	local ragdolled = Instance.new("Folder")
	ragdolled.Name = "Ragdolled"
	ragdolled.Parent = character

	local key = math.random(1, 99999999) * math.random(1, 5) / math.random(2, 9)
	local holder = Instance.new("NumberValue")
	holder.Name = "Key"
	holder.Value = key
	holder.Parent = ragdolled

	local hum: Humanoid = character:WaitForChild("Humanoid")
	hum.PlatformStand = true
	hum.RequiresNeck = false

	Ragdoll(true, character, hum)

	if typeof(length) == "number" and length > 0 and length <= 999 then
		task.delay(length, function()
			module.Unragdoll(character, key)
		end)
	end
end

module.Unragdoll = function(character: Model, key: number?)
	if character and character:FindFirstChild("HumanoidRootPart") then
		if key and character:FindFirstChild("Ragdolled") and character.Ragdolled.Key.Value ~= key then
			return
		end
		local hrp = character.HumanoidRootPart
		local vel = hrp.AssemblyLinearVelocity
		Ragdoll(false, character, character.Humanoid)
		hrp.AssemblyLinearVelocity = vel
	end
end

return module

the self collision isnt the problem as its still affected even with it off.
and the other 3 i believe ive already done.
its very frustrating that it just doesnt want to work properly


and after rereading it, the limbs dont even collide with eachother anyways, only the collider parts themselves

I think the problem derives from the fact that the collider parts are colliding with other collider parts.

that is not the issue as it function the same way without them, besides the quality of life look

Can you provide more details on how you apply a force on the ragdoll?

it uses bodyvelocitys
while i know its deprecated, it works fine for others who did the same thing
so i can tell thats not the issue itself. but even when using ragdolls from other people who dont have this issue, this issue still continues, making it very hard to figure out the problem

This might just be a problem with your own computer system. Has anyone else on a different computer system noticed this bug in your game? If not, your ragdoll system might be stretching the limits of your CPU.