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