Ragdoll system is so rigid and stiff

My ragdoll system is so rigid and stiff

Clips

Stiff ragdoll clip:
Watch 2025-01-23 13-26-37 | Streamable
Working ragdoll clip:
Watch 2025-01-23 13-26-04 | Streamable

Code

Server script:


local ReplicatedStorage = game:GetService("ReplicatedStorage")
local ServerStorage = game:GetService("ServerStorage")

local Constants = require(ServerStorage.Constants.Constants)
local Events = ReplicatedStorage.Events


local Character: Model = script.Parent
local Torso: BasePart = Character:WaitForChild("Torso")
local Humanoid: Humanoid = Character:FindFirstChildOfClass("Humanoid")


Humanoid.BreakJointsOnDeath = false
Humanoid.RequiresNeck = false 



local RagdollValue = Instance.new("BoolValue")
RagdollValue.Name = "IsRagdoll"
RagdollValue.Parent = Character

Character:SetAttribute("LastRag", -100)
-------------------------------------------------------------------------------------------------


function Ragdoll(value: boolean)
	if value then

		Events.RagdollReplicator:FireAllClients(Character, value)
		
		Character:SetAttribute("LastRag", time())
		Humanoid.AutoRotate = false

		
	else

		Events.RagdollReplicator:FireAllClients(Character, value)
		Humanoid.AutoRotate = true

	end
end


RagdollValue.Changed:Connect(Ragdoll)
Humanoid.Died:Once(function()
	RagdollValue.Value = true
end)

task.spawn(function()
	while true do
		local timeInBetween = time() - Character:GetAttribute("LastRag")
		
		if timeInBetween > Constants.AutoUnragdollTime and Humanoid.Health > 0 then
			RagdollValue.Value = false
		end
		
		task.wait()
	end
end)

Client Script 1:


-- // Variables

local LocalCharacter = script.Parent.Parent
local Camera = workspace.CurrentCamera

repeat task.wait() until Camera
local OldCameraSubject = Camera.CameraSubject

local Humanoid = LocalCharacter:WaitForChild("Humanoid") :: Humanoid

local Clones = {
	
}

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,
}

-- // Functions

--> Allows for proper limb collisions
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

--> Converts Motor6D's into BallSocketConstraints
function replaceJoints(Character : Instance)
	local motors = 0
	
	for _, motor : Instance in pairs(Character:GetDescendants()) do
		if motor:IsA("Motor6D") then
			motors += 1
		end
	end
	
	print("Number of motors: ", motors)
	
	for _, motor : Instance in pairs(Character:GetDescendants()) do
		if motor:IsA("Motor6D") then
			

			if not attachmentCFrames[motor.Name] then return end
			if motor.Name == "RootJoint" 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"

			if not motor.Part1 then return end
			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 = 0
			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 Humanoid = Character:WaitForChild("Humanoid") :: Humanoid
	Humanoid.AutoRotate = false --> Disabling AutoRotate prevents the Character rotating in first person or Shift-Lock
end

function resetJoints(Character : Instance)
	local Humanoid = Character:WaitForChild("Humanoid") :: Humanoid

	if Humanoid.Health < 1 then return end
	

		
	for _, instance in pairs(Character:GetDescendants()) do
		if ragdollInstanceNames[instance.Name] then
			instance:Destroy()
		end

		if instance:IsA("Motor6D") then
			instance.Enabled = true
		end
	end

	Humanoid.AutoRotate = true
end

