Implementing R6 Ragdolls for players and NPCs

Howdy fellow Roblox devs! I have been working on this for a while, and I can’t really seem to find out a good solution. I have been trying to make an R6 ragdoll for a stealth game that I want to make; However, there are a few things that I’m having issues with. The first is client-server replication. What should the server handle and take care of? And vice versa for the client? stuff like that, and I have been trying to make it work for players and NPCs alike, although I am really struggling with implementing this. Here is the module script I have written (if anyone cares to read it):

local module = {}

local UIS = game:GetService("UserInputService")
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local TweenService = game:GetService("TweenService")

local events = ReplicatedStorage:WaitForChild("Events")
local changeStateEvent = events:WaitForChild("ChangeState")

function module.CreateAttachments(char)
	local attachments = {}

	local torso = char:WaitForChild("Torso")
	local head = char:WaitForChild("Head")
	local rightArm = char:WaitForChild("Right Arm")
	local leftArm = char:WaitForChild("Left Arm")
	local rightLeg = char:FindFirstChild("Right Leg")
	local leftLeg = char:FindFirstChild("Left Leg")
	local rootPart = char:WaitForChild("HumanoidRootPart")

	local rata = Instance.new("Attachment", torso)
	rata.Name = "RightArmTorsoAttachment"
	rata.Position = Vector3.new(1, 1, 0)
	table.insert(attachments,rata)

	local lata = Instance.new("Attachment", torso)
	lata.Name = "LeftArmTorsoAttachment"
	lata.Position = Vector3.new(-1, 1, 0)
	table.insert(attachments,lata)

	local rlta = Instance.new("Attachment", torso)
	rlta.Name = "RightLegTorsoAttachment"
	rlta.Position = Vector3.new(0.5, -1, 0)
	table.insert(attachments,rlta)

	local llta = Instance.new("Attachment", torso)
	llta.Name = "LeftLegTorsoAttachment"
	llta.Position = Vector3.new(-0.5, -1, 0)
	table.insert(attachments,llta)

	local raa = Instance.new("Attachment", char["Right Arm"])
	raa.Name = "RightArmAttachment"
	raa.Position = Vector3.new(-0.5, 1, 0)
	table.insert(attachments,raa)

	local laa = Instance.new("Attachment", char["Left Arm"])
	laa.Name = "LeftArmAttachment"
	laa.Position = Vector3.new(0.5, 1, 0)
	table.insert(attachments,laa)

	local rlaa = Instance.new("Attachment", rightLeg)
	rlaa.Name = "RightLegAttachment"
	rlaa.Position = Vector3.new(0, 1, 0)
	table.insert(attachments,rlaa)

	local llaa = Instance.new("Attachment", leftLeg)
	llaa.Name = "LeftLegAttachment"
	llaa.Position = Vector3.new(0, 1, 0)
	table.insert(attachments,llaa)

	local ha = Instance.new("Attachment", char.Head)
	ha.Name = "HeadAttachment"
	ha.Position = Vector3.new(0, -0.5, 0)
	table.insert(attachments,ha)

	print('finish')
end

