I havent been on this forum for years, but I saw someone thinking they can BEST ME??? NEVER.
You need Active Ragdoll for your r6 game? here. No rigs, no setup, js call .new with the character, yes it supports npcs.
API:
- RagdollSystem.new(char: Character, player?: Player, useDoubleRig?: boolean = false)
- thats it, everything else handled internally, just use the module right, no i wont reply
Enjoy the beautiful types too, cause why not
local Players = game:GetService("Players")
local PhysicsService = game:GetService("PhysicsService")
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local RunService = game:GetService("RunService")
local Debris = game:GetService("Debris")
local RagdollSystem = {}
RagdollSystem.__index = RagdollSystem
export type AnimationData = {
track: AnimationTrack,
isPlaying: boolean,
}
export type ConnectionTable = {
[string]: RBXScriptConnection,
}
export type self = {
character: Model,
player: Player?,
useDoubleRig: boolean,
isDead: boolean,
ragdollState: boolean,
activeRagdollState: string,
connections: ConnectionTable,
rigFolder: Folder,
doubleFolder: Folder?,
animations: { [string]: AnimationData }?,
}
export type RagdollSystem = typeof(setmetatable({} :: self, RagdollSystem))
-- Collision groups setup
local function setupCollisionGroups()
local groups = { "NC", "DRChar" }
for _, group in ipairs(groups) do
pcall(function()
PhysicsService:RegisterCollisionGroup(group)
end)
end
PhysicsService:CollisionGroupSetCollidable("Default", "NC", false)
PhysicsService:CollisionGroupSetCollidable("NC", "NC", true)
PhysicsService:CollisionGroupSetCollidable("DRChar", "Default", false)
end
setupCollisionGroups()
-- Sound IDs for body impacts
local BODY_IMPACTS =
{ 7446607140, 7446609976, 7446606976, 7446606796, 7446606925, 7446607091, 7446607037, 7446609932, 7446610013 }
-- Joint attachment positions (simplified)
local ATTACHMENT_CFRAMES = {
["Neck"] = { CFrame.new(0, 1, 0), CFrame.new(0, -0.5, 0) },
["Left Shoulder"] = { CFrame.new(-1.5, 0.5, 0), CFrame.new(0.5, 0.5, 0) },
["Right Shoulder"] = { CFrame.new(1.5, 0.5, 0), CFrame.new(-0.5, 0.5, 0) },
["Left Hip"] = { CFrame.new(-0.5, -1, 0), CFrame.new(0, 1, 0) },
["Right Hip"] = { CFrame.new(0.5, -1, 0), CFrame.new(0, 1, 0) },
["RootJoint"] = { CFrame.new(0, 0, 0), CFrame.new(0, 0, 0) },
}
-- Muscle positions for double rig
local MUSCLE_CFRAMES = {
Head = CFrame.new(0, 0, 0),
Torso = CFrame.new(0, 0, 0),
["Left Arm"] = CFrame.new(0, 0, 0),
["Right Arm"] = CFrame.new(0, 0, 0),
["Left Leg"] = CFrame.new(0, 0, 0),
["Right Leg"] = CFrame.new(0, 0, 0),
}
-- Animation IDs for double rig states
local ANIMATION_IDS = {
Head = 14884198606,
Torso = 14884229692,
["Left Arm"] = 14884240711,
["Right Arm"] = 14884237652,
["Left Leg"] = 14884244257,
["Right Leg"] = 14884229692,
}
-- Character limb names
local CHAR_LIMBS = {
"Head",
"Torso",
"Left Arm",
"Right Arm",
"Left Leg",
"Right Leg",
"HumanoidRootPart",
}
local function isCharLimb(name)
for _, limb in ipairs(CHAR_LIMBS) do
if limb == name then
return true
end
end
return false
end
function RagdollSystem.new(character: Model, player: Player?, useDoubleRig: boolean?): RagdollSystem
local self = setmetatable({} :: self, RagdollSystem) :: RagdollSystem
self.character = character
self.player = player
self.useDoubleRig = useDoubleRig or false
self.isDead = false
self.ragdollState = false
self.activeRagdollState = "None"
self.connections = {}
-- Create folders
self.rigFolder = Instance.new("Folder")
self.rigFolder.Name = "Rig"
self.rigFolder.Parent = character
if self.useDoubleRig then
self.doubleFolder = Instance.new("Folder")
self.doubleFolder.Name = "Double"
self.doubleFolder.Parent = character
self.animations = {}
end
self:setup()
return self
end
function RagdollSystem:setup(): ()
local humanoid = self.character:FindFirstChildOfClass("Humanoid")
if not humanoid then
return
end
-- Configure humanoid
humanoid.BreakJointsOnDeath = false
humanoid.RequiresNeck = false
-- Connect death events
self.connections.died = humanoid.Died:Connect(function()
self.isDead = true
self:setRagdoll(true)
self:updateRagdoll()
end)
self.connections.stateChanged = humanoid.StateChanged:Connect(function(_, new: Enum.HumanoidStateType)
if new == Enum.HumanoidStateType.Dead then
self.isDead = true
self:updateRagdoll()
end
end)
-- Setup collision detection for impact sounds
self:setupCollisionDetection()
if self.useDoubleRig then
self:setupDoubleRig()
end
-- Cleanup when character is removed
self.connections.ancestryChanged = self.character.AncestryChanged:Connect(function(_, parent: Instance?)
if parent == nil then
self:destroy()
end
end)
end
function RagdollSystem:setupCollisionDetection(): ()
local soundCooldown = false
for _, child in pairs(self.character:GetChildren()) do
if child:IsA("BasePart") then
self.connections[child.Name .. "_touched"] = child.Touched:Connect(function(hit: BasePart)
local velocity = child.AssemblyLinearVelocity.Magnitude
-- Impact sound
if
velocity > 5
and not soundCooldown
and self.ragdollState
and not hit:IsDescendantOf(self.character)
then
soundCooldown = true
task.delay(0.25, function()
soundCooldown = false
end)
-- stub, i originally played a sound here (you can too)
end
end)
end
end
end
function RagdollSystem:createCollider(part: BasePart): ()
if part:FindFirstChild("ColliderPart") then
return
end
local collider = Instance.new("Part")
collider.Name = "ColliderPart"
collider.Size = part.Size / 1.7
collider.Massless = true
collider.CFrame = part.CFrame
collider.Transparency = 1
collider.CustomPhysicalProperties = PhysicalProperties.new(2, 5, 0.2, 80, 80)
local weld = Instance.new("WeldConstraint")
weld.Part0 = collider
weld.Part1 = part
weld.Parent = collider
collider.Parent = part
end
function RagdollSystem:setupDoubleRig(): ()
-- Create double rig parts
for _, part in pairs(self.character:GetChildren()) do
if part:IsA("BasePart") and isCharLimb(part.Name) then
local dublePart = Instance.new("Part")
dublePart.Name = part.Name
dublePart.Size = part.Size
dublePart.CanCollide = false
dublePart.CanQuery = false
dublePart.CanTouch = false
dublePart.Transparency = 1
dublePart.CollisionGroup = "DRChar"
dublePart.Parent = self.doubleFolder
-- Set custom physics properties for original part
part.CustomPhysicalProperties = PhysicalProperties.new(2, 0.45, 0.6, 100, 100)
-- Find motor6D for this limb
local motor6d = self:findMotor6DForLimb(part)
if motor6d then
-- Create attachments
local attach1 = Instance.new("Attachment")
local attach2 = Instance.new("Attachment")
attach1.Parent = part
attach2.Parent = dublePart
attach1.CFrame = MUSCLE_CFRAMES[part.Name] or CFrame.new()
attach2.CFrame = MUSCLE_CFRAMES[part.Name] or CFrame.new()
-- Create spring constraint
local spring = Instance.new("SpringConstraint")
spring.Name = part.Name .. " Spring"
spring.Attachment0 = attach1
spring.Attachment1 = attach2
spring.Damping = 80
spring.Stiffness = 20000
spring.Visible = false
spring.Enabled = false
spring.Coils = 1
spring.MinLength = 0
spring.MaxLength = 5
spring.MaxForce = 500000
spring.LimitsEnabled = true
spring.Parent = self.doubleFolder
-- Clone motor6d for double rig
local clonedMotor = motor6d:Clone()
clonedMotor.Parent = self.doubleFolder
clonedMotor.Enabled = true
end
end
end
-- Update motor6d connections for double rig
for _, motor in pairs(self.doubleFolder:GetChildren()) do
if motor:IsA("Motor6D") then
if motor.Part1 and self.doubleFolder:FindFirstChild(motor.Part1.Name) then
motor.Part1 = self.doubleFolder:FindFirstChild(motor.Part1.Name)
end
if motor.Part0 and self.doubleFolder:FindFirstChild(motor.Part0.Name) then
motor.Part0 = self.doubleFolder:FindFirstChild(motor.Part0.Name)
end
end
end
-- Create root weld
local weld = Instance.new("Weld")
weld.Name = "DoubleWeld"
weld.Part1 = self.character:FindFirstChild("Torso")
weld.Part0 = self.doubleFolder:FindFirstChild("Torso")
weld.C0 = CFrame.new()
weld.Parent = self.character
-- Load animations
self:loadAnimations()
-- Start update loop
self.connections.heartbeat = RunService.Heartbeat:Connect(function(dt: number)
self:updateDoubleRig(dt)
end)
end
function RagdollSystem:findMotor6DForLimb(limb: BasePart): Motor6D?
if limb.Name == "HumanoidRootPart" then
return nil
end
local torso = self.character:FindFirstChild("Torso")
if not torso then
return nil
end
for _, descendant in pairs(torso:GetDescendants()) do
if descendant:IsA("Motor6D") and descendant.Part1 == limb then
return descendant
end
end
return nil
end
function RagdollSystem:loadAnimations(): ()
local humanoid = self.character:FindFirstChildOfClass("Humanoid")
if not humanoid then
return
end
for limbName, animId in pairs(ANIMATION_IDS) do
local animation = Instance.new("Animation")
animation.AnimationId = "rbxassetid://" .. animId
local track = humanoid:LoadAnimation(animation)
self.animations[limbName] = {
track = track,
isPlaying = false,
}
track.Stopped:Connect(function()
if self.activeRagdollState == limbName then
self.activeRagdollState = "None"
end
self.animations[limbName].isPlaying = false
end)
end
end
function RagdollSystem:updateDoubleRig(): ()
if self.character.Parent == nil then
return
end
local anyAnimationPlaying = false
-- Update animation states and check if any are playing
for limbName, animData in pairs(self.animations) do
if self.activeRagdollState == limbName then
if not animData.track.IsPlaying then
animData.track:Play()
animData.isPlaying = true
end
else
if animData.track.IsPlaying then
animData.track:Stop()
animData.isPlaying = false
end
end
-- Check if this animation is currently playing
if animData.track.IsPlaying then
anyAnimationPlaying = true
end
end
-- Only enable active ragdoll when animations are playing AND character is ragdolled
local activeRagdollEnabled = self.ragdollState and anyAnimationPlaying
-- Update spring constraints - only active when animations are playing
for _, item in pairs(self.doubleFolder:GetChildren()) do
if item:IsA("SpringConstraint") then
item.Enabled = activeRagdollEnabled
elseif item:IsA("Motor6D") then
-- Double rig motor6ds are always enabled for the double rig to work
item.Enabled = true
end
end
-- Handle root joint - connect to double rig only when active ragdoll is enabled
local rootJoint = self.character.HumanoidRootPart:FindFirstChildOfClass("Motor6D")
if rootJoint then
if activeRagdollEnabled and not self.isDead then
-- Connect to double rig for active ragdoll
rootJoint.Part1 = self.doubleFolder:FindFirstChild("Torso")
else
-- Connect to normal torso for free ragdoll
rootJoint.Part1 = self.character:FindFirstChild("Torso")
end
end
-- Network ownership for NPCs
if not Players:GetPlayerFromCharacter(self.character) and not self.isDead then
pcall(function()
self.character.HumanoidRootPart:SetNetworkOwner(nil)
end)
end
end
function RagdollSystem:updateRagdoll(): ()
-- Create ball socket constraints if needed
for _, descendant in pairs(self.character:GetDescendants()) do
if descendant:IsA("Motor6D") then
local attachData = ATTACHMENT_CFRAMES[descendant.Name]
if
attachData
and descendant.Part0
and descendant.Part1
and not self.rigFolder:FindFirstChild(descendant.Name .. " Socket")
then
local attach0 = Instance.new("Attachment")
local attach1 = Instance.new("Attachment")
attach0.CFrame = attachData[1]
attach1.CFrame = attachData[2]
self:createCollider(descendant.Part1)
local socket = Instance.new("BallSocketConstraint")
socket.Attachment0 = attach0
socket.Attachment1 = attach1
socket.Radius = 0.15
socket.LimitsEnabled = true
socket.TwistLimitsEnabled = true
socket.MaxFrictionTorque = 0
socket.Restitution = 0
socket.UpperAngle = 90
socket.TwistLowerAngle = -45
socket.TwistUpperAngle = 45
-- Special settings for neck
if descendant.Name == "Neck" then
socket.UpperAngle = 45
socket.TwistLowerAngle = -70
socket.TwistUpperAngle = 70
end
attach0.Parent = descendant.Part0
attach1.Parent = descendant.Part1
socket.Parent = self.rigFolder
socket.Name = descendant.Name .. " Socket"
socket.Enabled = self.ragdollState
end
end
end
-- Update constraint states
for _, socket in pairs(self.rigFolder:GetChildren()) do
if socket:IsA("BallSocketConstraint") then
socket.Enabled = self.ragdollState
end
end
-- Update motor6d states
for _, descendant in pairs(self.character:GetDescendants()) do
if descendant:IsA("Motor6D") then
descendant.Enabled = not self.ragdollState
end
end
-- Update collision groups and physics
for _, part in pairs(self.character:GetChildren()) do
if part:IsA("BasePart") then
local collider = part:FindFirstChild("ColliderPart")
if collider and collider:IsA("BasePart") then
collider.CollisionGroup = self.ragdollState and "Default" or "NC"
part.CollisionGroup = self.ragdollState and "NC" or "Default"
end
part.CustomPhysicalProperties = PhysicalProperties.new(2, 5, 0.2, 80, 80)
end
end
-- Update humanoid
local humanoid = self.character:FindFirstChildOfClass("Humanoid")
if humanoid then
humanoid.PlatformStand = self.ragdollState
end
end
function RagdollSystem:setRagdoll(enabled: boolean): ()
if self.ragdollState == enabled then
return
end
self.ragdollState = enabled
if self.isDead then
self.ragdollState = true
end
self:updateRagdoll()
end
function RagdollSystem:setActiveRagdollState(state: string?): ()
if not self.useDoubleRig then
return
end
self.activeRagdollState = state or "None"
end
-- New helper function to check if active ragdoll is currently engaged
function RagdollSystem:isActiveRagdollEngaged(): boolean
if not self.useDoubleRig or not self.ragdollState then
return false
end
-- Check if any animation is currently playing
for _, animData in pairs(self.animations or {}) do
if animData.track and animData.track.IsPlaying then
return true
end
end
return false
end
function RagdollSystem:destroy(): ()
-- Disconnect all connections
for _, connection in pairs(self.connections) do
if connection then
connection:Disconnect()
end
end
-- Clean up animations
if self.animations then
for _, animData in pairs(self.animations) do
if animData.track then
animData.track:Stop()
animData.track:Destroy()
end
end
end
-- Clean up folders
if self.rigFolder then
self.rigFolder:Destroy()
end
if self.doubleFolder then
self.doubleFolder:Destroy()
end
end
return RagdollSystem
