Animations break when NPC ragdolls

I’m working on a fighting/rpg game and I have this system that lets me use the same module that players use for combat that NPCs also use.

However whenever I ragdoll the NPC with a final M1 hit it seems to break the animations and i’m not sure why. I tried changing up the ragdoll script and making it destroy and recreate the Motor6D joints when they get back up and I made it reload the animations when they get back up but it didn’t seem to help. It also seems to be a client thing also, as when I switch to a server perspective it’s working perfectly fine. Any help would be appreciated.

Here’s a gif of the bug.
https://gyazo.com/9c3d934a7e77c0afce15d3d7242c1a8f

Here’s the ragdoll script code if anyone needs to see it to help.

-- Class
local API = {}
API.__index = API

-- Variables
local CollectionSerivce = game:GetService("CollectionService")
local RagdollTag = "PlayerRagdolled"

-- Constants
local ATTACHMENT_DATA = {
	{"LeftCollarRagdoll",Vector3.new(-1, 1, 0)},
	{"LeftHipRagdoll",Vector3.new(-1, -1, 0)},
	{"RightCollarRagdoll",Vector3.new(1, 1, 0)},
	{"RightHipRagdoll",Vector3.new(1, -1, 0)},
	{"RootRagdoll",Vector3.new(0, 0, 0)},
}

local ATTACHMENT_DATA2 = {
	{"LeftCollarRagdoll",Vector3.new(0.5, 1, 0)},
	{"LeftHipRagdoll",Vector3.new(-0.5, 1, 0)},
	{"RightCollarRagdoll",Vector3.new(-0.5, 1, 0)},
	{"RightHipRagdoll",Vector3.new(0.5, 1, 0)},
	{"RootRagdoll",Vector3.new(0, 0, 0)},
}

-- Constuctor
function API.New(Character)
	local self = {}
	self.Character = Character
	
	self.RagdollAttachments = {}
	self.CollisionParts = {}
	
	self.Ragdolled = false
	self.Died = false
	
	local Torso = Character:WaitForChild("Torso")
	local Humanoid = Character:WaitForChild("Humanoid")
	
	self.Torso = Torso
	self.Humanoid = Humanoid
	self.Humanoid.BreakJointsOnDeath = false
	
	self.BodyGyro = Instance.new("BodyGyro")
	self.BodyGyro.MaxTorque = Vector3.new(0, 40000, 0)
	self.BodyGyro.P = 30000
	
	local LeftArm = Character:WaitForChild("Left Arm")
	local RightArm = Character:WaitForChild("Right Arm")
	local LeftLeg = Character:WaitForChild("Left Leg")
	local RightLeg = Character:WaitForChild("Right Leg")
	local Torso = Character:WaitForChild("Torso")
	local RootPart = Character:WaitForChild("HumanoidRootPart")
	
	local LeftHip = Torso:WaitForChild("Left Hip")
	local RightHip = Torso:WaitForChild("Right Hip")
	local LeftShoulder = Torso:WaitForChild("Left Shoulder")
	local RightShoulder = Torso:WaitForChild("Right Shoulder")
	local RootJoint = RootPart:WaitForChild("RootJoint")
	
	self.Motors = {
		RootJoint;
		LeftHip;
		RightHip;
		LeftShoulder;
		RightShoulder;
	};
	
	self.C0s = {}
	for _, v in pairs(self.Motors) do
		self.C0s[v.Name] = {v.C0,v.C1};
	end
	
	self.BodyParts = {
		LeftArm;
		RightArm;
		LeftLeg;
		RightLeg;
		Torso;
		RootPart;
	}
	
	setmetatable(self,API)
	return self
end

-- Methods

-- Clears any stuff to prevent memory leaks
function API:Clear()
	--self.BodyGyro:Destroy()
	self.Ragdolled = false
	self.Died = false
	
	for _,a in pairs(self.RagdollAttachments) do
		a:Destroy()
	end
	
	for _,a in pairs(self.CollisionParts) do
		a:Destroy()
	end
end

-- Setups character everytime they spawn in
function API:Setup(Character)
	--self:Clear()
	self.Character = Character
	
	local Torso = Character:WaitForChild("Torso")
	local Humanoid = Character:WaitForChild("Humanoid")
	
	self.Torso = Torso
	self.Humanoid = Humanoid
	
	local LeftArm = Character:WaitForChild("Left Arm")
	local RightArm = Character:WaitForChild("Right Arm")
	local LeftLeg = Character:WaitForChild("Left Leg")
	local RightLeg = Character:WaitForChild("Right Leg")
	local Torso = Character:WaitForChild("Torso")
	local RootPart = Character:WaitForChild("HumanoidRootPart")
	
	local LeftHip = Torso:WaitForChild("Left Hip")
	local RightHip = Torso:WaitForChild("Right Hip")
	local LeftShoulder = Torso:WaitForChild("Left Shoulder")
	local RightShoulder = Torso:WaitForChild("Right Shoulder")
	local RootJoint = RootPart:WaitForChild("RootJoint")

	self.Motors = {
		RootJoint;
		LeftHip;
		RightHip;
		LeftShoulder;
		RightShoulder;
	};
	
	self.C0s = {}
	for _, v in pairs(self.Motors) do
		self.C0s[v.Name] = {v.Parent,v.C0,v.C1,v.Part0,v.Part1};
	end
	
	self.BodyParts = {
		LeftArm;
		RightArm;
		LeftLeg;
		RightLeg;
		Torso;
		RootPart;
	}
	
	self.BodyGyro = Instance.new("BodyGyro")
	self.BodyGyro.MaxTorque = Vector3.new(0, 40000, 0)
	self.BodyGyro.P = 30000