function createClone (Character : Instance) : Model
	
	-- Clone it
	
	Character.Archivable = true
	
	local Clone = Character:Clone()
	

	-- Make original character transparent
	for i, v in pairs(Character:GetDescendants()) do
		if v:IsA("BasePart") or v:IsA("Decal") then
			if v:IsA("BasePart") then
				v.CanCollide = false
				v.CanTouch = false
				v.CanQuery = false
				v.CollisionGroup = "Ragdoll"
				v.Transparency = 1
			elseif v:IsA("Decal") then
				v.Transparency = 1
			
				
			end

		

		end
	end
	
	-- Clone collisions
	for i, v in pairs(Clone:GetDescendants()) do
		if v:IsA("BasePart") then
			v.CollisionGroup = "Ragdoll"
		end
	end

	Clones[Clone.Name] = Clone
	
	task.spawn(function()
		task.wait(10)
		if Clones[Clone.Name] then
			Clones[Clone.Name]:Destroy()
			Clones[Clone.Name] = nil
		end
	end)
	
	Clone.Name = Character.Name .. "_Clone"
	Clone.Parent = workspace:FindFirstChild("Players")
	
	-- Humanoid stuff
	local Humanoid = Clone:WaitForChild("Humanoid") :: Humanoid
	local oHumanoid = Character:WaitForChild("Humanoid") :: Humanoid
	oHumanoid:WaitForChild("Animator"):Destroy()

	local oRootPart = oHumanoid.RootPart
	local RootPart = Humanoid.RootPart
	RootPart.CanCollide = false
	
	repeat task.wait() until RootPart and oRootPart
	
 
	task.spawn(function()
		while Clone do
			RootPart.CFrame = oRootPart.CFrame
			task.wait()
		end
		
	end)
	
	Humanoid:ChangeState(Enum.HumanoidStateType.Dead)

	Humanoid.NameDisplayDistance = 0
	Humanoid.HealthDisplayDistance = 0
	Humanoid.HealthDisplayType = Enum.HumanoidHealthDisplayType.AlwaysOff
	Humanoid.NameOcclusion = Enum.NameOcclusion.OccludeAll
	
	if Character == LocalCharacter then
		Camera.CameraSubject = RootPart
	end

	
	Character.Archivable = false

	
	return Clone
end

function ragdollReplication (Character : Instance, value : boolean)

	if value then

		local Clone = createClone(Character)

	
		replaceJoints(Clone)
		

	else
		if Character == LocalCharacter then
			Camera.CameraSubject = OldCameraSubject
		end
		
		if Clones[Character.Name] then
			Clones[Character.Name]:Destroy()
			Clones[Character.Name] = nil
			
			-- Make original character opaque
			for i, v in pairs(Character:GetDescendants()) do
				if v:IsA("BasePart") then
					v.CanCollide = true
					v.CanTouch = true
					v.CanQuery = true
					v.CollisionGroup = "Players"

					if v.Name ~= "HumanoidRootPart" then
						v.Transparency = 0
					end
				end
			end

		end
		resetJoints(Character)
	end
end

local ReplicatedStorage = game:GetService("ReplicatedStorage")
ReplicatedStorage.Events.RagdollReplicator.OnClientEvent:Connect(ragdollReplication)

Client Script 2:

local Character: Model = script.Parent.Parent
local Torso: BasePart = Character:WaitForChild("Torso")
local Humanoid: Humanoid = Character:WaitForChild("Humanoid")
local RagdollValue: BoolValue = Character:WaitForChild("IsRagdoll")

local function push()
	Torso:ApplyImpulse(Torso.CFrame.LookVector * -100)
end

RagdollValue:GetPropertyChangedSignal("Value"):Connect(function()
	if (Humanoid.Health == 0) then --> Prevents OOF sound from playing twice thanks to @robloxdestroyer1035
		Humanoid:SetStateEnabled(Enum.HumanoidStateType.GettingUp, false)
		push()
		return
	end
	
	if RagdollValue.Value then
		Humanoid:ChangeState(Enum.HumanoidStateType.Ragdoll)
		Humanoid:SetStateEnabled(Enum.HumanoidStateType.GettingUp, false)
		push()
	elseif not RagdollValue.Value then

		Humanoid:ChangeState(Enum.HumanoidStateType.GettingUp)
	end
end)

Humanoid.Died:Connect(push)

As you can see it bugs out sometimes

2 Likes

I think this happens because my code skips over the Motor6Ds i still dont know how to solve it

2 Likes

Bump.Bump.Bump.Bump.Bump.Bump.