How To Achieve Active Ragdoll In Roblox

Hello everyone! I am kiy4ku.

I recently created a Ragdoll Module, and I realized there wasn’t enough information about active ragdolls on Roblox.

In this tutorial, I will create an active ragdoll only for the upper body, but you can use the same approach to create active ragdolls for other parts as well.

So I’ll explain how you can implement active ragdolls in your games.

Result Preview;

Let’s get started.


Explanation

- Ragdoll

First, you need to ragdoll your character. You can use or create a ragdoll module for this. I’ll be using my Ragdoll Module in this tutorial.

- Getting Animation Data

We will start by creating a model inside the character. Then, create the same number of parts as the number of limbs on the character, set each part to the same size as the corresponding limb, and place these parts inside the model.

Next, create a loop to get the character’s limbs again (for _, limb: BasePart in character:GetChildren() do... end). Check if there is a Motor6D in each limb, and if it matches the parts in the model we created, then create a new Motor6D. Adjust the Part0 and Part1 properties according to the parts in the model.

As a result, you will have another model that simulates your character’s animations.

- Applying Animation Data

In this step, you can use SpringConstraint, AlignPosition, etc., but I recommend using AlignOrientation, which is what I’ll be using in this tutorial.

We will simply try to simulate the rotation of the limbs in the model we created during each RunService.Stepped event in our main character.


Implementation

1: Ragdoll The Character

First, you need to ragdoll your character. You can use or create a ragdoll module for this. I’ll be using my Ragdoll Module in this tutorial.

2: Create A Table For Storage AlignOrientation Properties

You can use this method;

local jointName = tostring(string.gsub(joint.Name, "%s+", ""))
jointName = jointName:match("[A-Z]?%l*$")

for getting last word of a word phrase like LeftUpperArm -> Arm

Table Example
local jointInfo = {
	['Neck'] = {
		MaxTorque = 500,
		Responsiveness = 200
	},

	['Waist'] = {
		MaxTorque = 1500,
		Responsiveness = 200
	},

	['Ankle'] = {
		MaxTorque = 200,
		Responsiveness = 200
	},

	['Wrist'] = {
		MaxTorque = 200,
		Responsiveness = 200
	},
}

3: Create A Clone Character For Animations

Code
player.CharacterAdded:Connect(function(character)
	if not player:HasAppearanceLoaded() then
		repeat task.wait(0.1) until player:HasAppearanceLoaded() or not player.Character
	end -- yield's until character loaded (i dont know better solution for wait character appearance loaded in client *player.Appe)
	
	characterUtil:disableRootPartCollision(character) -- Disables collusion of HumanoidRootPart and set's Density to 0.0001
	
	local rootPart: BasePart = character:WaitForChild('HumanoidRootPart')
	
	clonedCharacter = Instance.new('Model')
	clonedCharacter.Name = 'ClonedCharacter'
	clonedCharacter.Parent = character
	
	local characterLimbs = {}
	
	for _, limb: BasePart in character:GetChildren() do
		if not limb:IsA('BasePart') then continue end
		limb.CollisionGroup = 'Character'
		if limb == rootPart then continue end

		local newLimb = Instance.new('Part')
		newLimb.Size = limb.Size
		newLimb.CanCollide = false
		newLimb.CanQuery = false
		newLimb.CanTouch = false
		newLimb.Massless = true
		newLimb.Name = limb.Name
		newLimb.Transparency = 1
		newLimb.Parent = clonedCharacter
		
		table.insert(characterLimbs, limb)
	end
end)

And create AlignOrientation for simulate animation In limb’s

Code
for i, limb: BasePart in characterLimbs do
	local clonedLimb = clonedCharacter:FindFirstChild(limb.Name)
	if not clonedLimb then
		table.remove(characterLimbs, i)
		continue
	end

	local joint = limb:FindFirstChildOfClass('Motor6D')
	if not joint then
		table.remove(characterLimbs, i)
		continue
	end

	local newJoint = joint:Clone()
	newJoint.Enabled = true

	newJoint.Part1 = clonedLimb

	newJoint.Part0 =
		if limb.Name == 'LowerTorso' then
		rootPart
		else
		clonedCharacter:FindFirstChild(newJoint.Part0.Name)

	newJoint.Parent = clonedLimb

	local jointName = tostring(string.gsub(joint.Name, "%s+", ""))
	jointName = jointName:match("[A-Z]?%l*$")

	local properties = jointInfo[jointName]
	if not properties then continue end

	joint.Enabled = false
	limb.CanCollide = true

	local attachment = Instance.new('Attachment', limb)
	local clonedAttachment = Instance.new('Attachment', clonedLimb)

	local orientation = Instance.new('AlignOrientation')
	orientation.Name = limb.Name
	orientation.CFrame = clonedLimb.CFrame
	orientation.Attachment0 = attachment
	orientation.Attachment1 = clonedAttachment
	orientation.ReactionTorqueEnabled = true

	for property, value in properties do
		pcall(function() orientation[property] = value end)
	end

	orientation.Parent = limb
end

4: Simulate Animations On Character