end

-- Returns a value that tells if player is ragdolled or not
function API:IsRagdolled()
	return self.Ragdolled
end

-- Creates Motor6D welds
function API:CreateWeld(Name,Parent,C0,C1,Part0,Part1)
	local Motor = Instance.new("Motor6D")
	Motor.Name = Name
	Motor.Part0 = Part0
	Motor.Part1 = Part1
	Motor.C0 = C0
	Motor.C1 = C1
	Motor.Parent = Parent
	
	return Motor
end

-- Used for limb collisions when ragdolled
function CreateNewCollisionBall()
	local Part = Instance.new("Part")
	Part.Transparency = 1
	Part.CanCollide = true
	Part.Anchored = false
	Part.Massless = true
	Part.Size = Vector3.new(1,1,1)
	Part.Shape = Enum.PartType.Ball
	
	local Weld = Instance.new("Weld")
	Weld.Part1 = Part
	Weld.Parent = Part
	
	return Part,Weld
end

-- Sets ragdoll, second argument sets ragdoll death meaning cant be unragdolled when dead
function API:Ragdoll(TrueOrFalse,Died)
	if Died then
		self.Died = true
	end
	
	if TrueOrFalse and not self.Ragdolled then
		self.Ragdolled = true
		
		self.Humanoid:SetStateEnabled(Enum.HumanoidStateType.Jumping, false)
		self.Humanoid:SetStateEnabled(Enum.HumanoidStateType.GettingUp, false)
		self.Humanoid:SetStateEnabled(Enum.HumanoidStateType.Ragdoll, true)
		
		-- Beginning Attachments
		local LeftHipAttachment = Instance.new("Attachment")
		LeftHipAttachment.Position = ATTACHMENT_DATA[2][2]
		LeftHipAttachment.Parent = self.BodyParts[5]
		
		local RightHipAttachment = Instance.new("Attachment")
		RightHipAttachment.Position = ATTACHMENT_DATA[4][2]
		RightHipAttachment.Parent = self.BodyParts[5]
		
		local LeftShoulderAttachment = Instance.new("Attachment")
		LeftShoulderAttachment.Position = ATTACHMENT_DATA[1][2]
		LeftShoulderAttachment.Parent = self.BodyParts[5]
		
		local RightShoulderAttachment = Instance.new("Attachment")
		RightShoulderAttachment.Position = ATTACHMENT_DATA[3][2]
		RightShoulderAttachment.Parent = self.BodyParts[5]
		
		local RootAttachment = Instance.new("Attachment")
		RootAttachment.Position = ATTACHMENT_DATA[5][2]
		RootAttachment.Parent = self.BodyParts[5]
		
		-- Ending Attachments
		local LeftHipAttachment2 = Instance.new("Attachment")
		LeftHipAttachment2.Position = ATTACHMENT_DATA2[2][2]
		LeftHipAttachment2.Parent = self.BodyParts[3]
		
		local RightHipAttachment2 = Instance.new("Attachment")
		RightHipAttachment2.Position = ATTACHMENT_DATA2[4][2]
		RightHipAttachment2.Parent = self.BodyParts[4]
		
		local LeftShoulderAttachment2 = Instance.new("Attachment")
		LeftShoulderAttachment2.Position = ATTACHMENT_DATA2[1][2]
		LeftShoulderAttachment2.Parent = self.BodyParts[1]
		
		local RightShoulderAttachment2 = Instance.new("Attachment")
		RightShoulderAttachment2.Position = ATTACHMENT_DATA2[3][2]
		RightShoulderAttachment2.Parent = self.BodyParts[2]
		
		local RootAttachment2 = Instance.new("Attachment")
		RootAttachment2.Position = ATTACHMENT_DATA2[5][2]
		RootAttachment2.Parent = self.BodyParts[6]
		
		-- Ballsocket joints
		local RightHipSocket = Instance.new("BallSocketConstraint")
		RightHipSocket.Parent = RightHipAttachment
		RightHipSocket.Attachment0 = RightHipAttachment
		RightHipSocket.Attachment1 = RightHipAttachment2
		
		local LeftHipSocket = Instance.new("BallSocketConstraint")
		LeftHipSocket.Parent = LeftHipAttachment
		LeftHipSocket.Attachment0 = LeftHipAttachment
		LeftHipSocket.Attachment1 = LeftHipAttachment2
		
		local RightShoulderSocket = Instance.new("BallSocketConstraint")
		RightShoulderSocket.Parent = RightShoulderAttachment
		RightShoulderSocket.Attachment0 = RightShoulderAttachment
		RightShoulderSocket.Attachment1 = RightShoulderAttachment2

		local LeftShoulderSocket = Instance.new("BallSocketConstraint")
		LeftShoulderSocket.Parent = LeftShoulderAttachment
		LeftShoulderSocket.Attachment0 = LeftShoulderAttachment
		LeftShoulderSocket.Attachment1 = LeftShoulderAttachment2
		
		local RootSocket = Instance.new("BallSocketConstraint")
		RootSocket.Parent = RootAttachment
		RootSocket.Attachment0 = RootAttachment
		RootSocket.Attachment1 = RootAttachment2
		
		-- Adding attachments to table
		table.insert(self.RagdollAttachments,LeftHipAttachment)
		table.insert(self.RagdollAttachments,RightHipAttachment)
		table.insert(self.RagdollAttachments,LeftShoulderAttachment)
		table.insert(self.RagdollAttachments,RightShoulderAttachment)
		table.insert(self.RagdollAttachments,RootAttachment)
		
		table.insert(self.RagdollAttachments,LeftHipAttachment2)
		table.insert(self.RagdollAttachments,RightHipAttachment2)
		table.insert(self.RagdollAttachments,LeftShoulderAttachment2)
		table.insert(self.RagdollAttachments,RightShoulderAttachment2)
		table.insert(self.RagdollAttachments,RootAttachment2)
		
		local CollisionBallLeftArm,W1 = CreateNewCollisionBall()
		CollisionBallLeftArm.Parent = self.BodyParts[1]
		W1.Part0 = self.BodyParts[1]
		
		local CollisionBallRightArm,W2 = CreateNewCollisionBall()
		CollisionBallRightArm.Parent = self.BodyParts[2]
		W2.Part0 = self.BodyParts[2]
		
		local CollisionBallLeftLeg,W3 = CreateNewCollisionBall()
		CollisionBallLeftLeg.Parent = self.BodyParts[3]
		W3.Part0 = self.BodyParts[3]

		local CollisionBallRightLeg,W4 = CreateNewCollisionBall()
		CollisionBallRightLeg.Parent = self.BodyParts[4]
		W4.Part0 = self.BodyParts[4]
		
		local IsPlayer = game.Players:GetPlayerFromCharacter(self.Character)
		if not IsPlayer then
			self.Humanoid.PlatformStand = true
		end
		
		table.insert(self.CollisionParts,CollisionBallLeftArm)
		table.insert(self.CollisionParts,CollisionBallRightArm)
		table.insert(self.CollisionParts,CollisionBallLeftLeg)
		table.insert(self.CollisionParts,CollisionBallRightLeg)
		
		for _,a in pairs(self.Motors) do
			a:Destroy()
		end
		
		self.BodyGyro.Parent = self.BodyParts[6]
		
		if not CollectionSerivce:HasTag(self.Character,RagdollTag) then
			CollectionSerivce:AddTag(self.Character,RagdollTag)
		end
	elseif not TrueOrFalse and self.Ragdolled and not self.Died then
		self.Ragdolled = false
		
		self.Humanoid:SetStateEnabled(Enum.HumanoidStateType.Ragdoll, false)
		self.Humanoid:SetStateEnabled(Enum.HumanoidStateType.FallingDown, false)
		self.Humanoid:SetStateEnabled(Enum.HumanoidStateType.GettingUp, true)
		
		local IsPlayer = game.Players:GetPlayerFromCharacter(self.Character)
		if not IsPlayer then
			while self.Humanoid.PlatformStand do
				self.Humanoid.PlatformStand = false
				wait()
			end
		end
		
		-- Removing attachments
		for _,a in pairs(self.RagdollAttachments) do
			a:Destroy()
		end
		
		for _,a in pairs(self.CollisionParts) do
			a:Destroy()
		end
		
		self.Motors = {}
		for Name, Data in pairs(self.C0s) do
			local NM = self:CreateWeld(Name,Data[1],Data[2],Data[3],Data[4],Data[5])
			table.insert(self.Motors,NM)
		end
		
		self.BodyGyro.Parent = nil
		
		if CollectionSerivce:HasTag(self.Character,RagdollTag) then
			CollectionSerivce:RemoveTag(self.Character,RagdollTag)
		end
	end
end

return API
1 Like

Try pausing or stopping the anim when it ragdolls. And play it again when it gets back up.

1 Like

Nope didn’t fix the animations.

1 Like

You should really be disabling the motor6d’s because it uses far less resources than having to instance them again. Plus i believe that deleting the motor6d’s means that when the new one’s are created they aren’t referenced.

2 Likes

I use to have it just be disabled, but the same thing happened.

Where are you loading the animations? Is it in the humanoid?

Using the new animator instance.

Hmm then that is very strange.