NPC Ragdoll looks like crap when in play mode

Howdy Roblox devs! For a couple of days I’ve been trying to get a good, realistic ragdoll in my game and it works great for my standards when I am in “Run” mode, but when I do “Play” mode it looks like crap and it’s all laggy and stuff. Does anyone have any solutions? (If anyone would like to see the script that I’m using I can comment it) Thanks for reading!

9 Likes

Could I see both the script and a video showcasing it?

5 Likes

yeah of course!
Run Mode (awesome lookin ragdoll imo)


Play Mode (doo doo ragdoll imo)

and here’s the main ragdoll script:

wait(2)

for i, v in pairs(game:GetDescendants()) do
	if v:IsA("Model") and v:FindFirstChildOfClass("Humanoid") then

		--> Make sure this script is located under the Character Model
		local Character: Model = v
		local Torso: BasePart = Character:WaitForChild("Torso")
		local Humanoid: Humanoid = Character:FindFirstChildOfClass("Humanoid")

		--> Necessary for Ragdolling to function properly
		Character.Humanoid.BreakJointsOnDeath = false
		Character.Humanoid.RequiresNeck = false

		--> Specific CFrame's made for the best looking Ragdoll
		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,
		}

		--> Used to trigger Ragdoll
		local RagdollValue = Instance.new("BoolValue")
		RagdollValue.Name = "IsRagdoll"
		RagdollValue.Parent = Character
		-------------------------------------------------------------------------------------------------

		--> push :)
		local function push()
			local velRange = math.random() < 0.5 and {-35, -25} or {25, 35}
			local angularVelRange = math.random() < 0.5 and {-85, -30} or {30, 85}

			-- Torso:ApplyImpulse(Vector3.new(math.random(velRange[1], velRange[2]), 0, math.random(velRange[1], velRange[2])))
			Torso:ApplyAngularImpulse(Vector3.new(math.random(angularVelRange[1], angularVelRange[2]),math.random(angularVelRange[1], angularVelRange[2]),0)) --(Vector3.new(math.random(angularVelRange[1], angularVelRange[2]), math.random(angularVelRange[1], angularVelRange[2]), math.random(angularVelRange[1], angularVelRange[2])))
		end

		--> 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 motor 6d's with ballsocketconstraints
		local function replaceJoints()
			for _, motor: Motor6D in pairs(Character:GetDescendants()) do
				if motor:IsA("Motor6D") then
					if not attachmentCFrames[motor.Name] 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"

					createColliderPart(motor.Part1)

					local b = Instance.new("BallSocketConstraint")
					b.Attachment0 = a0
					b.Attachment1 = a1
					b.Name = "RagdollConstraint"

					-- Set different constraint properties based on the joint
					if motor.Name == "Neck" then
						b.Radius = 0.1
						b.LimitsEnabled = true
						b.TwistLimitsEnabled = true
						b.UpperAngle = 45
						b.TwistLowerAngle = -70
						b.TwistUpperAngle = 70
					elseif motor.Name == "Left Shoulder" or motor.Name == "Right Shoulder" then
						b.Radius = 0.2
						b.LimitsEnabled = true
						b.TwistLimitsEnabled = true
						b.UpperAngle = 50
						b.TwistLowerAngle = -30
						b.TwistUpperAngle = 30
					elseif motor.Name == "Left Hip" or motor.Name == "Right Hip" then
						b.Radius = 0.3
						b.LimitsEnabled = true
						b.TwistLimitsEnabled = true
						b.UpperAngle = 25
						b.TwistLowerAngle = -30
						b.TwistUpperAngle = 30
					end

					b.MaxFrictionTorque = 0
					b.Restitution = 0

					a0.Parent = motor.Part0
					a1.Parent = motor.Part1
					b.Parent = motor.Parent
				end
			end
		end

		--> Destroys all Ragdoll made instances and re-enables the Motor6D's
		local function resetJoints()
			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
		end

		local function Ragdoll(value: boolean)
			if value then
				Humanoid:ChangeState(Enum.HumanoidStateType.Ragdoll)
				Humanoid:SetStateEnabled(Enum.HumanoidStateType.GettingUp, false)
				replaceJoints()
				push()
			else 
				resetJoints()
				Humanoid:ChangeState(Enum.HumanoidStateType.GettingUp)
			end
		end

		-------------------------------------------------------------------------------------------------
		--> Connect the Events
		RagdollValue.Changed:Connect(Ragdoll)
		Humanoid.Died:Once(function()
			RagdollValue.Value = true
			push()
		end)
	end
end
--[[
for i, v in pairs(workspace:GetChildren()) do
	if v:IsA("Model") and v:FindFirstChildOfClass("Humanoid") then
		wait(.25)
		v:FindFirstChild("IsRagdoll").Value = true
	end
end]]
8 Likes

In the videos you’re showing, you’re using the R6 avatar… which is known to have a very bland response with any ragdoll system. I’d recommend swapping to R15 for a cooler Ragdoll effect.

2 Likes

Is the ragdoll script a LocalScript or a script? From what I can see from the video, the ragdoll doesn’t replicate to the client, whereas you can see the ragdoll clearly from the server’s view (Run Mode)

7 Likes

no. that’s not how it works.

the posters problem is most likely because physics replication.

2 Likes

yeah its a server script I tried to change it from server to client replication but it just leaves the limbs behind and works even worse.

2 Likes

i’m just trying to use R6 cause i was gonna make a stealth game cause i’m more comfortable with R6 regarding things like sizes and i thought it would be unique and simpler…

4 Likes

how could i effectively replicate it to the client (sorry that your miserable)

2 Likes

If it’s a server script it should be able to replicate it to every client. Could you try to compare the RagdollValue.Value in both the client and the server? And ensure that the constraints and attachments are properly created too.

1 Like

I usually set the players state on the client, and server. Try setting the Humanoids State on the Client, alongside the server.

1 Like

you can try to set the NPC networkownership to a player client.

3 Likes

It turns out I forgot that I use the exact if not the same ragdoll script, try mine and lmk if it works. I only added client NetworkOwnership for smoothier ragdoll :slight_smile:

--> Make sure this script is located in StarterPlayer.StarterCharacterScripts
local Character: Model = script.Parent
local Torso: BasePart = Character:WaitForChild("Torso")
local Humanoid: Humanoid = Character:FindFirstChildOfClass("Humanoid")

--> Necessary for Ragdolling to function properly
Character.Humanoid.BreakJointsOnDeath = false
Character.Humanoid.RequiresNeck = false

--> Specific CFrame's I made for the best looking Ragdoll
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,
}

-------------------------------------------------------------------------------------------------

--> 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
local function replaceJoints()
	Humanoid.AutoRotate = false --> Disabling AutoRotate prevents the Character rotating in first person or Shift-Lock
	for _, motor: Motor6D in pairs(Character:GetDescendants()) do
		if motor:IsA("Motor6D") then
			if not attachmentCFrames[motor.Name] 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"

			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
end

--> Destroys all Ragdoll made instances and re-enables the Motor6D's
local function resetJoints()
	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

local function Ragdoll(value: boolean)
	if value then 
		replaceJoints() 
		local Player = game:GetService("Players"):GetPlayerFromCharacter(Character)
		if Player then
			for _, part in pairs(Character:GetDescendants()) do
				if part:IsA("BasePart") then
					part:SetNetworkOwner(Player)
				end
			end
		end
	else 
		resetJoints() 
	end
end

local Connection
Connection = Humanoid.Changed:connect(function() 
	if Humanoid then
		if Humanoid.Health <= 0 then 
			Connection:Disconnect() 
			Humanoid:ChangeState(Enum.HumanoidStateType.Dead) 
			Ragdoll(true)
		end
	end
end)

ill try to try all of that stiff but i probably should mention that the ragdoll is and npc and the thing that i dont like about the play mode ragdoll is that like the limbs kinda freeze middair and stuff but yeah ill try those as soon as i can thanks guys

Yeah this looks like its a network issue. If the animation is run on the server then call the function :SetNetworkOwner(nil) on the HumanoidRootPart

If the animation is run locally then do :SetNetworkOwner(Player)

sorry i think there might be a little bita confusion theres no animations is just like the ballsocketconstraits on the ragdoll but yeah ill try setting the network ownership later today prob if i can

ok i just tried doing that, it doesn’t work though because the check for the change of value is in a server script.

i tried your script and unfortunatly it looks the same as the second clip that I sent. what i’m really trying to do it just make the ragdoll look smooth, idk if any of you have played entry point but you might’ve noticed that the ragdolls in that look smooth and i can’t find out how to do it.

Do you have any other external scripts interfering with the ragdoll script?

dude im such a fetching idiot sorry i forgot to say in the title that its nps that look crappy specifically