How Should I Resize Individual Limbs on R15?

So I am at a little dilemma here about how I can resize the individual body parts (torso, right arm, left leg, etc.) without leaving any gaps as seen here:
Screenshot_3

Ideally I would like to duplicate the same behavior that for example BodyHeightScale does when changing its values but only for individual body parts. What I am looking for is a way to calculate each Motor6D to clamp to each other, thus eliminating the gaps.

3 Likes

When you resize these parts you’ll also want to move the C0/C1 on the Motor6D joints that connect the parts together. If you move these joints closer together you’ll get the look you’re wanting.

So to make it more clear, you’ll need to find the joints that correspond to the parts you’re resizing:
For the RightHand you have RightWrist, for the RightLowerArm you have RightElbow, for the RightUpperArm you have RightShoulder.

One thing to note is that you will have tor move this via script.

That code looks something like:

Character.RightHand.RightWrist.C0 = CFrame.new(x1, y1, z1)
Character.RightHand.RightWrist.C1 = CFrame.new(x2, y2, z2)

I played around with this. Here are some images showing my results:

Here are the Motor6D joints I was talking about (below):

And after I adjusted the C0/C1 values a bit (values will be different depending on the size you want to make the limb)

Hope this helps!

3 Likes

I’ve played around with the joints, but I couldn’t get the body parts to line up consistently and reliably. What were the calculations that you performed in your example?

The positions are going to depend on the individual sizes on the limbs as well as body height. I just picked the C0 and C1 that worked for my character after I shrank my limb down to exactly half its normal size. Are you wanting to come up with something that would work for any size limb in any size body?

What I gave you should answer your question I believe. It does take some playing around with to get it right but I’m not totally certain of the full scope of your needs.

Parts need to dynamically clamp together based on length, yes. Exactly how scaling the entire R15 works.

Seems like you understand the basic building blocks of how this concept could be implemented. Are you wanting someone to write it for you? Have you tried to do it yourself yet? I would be happy to help you debug your code if you need some help.

I can’t seem to figure out the right calculations to perform on C0 and C1 with the joints, that’s what I am seeking a solution for.

It helps to have a bit of insight into the way C0/C1 for character joints work. Simply:

Joint.C0 = Part0.Attachment.CFrame
Joint.C1 = Part1.Attachment.CFrame

I would recommend scaling the attachments with the part so everything else that relies on them continues to behave as expected (e.g. a shoulder accessory isn’t floating high above the mini shoulder when you add it). So how can you scale attachments?

On the left we have a Part of Size=<2,4,2>. The relative position of its top-right edge to the center is X=1,Y=2, so we set the attachment Position to <1,2,0>. When we resize the part (middle) to 50% of its original size, the attachment remains where it was. The relative to the position of its top-right edge has also been halved, so we just multiply the attachment position by the same factor to get <0.5,1,0> (right).

So finally, your code will look something like:

local scaleFactor = Vector3.new(0.5,0.5,0.5)
for _,part in pairs(partsToRescale) do
    part.OriginalSize.Value = part.OriginalSize.Value
    part.Size = part.Size * scaleFactor

    for _,attachment in pairs(part:GetChildren()) do
        if attachment:IsA("Attachment") then
            if attachment:FindFirstChild("OriginalPosition") then
                attachment.OriginalPosition.Value = attachment.OriginalPosition.Value * scaleFactor
            end

            attachment.Position = attachment.Position * scaleFactor
        end
    end

    local joint,attachment0,attachment1 = getRigComponentsForPart(part)
    joint.C0 = attachment0.CFrame
    joint.C1 = attachment1.CFrame
end

-- Update accessories to use new attachment scales (you may need to disconnect them from the character first)
for _,accessory in humanoid:GetAccessories() do
    humanoid:AddAccessory(accessory)
end

By setting the OriginalSize/Position values of parts/attachments, your characters will still work with built-in humanoid scaling (e.g. if you make the arm half its original size, and then the character 2x its original size with the Body_____Scale values, your avatar’s arm will still be 0.5x of what it normally would be for a character of that scale)

getRigComponents is just an abstraction to signify that Roblox provides no good way of figuring out which attachment corresponds to which, so you will probably need to recurse through the avatar before rescaling to build a data structure that allows you to figure this out.

14 Likes

Alright I think I got the gist of how I should tackle this. Thanks for the help!

I was able to get things working accordingly with this snippet of code:

local function CreateTag(Name,Class,Value,To)
	local tag = Instance.new(Class)
	tag.Name = Name
	tag.Value = Value
	tag.Parent = To
	
	return tag
end

local function updateAttachments(part)
	local Attachments = part:GetChildren()
	
	for _,attachment in pairs(Attachments) do
		if attachment:IsA("Attachment") then
		local AttachmentPos = attachment.Position
		local XPos = AttachmentPos.X+part.Size.X/2
		local YPos = AttachmentPos.Y+part.Size.Y/2
		local ZPos = AttachmentPos.Z+part.Size.Z/2
		local Origin = attachment:FindFirstChild("Origin") or CreateTag("Origin","Vector3Value",Vector3.new(XPos/part.Size.X,YPos/part.Size.Y,ZPos/part.Size.Z),attachment)
		
		attachment.Position = Vector3.new(
			(part.Size.X*Origin.Value.X)-part.Size.X/2,
			(part.Size.Y*Origin.Value.Y)-part.Size.Y/2,
			(part.Size.Z*Origin.Value.Z)-part.Size.Z/2
		)
		end
		
	end
end

updateAttachments() is called before (to update tags) and after the part is resized, then finally BuildRigFromAttachments() is used to reattach the modified attachments. Thanks for the help, everyone!

2 Likes

Hello, it appears using your script does not work.
It scales my arm but not in a correct way, and it yields this error:
image

Here is the script:

function getRigComponentsForPart(part)
	local e = {}
	for i,v in pairs(part:GetChildren()) do
		if v:IsA('JointInstance') then
			table.insert(e,v)
		end
	end
	return e
end
script.Parent.ClickDetector.MouseClick:Connect(function(p)
	local scaleFactor = Vector3.new(1,2,1)
	local partsToRescale = {
		p.Character.RightUpperArm,
		p.Character.RightLowerArm
	}
	local humanoid = p.Character.Humanoid
	for _,part in pairs(partsToRescale) do
		part.OriginalSize.Value = part.OriginalSize.Value
		part.Size = part.Size * scaleFactor

		for _,attachment in pairs(part:GetChildren()) do
			if attachment:IsA("Attachment") then
				if attachment:FindFirstChild("OriginalPosition") then
					attachment.OriginalPosition.Value = attachment.OriginalPosition.Value * scaleFactor
				end

				attachment.Position = attachment.Position * scaleFactor
			end
		end

		local joint,attachment0,attachment1 = getRigComponentsForPart(part)
		joint.C0 = attachment0.CFrame
		joint.C1 = attachment1.CFrame
	end

	-- Update accessories to use new attachment scales (you may need to disconnect them from the character first)
	for _,accessory in humanoid:GetAccessories() do
		humanoid:AddAccessory(accessory)
	end
end)

Here is what it looks like ingame:

is it possible for you to indicate which line in the supplied script is line 32?

I know im a few years late to the party, but I figured out how to correspond the motor 6d’s with the updated attachment after the part size change.
Now, for a motor 6d there’s always 2 common patterns.
1, C1 of a motor 6d is always the CFRAME of a child of motor 6d’s parent (an attachment)
2, C0 of a motor 6d is always the CFRAME of motor 6d Part0’s child (an attachment within the child)

now, with these basic patterns we can create a data structure (which helps corresponds which attachment is which) like you proposed BEFORE the size change. The reason why its crucial that it is before is because of the way I create the data structure

How I do it is simply make conditions checking for the patterns I stated above for a part’s motor 6d. Then, I create a string value called “attachment0” and “attachment1” and parent it to the motor 6d. these values represent an attachment’s CFRAME which correlates to the motor 6d’s c0/c1.

here’s the code that needs to be executed before echo scales the attachments:

if not char:FindFirstChild("hasJointData") then
									local hasJointData = Instance.new("StringValue",char)
									hasJointData.Name = "hasJointData"
									for _,v in next, char:GetChildren() do
										if v:FindFirstChildWhichIsA("Motor6D") then
											local attachment0Name = Instance.new("StringValue",v:FindFirstChildWhichIsA("Motor6D"))
											attachment0Name.Name = "attachment0Name"
											local attachment1Name = Instance.new("StringValue",v:FindFirstChildWhichIsA("Motor6D"))
											attachment1Name.Name = "attachment1Name"
											local mainJoint = v:FindFirstChildWhichIsA("Motor6D")
											for _, v in next, v:FindFirstChildWhichIsA("Motor6D").Part0:GetChildren() do
												if v:IsA("Attachment") then
													if v.CFrame.Position == mainJoint.C0.Position then
														attachment0Name.Value = v.Name
													end
												end
											end
											for _, v in next, mainJoint.Parent:GetChildren() do
												if v:IsA("Attachment") then
													if v.CFrame.Position == mainJoint.C1.Position then
														attachment1Name.Value = v.Name
													end
												end
											end
										end
									end

Sorry if its formatted bad,
also this solution was created under an hour so I know its pretty shabby

The problem is is that this is far from perfect, the motor 6d’s I think based on my few observations, its placed perfectly where it should, the problem with it is the motor6d placement is wrong. and its wrong because of the attachments because the motor 6d Cframe change is relative of the attachments, and since the attachments are placed wrong the motor 6d C0 and c1 is incorrect.

So we have to fix the attachments cframe…

we have to figure out how the attachments are placed on the character in order to figure out how to actually make the attachments cframe correct, since multiplying the cframe of an attachment by a factor will not work.