How do I change a part size with the origin not being at center?

Let’s say there’s a part sized 1,1,1

if I change it to 1,1,2, it will result as following: (Notice the grids on the baseplate)

How do I change it size and position as following by using script? I want it to extend 1 stud on either left or right side but not extend 0.5 studs on both side.
image

9 Likes

I haven’t used this in a while but something like this.
Part:Resize(Enum.NormalId.Left, 1)
https://developer.roblox.com/en-us/api-reference/function/BasePart/Resize
This won’t resize if there’s another part in the way. like if there is a Part that is less than 1 stud from the left normal it won’t resize.

You can also resize and set a new CFrame at the same time.
Part.Size = Vector3.new(Part.Size.X + 1,Part.Size.Y,Part.Size.Z)
Part.CFrame = Part.CFrame * CFrame.new(.5,0,0)

24 Likes

@johnnygadget already described it, but the best way I can think of doing it is by moving the part half the difference in size:

local newSize = Vector3.new(1, 1, 2)
local deltaSize = newSize - part.Size --> (0, 0, 1)

part.Size = newSize

-- moving the part forward half the distance:
part.Position = part.CFrame * deltaSize/2

-- moving the part back half the distance:
part.Position = part.CFrame * -deltaSize/2

The best way to understand why this works is with a graph.

Say you have a square made up of the lines x = 0, x = 1, y = 0, y = 1:

You can find the center by taking an average of the x’s and y’s:

origin.X = (x0 + x1) / 2 --> (0 + 1) / 2 --> 1/2
origin.Y = (y0 + y1) / 2 --> (0 + 1) / 2 --> 1/2

so, if we increase x1 by 1 to create a rectangle of your shape:

Our new center of the shape would be (1, 1/2):

origin.X = (0 + 2) / 2 --> 1
origin.Y = (0 + 1) / 2 --> 1/2

If you haven’t noticed from the picture, x1 moved one unit, while the center moved half that amount. We can apply this to our problem by making the change in the part’s position proportional to how much the size has changed, and in order to make that change in position lock to a surface, we can use the CFrame to apply the size offset in local space, which results in the code from the beginning of this post.

I hope that helps with understanding the math better, if you didn’t already understand it.

13 Likes
local NORMAL_DIRECTIONS = {
	[Enum.NormalId.Top] = Vector3.new(0, 1, 0);
	[Enum.NormalId.Bottom] = Vector3.new(0, -1, 0);
	[Enum.NormalId.Back] = Vector3.new(0, 0, 1);
	[Enum.NormalId.Front] = Vector3.new(0, 0, -1);
	[Enum.NormalId.Right] = Vector3.new(1, 0, 0);
	[Enum.NormalId.Left] = Vector3.new(-1, 0, 0);
}

return function(part, normal, delta)
	local Direction = NORMAL_DIRECTIONS[normal]
	part.Size = part.Size + Vector3.new(math.abs(Direction.X), math.abs(Direction.Y), math.abs(Direction.Z))*delta
	part.CFrame = part.CFrame + NORMAL_DIRECTIONS[normal]*delta/2
end

Here is a solution if you just want to size it in one direction at a time. You can put this code into a module and then call that return function with the part, the normal which is a NormalId Enum, and then delta or the change in size.

Example:

local ReplicatedStorage = game:GetService("ReplicatedStorage")
local Resize = require(ReplicatedStorage.Resize)

Resize(workspace.Part, Enum.NormalId.Right, 10)

EDIT: Modified it to work correctly with the negative directions.

16 Likes

Actually my ultimate goal is to scale a R6 player’s arm, I think using Part.Resize will be the simplest way to perform it.
In my scenario, I resized the Arm first, then change the C1 of Left Shoulder Motor6D, anyways, thanks everyone for assisting me!

3 Likes