Code
runService.Stepped:Connect(function()
	if not player.Character then
		return
	end
	
	local humanoid = player.Character:FindFirstChildOfClass('Humanoid')
	if humanoid and humanoid:GetState() == Enum.HumanoidStateType.Dead then
		if clonedCharacter then
			clonedCharacter:Destroy()
			clonedCharacter = nil
		end
		
		return
	end
	
	if not clonedCharacter or not clonedCharacter.Parent then
		if clonedCharacter then
			clonedCharacter:Destroy()
			clonedCharacter = nil
		end
		
		return
	end
	
	for i, limb: BasePart in player.Character:GetChildren() do
		local alignOrientation = limb:FindFirstChildOfClass('AlignOrientation')
		if not alignOrientation then
			continue
		end
		
		local clonedLimb: BasePart = clonedCharacter:FindFirstChild(limb.Name)
		if not clonedLimb then
			continue
		end
		
		alignOrientation.CFrame = clonedLimb.CFrame
	end
end)

Full Code
--!nocheck

local replicatedStorage = game:GetService('ReplicatedStorage')
local players = game:GetService('Players')
local runService = game:GetService('RunService')

local player = players.LocalPlayer

local ragdollService = require(replicatedStorage:WaitForChild('RagdollService'))
local characterUtil = require(replicatedStorage.RagdollService:WaitForChild('Shared'):WaitForChild('Utils'):WaitForChild('character'))

local clonedCharacter: Model? = nil

local jointInfo = {
	['Neck'] = {
		MaxTorque = 500,
		Responsiveness = 200
	},

	['Waist'] = {
		MaxTorque = 1500,
		Responsiveness = 200
	},

	['Ankle'] = {
		MaxTorque = 200,
		Responsiveness = 200
	},

	['Wrist'] = {
		MaxTorque = 200,
		Responsiveness = 200
	},
}

player.CharacterAdded:Connect(function(character)
	if not player:HasAppearanceLoaded() then
		repeat task.wait(0.1) until player:HasAppearanceLoaded() or not player.Character
	end
	
	characterUtil:disableRootPartCollision(character)
	
	local rootPart: BasePart = character:WaitForChild('HumanoidRootPart')
	
	clonedCharacter = Instance.new('Model')
	clonedCharacter.Name = 'ClonedCharacter'
	clonedCharacter.Parent = character
	
	local characterLimbs = {}
	
	for _, limb: BasePart in character:GetChildren() do
		if not limb:IsA('BasePart') then continue end
		limb.CollisionGroup = 'Character'
		if limb == rootPart then continue end

		local newLimb = Instance.new('Part')
		newLimb.Size = limb.Size
		newLimb.CanCollide = false
		newLimb.CanQuery = false
		newLimb.CanTouch = false
		newLimb.Massless = true
		newLimb.Name = limb.Name
		newLimb.Transparency = 1
		newLimb.Parent = clonedCharacter
		
		table.insert(characterLimbs, limb)
	end
	
	for i, limb: BasePart in characterLimbs do
		local clonedLimb = clonedCharacter:FindFirstChild(limb.Name)
		if not clonedLimb then
			table.remove(characterLimbs, i)
			continue
		end
		
		local joint = limb:FindFirstChildOfClass('Motor6D')
		if not joint then
			table.remove(characterLimbs, i)
			continue
		end

		local newJoint = joint:Clone()
		newJoint.Enabled = true
		
		newJoint.Part1 = clonedLimb
		
		newJoint.Part0 =
			if limb.Name == 'LowerTorso' then
				rootPart
			else
				clonedCharacter:FindFirstChild(newJoint.Part0.Name)
			
		newJoint.Parent = clonedLimb
		
		local jointName = tostring(string.gsub(joint.Name, "%s+", ""))
		jointName = jointName:match("[A-Z]?%l*$")

		local properties = jointInfo[jointName]
		if not properties then continue end
		
		joint.Enabled = false
		limb.CanCollide = true

		local attachment = Instance.new('Attachment', limb)
		local clonedAttachment = Instance.new('Attachment', clonedLimb)

		local orientation = Instance.new('AlignOrientation')
		orientation.Name = limb.Name
		orientation.CFrame = clonedLimb.CFrame
		orientation.Attachment0 = attachment
		orientation.Attachment1 = clonedAttachment
		orientation.ReactionTorqueEnabled = true
		
		for property, value in properties do
			pcall(function() orientation[property] = value end)
		end
		
		orientation.Parent = limb
	end
end)

runService.Stepped:Connect(function()
	if not player.Character then
		return
	end
	
	local humanoid = player.Character:FindFirstChildOfClass('Humanoid')
	if humanoid and humanoid:GetState() == Enum.HumanoidStateType.Dead then
		if clonedCharacter then
			clonedCharacter:Destroy()
			clonedCharacter = nil
		end
		
		return
	end
	
	if not clonedCharacter or not clonedCharacter.Parent then
		if clonedCharacter then
			clonedCharacter:Destroy()
			clonedCharacter = nil
		end
		
		return
	end
	
	for i, limb: BasePart in player.Character:GetChildren() do
		local alignOrientation = limb:FindFirstChildOfClass('AlignOrientation')
		if not alignOrientation then
			continue
		end
		
		local clonedLimb: BasePart = clonedCharacter:FindFirstChild(limb.Name)
		if not clonedLimb then
			continue
		end
		
		alignOrientation.CFrame = clonedLimb.CFrame
	end
end)

Game Link

The End

And that concludes on how to achieve active ragdoll. Feel free to reply and raise questions if you have any.

A like :+1: on this post will be greatly appreciated! Thank you.

7 Likes