-
What do you want to achieve?
I want to make my custom rigs work properly on a player (attachement) -
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. -
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: