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)
The End
And that concludes on how to achieve active ragdoll. Feel free to reply and raise questions if you have any.
A like on this post will be greatly appreciated! Thank you.