Custom Rigs not working properly

  1. What do you want to achieve?
    I want to make my custom rigs work properly on a player (attachement)

  2. What is the issue?
    I have a video right below of my issue. As you can see the upper legs are way too deep’d in for some reason.

  3. What solutions have you tried so far?
    I have tried reworking this script multiple times and I don’t know how to fix it to be brutally honest. I want to make my custom rigs 100% viable without having to add joints by hand (since the game will have a ton of rigs, I want to make the attachements automatically).

This is basically the script that’s making the rig attachement. It’s a bit complicated to understand but many of yall will.

-- Services + Cache
local skinModule = {}
local replicatedStorage = game:GetService("ReplicatedStorage")
local serverScriptService = game:GetService("ServerScriptService")
local skinFolder = replicatedStorage:WaitForChild("Skins")

-- Modules
local giveHeadBillboard = require(serverScriptService:WaitForChild("CharacterHandler"):WaitForChild("giveHeadBillboard"))
local cameraChange = replicatedStorage:WaitForChild("RemoteFolder"):WaitForChild("Gameplay"):WaitForChild("CameraChange")

-- Get the hip offset for specific skins
local SKIN_HIP_OFFSETS = {
	["Basic Guy"] = 2,
	["Basic Girl"] = 2.4,
	-- add more
}

-- Track which players are already in ApplySkin (debounce)
local applyingPlayers = {}

-- Hip correction just for upper legs
local hipDepthOffset = 0.05  -- Z-axis correction
local hipHeightOffset = 0.1  -- Y-axis, raise legs a bit
local hipSpreadOffset = 0.15 -- X-axis, spread legs out

-- Apply offsets for joints (runtime corrections) // Desesparate attempt to make it work. Very bad tho
function skinModule.ApplyOffsets(character)
	local joints = {}

	-- Collect all Motor6Ds
	for _, desc in pairs(character:GetDescendants()) do
		if desc:IsA("Motor6D") then
			joints[desc.Name] = desc
		end
	end

	for _, joint in pairs(joints) do
		local baseC0 = joint.C0
		local offset = Vector3.new(0, 0, 0)

		if joint.Name == "LeftHip" then
			offset = Vector3.new(-hipSpreadOffset, hipHeightOffset, -hipDepthOffset)
		elseif joint.Name == "RightHip" then
			offset = Vector3.new(hipSpreadOffset, hipHeightOffset, -hipDepthOffset)
		end

		joint.C0 = baseC0 + offset

		if offset.Magnitude > 0 then
			print(("🔧 %s | Base:(%.2f, %.2f, %.2f) | Offset:(%.2f, %.2f, %.2f) | Final:(%.2f, %.2f, %.2f)"):format(
				joint.Name,
				baseC0.X, baseC0.Y, baseC0.Z,
				offset.X, offset.Y, offset.Z,
				joint.C0.X, joint.C0.Y, joint.C0.Z
				))
		end
	end
end

-- Function to apply the skin to the player's character
function skinModule.ApplySkin(player, character, skinName)
	-- Debounce: prevent multiple ApplySkin calls in quick succession [DON'T EVER DELETE]
	if applyingPlayers[player] then
		return
	end
	applyingPlayers[player] = true

	print("ApplySkin called for:", player.Name)

	-- Prefer passed skinName, fallback to EquippedSkin
	local equippedSkinName = skinName or player:WaitForChild("EquippedSkin").Value
	local skinModel = skinFolder:FindFirstChild(equippedSkinName) or skinFolder:FindFirstChild("Basic Guy")
	if not skinModel then
		warn("❌ No valid skin found at all. Please contact an admin")
		applyingPlayers[player] = nil
		return
	end

	local newChar = skinModel:Clone()
	newChar.Name = player.Name

	-- Setup humanoid and root part
	local humanoid = newChar:FindFirstChildOfClass("Humanoid") or Instance.new("Humanoid", newChar)
	local hrp = newChar:FindFirstChild("HumanoidRootPart") or Instance.new("Part", newChar)
	if not newChar:FindFirstChild("HumanoidRootPart") then
		hrp.Name = "HumanoidRootPart"
		hrp.Size = Vector3.new(2,2,1)
		hrp.CanCollide = false
		hrp.Anchored = false
	end
	newChar.PrimaryPart = hrp

	-- Force R15 if the skin has R15 body parts
	if humanoid.RigType == Enum.HumanoidRigType.R6 then
		if newChar:FindFirstChild("UpperTorso") and newChar:FindFirstChild("LowerTorso") then
			print("Humanoid was R6, forcing to R15 for:", player.Name)
			humanoid.RigType = Enum.HumanoidRigType.R15
		end
	end

	-- Unanchor all parts
	for _, part in ipairs(newChar:GetDescendants()) do
		if part:IsA("BasePart") then
			part.Anchored = false
			part.CanCollide = false
		end
	end

	-- Setup Motor6Ds (only if RigType is R15) // I believe this is where the bug is
	if humanoid.RigType == Enum.HumanoidRigType.R15 and not newChar:GetAttribute("JointsFixed") then
		local joints = {
			{part0="HumanoidRootPart", part1="LowerTorso", name="Root"},
			{part0="LowerTorso", part1="UpperTorso", name="Waist"},
			{part0="UpperTorso", part1="Head", name="Neck"},
			{part0="UpperTorso", part1="LeftUpperArm", name="LeftShoulder"},
			{part0="LeftUpperArm", part1="LeftLowerArm", name="LeftElbow"},
			{part0="LeftLowerArm", part1="LeftHand", name="LeftWrist"},
			{part0="UpperTorso", part1="RightUpperArm", name="RightShoulder"},
			{part0="RightUpperArm", part1="RightLowerArm", name="RightElbow"},
			{part0="RightLowerArm", part1="RightHand", name="RightWrist"},
			{part0="LowerTorso", part1="LeftUpperLeg", name="LeftHip"},
			{part0="LeftUpperLeg", part1="LeftLowerLeg", name="LeftKnee"},
			{part0="LeftLowerLeg", part1="LeftFoot", name="LeftAnkle"},
			{part0="LowerTorso", part1="RightUpperLeg", name="RightHip"},
			{part0="RightUpperLeg", part1="RightLowerLeg", name="RightKnee"},
			{part0="RightLowerLeg", part1="RightFoot", name="RightAnkle"},
		}

		for _, joint in ipairs(joints) do
			local part0 = newChar:FindFirstChild(joint.part0)
			local part1 = newChar:FindFirstChild(joint.part1)
			if part0 and part1 and not part0:FindFirstChild(joint.name) then
				local motor = Instance.new("Motor6D")
				motor.Name = joint.name
				motor.Part0 = part0
				motor.Part1 = part1

				local baseC0 = part0.CFrame:ToObjectSpace(part1.CFrame)

				local offsets = {
					LeftHip  = CFrame.new(0, 0, -hipDepthOffset),
					RightHip = CFrame.new(0, 0, -hipDepthOffset),
				}

				local correction = offsets[joint.name] or CFrame.new()
				motor.C0 = baseC0 * correction
				motor.C1 = CFrame.new()
				motor.Parent = part0
			end
		end
		newChar:SetAttribute("JointsFixed", true)
	end

	-- Save old character pivot and destroy old character
	local oldCFrame
	if character then
		if character.PrimaryPart then
			oldCFrame = character:GetPivot()
		else
			oldCFrame = character:GetModelCFrame()
			print("⚠️ Character missing PrimaryPart, using GetModelCFrame for", player.Name)
		end
		character:Destroy()
	end

	-- Parent new character and assign to player
	newChar.Parent = workspace
	player.Character = newChar

	-- Restore position
	if oldCFrame then
		newChar:PivotTo(oldCFrame + Vector3.new(0,3,0))
	end

	-- Humanoid setup
	if humanoid then
		humanoid.HipHeight = SKIN_HIP_OFFSETS[equippedSkinName] or 2
		
		--humanoid.HipHeight = 2 -- testing

		if not newChar:FindFirstChild("Animate") then
			local animateScript = replicatedStorage:WaitForChild("CharacterAssets"):FindFirstChild("Animate")
			if animateScript then
				animateScript:Clone().Parent = newChar
			end
		end

		local head = newChar:FindFirstChild("Head") or newChar:WaitForChild("Head", 5)
		if head then
			giveHeadBillboard.SetValues(player, newChar)
			cameraChange:FireClient(player, humanoid)
		else
			
		end
	end

	-- Release debounce after short delay
	task.delay(0.5, function()
		applyingPlayers[player] = nil
	end)
end

return skinModule

If you need anything to help me fix this bug lmk.

There’s the video:

1 Like

Sorry for bumping I still have the same issue 2 days after…

1 Like