Resizing multiple parts in the same direction with different rotation

Hey there, I have a building game in Roblox which is called “Wubby” and it has a resize tool inside of it. The resize tool supports multiple selection, however, if a non-parallel rotated block with the first block (like 0, 0, 0 vs 0, 90, 0) is inside the multiple selection, it’ll just resize that block towards its own axis instead of following the first part’s axis.

What I’ve been searching for is something like this:
0bf564c12e27a01fa2814ecad30c5e4e84099c27

btw, the “ResizeObject” mode you can see in this gif is nearly the same thing that is happening to me, I want to make it work like “ResizeWorld”

Since my resize tool system works with normals, what I’ve been looking for is something like this:

(I’m not really good explaining, haha) I was looking for someway (WITHOUT RAYCASTING, I know how to use raycasting but I don’t want to use raycasting for this case because of performance issues) to adapt a normal to another part’s orientation or CFrame. In the first segment of that image you can see a part pointing to one direction, and in the next one you can see a rotated part which also keeps the same direction, however it’s using a different normal to be in the same direction as the first part.

Any help is welcome, I’ve been 3 days straight searching a solution for this and I haven’t found anything that suits my needs!

The first thing that showed up when I searched for “global resizing roblox” was the solution of what you need: How can I scale or size a part on a global axis, instead of local, via script - #4 by SalmonJumped

If that’s not what you want, please let me know

1 Like

If I recall correctly, CFrame has a built-in function called VectorToObjectSpaxe that can converts your normal vector relative to the part’s CFrame

Hey I’ve looked at the link you sent and I don’t quite understand that answer. I need to calculate 2 things when resizing: the CFrame and the size, that answer only gives me the size, and also has an example that makes no sense whatsoever (why are you getting the part’s size to then apply the method you’re trying to showcase?)

Still, thank you for answering because I completely forgot WorldSpaces and ObjectSpacess were a thing!

update 2025: fixed the script a little bit so it’s more readable cuz this is very useful ngl

original post:

Hey, I forgot to respond to this long time ago. Someone gave me this script through DMs that completely fixes my problem:

local PartsInformation = {}
local _INCREMENT = 1 -- your grid size ig

local Blocks = {} -- the blocks you're gonna resize

local function Snap(x, inc) -- if someone can simplify this function pls lmk idk what it does
	if inc == 0 then return end
	local rem = x%inc

	if 2*rem ~= inc then
		return (2*rem < inc and x or x + inc) - rem
	end

	return ((x - rem)%(2*inc) == 0 and x or x + inc) - rem
end

local FACE_MULTIPLIER = {
	[Enum.NormalId.Top] = 1;
	[Enum.NormalId.Bottom] = -1;
	[Enum.NormalId.Front] = -1;
	[Enum.NormalId.Back] = 1;
	[Enum.NormalId.Left] = -1;
	[Enum.NormalId.Right] = 1;
};

local facecomponent = {
	[Enum.NormalId.Top] = "Y",
	[Enum.NormalId.Bottom] = "Y",
	[Enum.NormalId.Front] = "Z",
	[Enum.NormalId.Back] = "Z",
	[Enum.NormalId.Left] = "X",
	[Enum.NormalId.Right] = "X",
}
		
local faceList = {
	Enum.NormalId.Back;
	Enum.NormalId.Bottom;
	Enum.NormalId.Front;
	Enum.NormalId.Left;
	Enum.NormalId.Right;
	Enum.NormalId.Top;
}

local faceVectors = {}
for index, face in faceList do
	faceVectors[index] = Vector3.FromNormalId(face)
end
	
local first, inc, FirstNormal
local LastDistance = 0

-- OverlayHandles is supposed to be ur handles
			
OverlayHandles.MouseButton1Down:Connect(function(face)
	LastDistance = 0
	first = Blocks[1]

	table.clear(PartsInformation)

	FirstNormal = first.CFrame:VectorToWorldSpace(Vector3.fromNormalId(face))

	for _,part in Blocks do
		local partCFrame = part.CFrame
		local rIndex, rDot = 1, -inf

		for i, test in faceVectors do
			local td = part.CFrame:VectorToWorldSpace(test):Dot(FirstNormal)

			if td > rDot then
				rDot = td
				rIndex = i
			end
		end

		local faceObj = faceList[rIndex]
		local isTruss = part:IsA('TrussPart')

		PartsInformation[part] = {
			partCFrame;
			part.Size;
			isTruss;
			isTruss and 2 or MIN_SIZE;
			FACE_MULTIPLIER[faceObj];
			FACE_COMPONENT[faceObj];
			faceVectors[rIndex];
		}
	end
	inc = Snap(_INCREMENT, 1)
	if inc == 0 then
		inc = 1
	end
end)

			
OverlayHandles.MouseDrag:Connect(function(face, distance)
	local ddis = Snap(distance, _INCREMENT)
	if ddis == LastDistance then return end
	LastDistance = ddis

	for part, info in PartsInformation do
		local sz, isTruss, ffm, face_mult, face_size, face_vec = info[2], info[3], info[4], info[5], info[6], info[7]

		local dis = distance*face_mult
		local mult = Snap(dis, isTruss and inc*ffm or cinc)

		local mod = sz[face_size] + mult*face_mult < ffm and face_vec*((ffm - sz[face_size])*face_mult) or face_vec*mult

		part.Size = sz + mod
		part.CFrame = info[1]*cf(mod*face_mult/2)
	end
end)

This topic was automatically closed 14 days after the last reply. New replies are no longer allowed.