Ball joint between bones

Hello everyone!

It’s been a while since I asked a question here on the forums, but I have been messing around with the new skinned meshes, particularly bones, and came across something I didn’t know how to do inside the Roblox engine.

Is there a way that, instead of moving bones in the form of an animation like you might Motor6ds, that you could allow bones to move freely connected to each other, in a ragdoll-y sort of fashion. Basically, connecting bones together with ball joints. You can easily connect separate parts together with “Ball in Socket” joints, but how would this work with bones in a rig?

I think the creator of this post was trying to do the same thing, but they never posted the solution:

Thanks, ExcessEnergy

1 Like

I tried making ballsocket constraints for a ragdoll like normal

and made normal attachments on the bodyparts which I then copied the worldposition of the bone and set the position on the corresponding attachment.

After all of this I made a script that constantly sets the position of the bones to the position of the attachments and that seemed to achieve a skinned ragdoll effect.

Now I havnt tried this on any character models yet so this could or could not work for a player model but that was how I ragdolled a skinned mesh oval which had 3 seperate pieces since I wanted to test this out aswell.

1 Like

Your idea of setting bones to the positions of parts connected by ball joints was what I was thinking of trying next. If you have any code or examples of doing it yourself, I would love to see.

I’m really surprised other people haven’t brought this topic up before, as you do it quite commonly in other engines such as Unity.

i actually never got the solution because it glitched and stretched too far and didn’t look right but i can give you the script i used.

local rootpartname = "RootPart" -- skinned mesh's rootpart
local bonerootpartname = "ValveBiped.Bip01_Pelvis" -- bone root part
local list = {}
for i,v in pairs(script.Parent:WaitForChild(rootpartname):GetDescendants()) do
	if v:IsA("Bone") then
		local op = v.WorldPosition
		local oo = v.WorldOrientation
		list[v.Name] = {v.Name, v.Orientation.X/1.5, v.Orientation.Y/1.5, v.Orientation.Z/1.5}
		local fp = Instance.new("Part", script.Parent)
		fp.Transparency = 1
		fp.CanCollide = true
		fp.Name = v.Name
		fp.Massless = true
		fp.CustomPhysicalProperties = PhysicalProperties.new(0, 0, 0)
		fp.Anchored = false
		if v.Parent:IsA("Bone") then
			fp.CFrame = CFrame.new(op, v.Parent.WorldPosition)
			fp.Size = Vector3.new(0.5, 0.5, (op-v.Parent.WorldPosition).Magnitude)
		else
			fp.Position = op
			fp.Size = Vector3.new(0.5, 0.5, 0.5)
		end
		local attachment = Instance.new("Attachment", fp)
		attachment.WorldPosition = op
		attachment.CFrame = attachment.CFrame * CFrame.new(0, 0, -script.Parent:WaitForChild(v.Name).Size.Z)
		attachment.WorldOrientation = oo
		attachment.Name = v.Name .."attachment"
		local attachment1 = Instance.new("Attachment", fp)
		local cor = coroutine.create(function()
			attachment1.Parent = script.Parent:WaitForChild(v.Parent.Name, 1)
			attachment1.CFrame = attachment1.CFrame * CFrame.new(0, 0, script.Parent:WaitForChild(v.Parent.Name).Size.Z)
			attachment1.Name = v.Name .."attachment2"
			local bs = Instance.new("BallSocketConstraint", fp)
			bs.Name = "bs"
			bs.Visible = false
			bs.LimitsEnabled = true
			bs.TwistLimitsEnabled = true
			bs.TwistLowerAngle = -20
			bs.TwistUpperAngle = 20
			bs.Attachment0 = attachment
			bs.Attachment1 = attachment1
			local hc = Instance.new("HingeConstraint", fp)
			hc.Name = "hc"
			hc.Visible = false
			hc.LimitsEnabled = true
			hc.LowerAngle = -10
			hc.UpperAngle = 10
			hc.Attachment0 = attachment
			hc.Attachment1 = attachment1
			local rc = Instance.new("RodConstraint", fp)
			rc.Name = "rc"
			rc.Visible = false
			rc.Limit = 1
			rc.Attachment0 = attachment
			rc.Attachment1 = attachment1
			wait()
			for i,v in pairs(script.Parent:GetChildren()) do
				if v:IsA("BasePart") then
					local nc = Instance.new("NoCollisionConstraint", fp)
					nc.Name = "nc"
					nc.Part0 = fp
					nc.Part1 = v
				end
			end
		end)
		coroutine.resume(cor)
	end
end
local wc = Instance.new("WeldConstraint", script.Parent:WaitForChild(bonerootpartname))
wc.Part0 = script.Parent:WaitForChild(rootpartname)
wc.Part1 = script.Parent:WaitForChild(bonerootpartname)
for i,v in pairs(script.Parent:GetChildren()) do
	if v:IsA("MeshPart") then
		v.CanCollide = false
	end
end
game:GetService("RunService").Heartbeat:Connect(function()
	for i,v in pairs(script.Parent:WaitForChild(rootpartname):GetDescendants()) do
		if v:IsA("Bone") then
			--script.Parent:WaitForChild(v.Name).Size = Vector3.new(0.5, 0.5, (script.Parent:WaitForChild(v.Name).Position-script.Parent:WaitForChild(v.Parent.Name).Position).Magnitude)
			v.WorldPosition = script.Parent:WaitForChild(v.Name).Position
			if v.Parent:IsA("Bone") then
				v.CFrame = CFrame.new(v.Position, script.Parent:WaitForChild(v.Parent.Name).Position) --+ Vector3.new(list[v.Name][1], list[v.Name][2], list[v.Name][3])
			end
			if v.Parent:IsA("Bone") then
				--local x, y, z = CFrame.new(script.Parent:WaitForChild(v.Name).Position, script.Parent:WaitForChild(v.Parent.Name).Position):ToEulerAnglesYXZ()
				--v.CFrame = CFrame.new(v.Position, script.Parent:WaitForChild(v.Parent.Name).Position)
			end
		end
	end
end)