function module.ragdoll(char)
	local torso = char:FindFirstChild("Torso")
	local head = char:FindFirstChild("Head")
	local rightArm = char:FindFirstChild("Right Arm")
	local leftArm = char:FindFirstChild("Left Arm")
	local rightLeg = char:FindFirstChild("Right Leg")
	local leftLeg = char:FindFirstChild("Left Leg")
	local rootPart = char:FindFirstChild("HumanoidRootPart")

	if char and char:FindFirstChild("Humanoid") then
		local humanoid = char:FindFirstChild("Humanoid")
		humanoid.RequiresNeck = false

		for _, part in pairs(char:GetDescendants()) do
			if part:IsA("Part") then
				local customPhysicalProperties = PhysicalProperties.new(0.75, 0.5, 0.5, 0.75, 1)
				part.CustomPhysicalProperties = customPhysicalProperties
			end
		end

		for i, v in pairs(humanoid:GetPlayingAnimationTracks()) do
			v:Stop()
		end

		local charTable = {
			torso,
			head,
			rightArm,
			leftArm,
			rightLeg,
			leftLeg,
			rootPart
		}

		for _, part in ipairs(charTable) do
			if part:IsA("BasePart") and part ~= char.HumanoidRootPart and part ~= torso then
				local joint = Instance.new("BallSocketConstraint")
				joint.Parent = part
				joint.Enabled = true
				joint.Visible = true
				joint.LimitsEnabled = true
				if part.Name == "Right Arm" then
					joint.Attachment0 = torso:FindFirstChild("RightArmTorsoAttachment")
					joint.Attachment1 = rightArm:FindFirstChild("RightArmAttachment")
					joint.TwistLowerAngle = -45
					joint.TwistUpperAngle = 90
					joint.UpperAngle = 90
					joint.TwistLimitsEnabled = true
				elseif part.Name == "Left Arm" then
					joint.Attachment0 = torso:FindFirstChild("LeftArmTorsoAttachment")
					joint.Attachment1 = leftArm:FindFirstChild("LeftArmAttachment")
					joint.TwistLowerAngle = -90
					joint.TwistUpperAngle = 45
					joint.UpperAngle = 90
					joint.TwistLimitsEnabled = true
				elseif part.Name == "Right Leg" then
					joint.Attachment0 = torso:FindFirstChild("RightLegTorsoAttachment")
					joint.Attachment1 = rightLeg:FindFirstChild("RightLegAttachment")
					joint.TwistLowerAngle = -20
					joint.TwistUpperAngle = 0
					joint.UpperAngle = 20
					joint.TwistLimitsEnabled = true
				elseif part.Name == "Left Leg" then
					joint.Attachment0 = torso:FindFirstChild("LeftLegTorsoAttachment")
					joint.Attachment1 = leftLeg:FindFirstChild("LeftLegAttachment")
					joint.TwistLowerAngle = -20
					joint.TwistUpperAngle = 0
					joint.UpperAngle = 20
					joint.TwistLimitsEnabled = true
				elseif part.Name == "Head" then
					joint.Attachment0 = torso.NeckAttachment
					joint.Attachment1 = head:FindFirstChild("HeadAttachment")
					joint.TwistLowerAngle = -45
					joint.TwistUpperAngle = 45
					joint.UpperAngle = 45
					joint.TwistLimitsEnabled = true
					joint.LimitsEnabled = false
				end
				joint.Restitution = 0
			end
		end

		if torso then
			for i, v in pairs(torso:GetChildren()) do
				if v:IsA("Motor6D") then
					v.Enabled = false
				end
			end
			torso.Velocity = Vector3.new(torso.Velocity.X, torso.Velocity.Y + 50, torso.Velocity.Z)
		end
	end
end

function unragdoll(char,player)

	local torso = char:FindFirstChild("Torso")
	local head = char:FindFirstChild("Head")
	local rightArm = char:FindFirstChild("Right Arm")
	local leftArm = char:FindFirstChild("Left Arm")
	local rightLeg = char:FindFirstChild("Right Leg")
	local leftLeg = char:FindFirstChild("Left Leg")
	local rootPart = char:FindFirstChild("HumanoidRootPart")

	local humanoid = char:FindFirstChild("Humanoid")
	
	if player then
		changeStateEvent:FireClient(player,Enum.HumanoidStateType.GettingUp)
	else
		humanoid:ChangeState(Enum.HumanoidStateType.GettingUp)
	end

	for i, v in pairs(torso:GetChildren()) do
		if v:IsA("Motor6D") then
			v.Enabled = true
		end
	end

	for _, part in ipairs(char:GetDescendants()) do
		if part:IsA("BallSocketConstraint") then
			part:Destroy()
		end
	end

	humanoid.RequiresNeck = true
end

return module

Obviously it’s pretty long so in summary it just makes attachments and provides the code for ragdolling characters and npcs, as well as unragdolling. Thank you in advance to all help from everybody. If something is unclear or needs clarifying, please feel free to ask! Thank you for looking!

It might be worth it to just use a premade ragdoll script. Since it is so popular many people have made it.

This is one that someone posted yesterday. And there are many more in the toolbox.

2 Likes

I loop through the character’s descendants and look for motor:6Ds that aren’t in a blacklist I created (just a table of motor6Ds and their names) I then replace them with ball and socket joints with the same names and switch the part0, and 1s for new attachments. I set hip height to 0, and turn can collide on for all the character’s limbs. To unragdoll I just change the ball and socket joints that aren’t in a blacklist to motor6Ds replacing the names, part0’s and 1s. I just have this in a module in server storage, I require it whenever a player is hit by a ragdoll move or maybe through a remote event when they press a key.

1 Like

Alright thanks both of your guys for your help so much I didn’t even know there was a resources tab! haha anyways yeah I just found a module and I think I’ll be using that, thanks again!

edit: if anyone else is reading this and is wondering which module i used it’s this one: https://create.roblox.com/store/asset/13730326929/Senkos-Ragdoll-module

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