Custom Selection Box/ Lasso Tool

How would I go about getting all of the parts in a GUI frame efficiently?
I have tried looking at the source code for BuildV4, but it is just all over the place.

1 Like

I don’t think there’s a plugin for this but it’s definitely scriptable.

Just script a point from first click and create a selection box all the way to mousebutton1 release point
Then detect all ui elements touching selection box with
https://www.robloxdev.com/articles/2D-Collision-Detection

1 Like

This is not an ideal starting point. This article is on 2D UI Collisions. The OP is asking how you can create a draggable selection frame that selects parts in the 3D Frame like F3X, BuildV4 or the default dragger.

You have it about right regarding how to go about making the GUI - wherever the player’s mouse is clicked down should be marked as Point A of the frame and wherever the player’s mouse lets go as Point B, forming a rectangular or square “selection area”. As for how to get that to translate over to the 3D side of things, I haven’t a clue but it’s not this article.

1 Like

I have a feeling you read the word BuildV4 and that’s it.

1 Like

Hey watch it!

All of the code related to this (except maybe for driving user input) is in SelectionScript between lines 191 and 255 (it’s nested in a do block).


The major themes: get the ray to your mouse when your drag begins, and a corresponding ray to your mouse when the drag ends. When you’re done, loop through every part you care about, and if they are between these two rays, they’re part of the selection.

--On start dragging...
local rel=workspace.CurrentCamera.CoordinateFrame:pointToObjectSpace(Mouse.Hit.p);
Slope1=Vector2.new(rel.x/-rel.z, rel.y/-rel.z)
--On stop dragging...
local rel=workspace.CurrentCamera.CoordinateFrame:pointToObjectSpace(Mouse.Hit.p);
Slope2=Vector2.new(rel.x/-rel.z, rel.y/-rel.z)
--Then we do the comparisons, which I extracted to another function.
selectedParts = Search(ALL_PARTS, Slope1, Slope2);

local function Search(Sources, a1, a2)
	--Regardless of whether we started in the top left, top right, etc., we want
	--a1 to be the top left corner and a2 to be the bottom right corner.
	a1, a2 = Vector2.new(math.min(a1.x, a2.x), math.min(a1.y, a2.y)), Vector2.new(math.max(a1.x, a2.x), math.max(a1.y, a2.y));

	local Found = {};

	local rel, x, y;
	for v, i in pairs(Sources) do

		--This does the same pointToObjectSpace logic as up above.
		rel = workspace.CurrentCamera.CoordinateFrame:pointToObjectSpace(v.Position);
		x, y = rel.x / -rel.z, rel.y / -rel.z;

		--Are we within bounds?
		if a1.x < x and x < a2.x and a1.y < y and y < a2.y and Rel.z < 0 then
			table.insert(Found, v);
		end
	end
	return Found;
end

If you really don’t “do” rays, you can do something similar where you map parts to the pixel on the screen and compare within the bounds just like above. I chose rays because I like rays. ¯\_(ツ)_/¯

Edit: I wrote a free-form lasso select once and would share it with you to punish you with math, but I can’t find it at the moment. :frowning:

23 Likes

You might be able to use ScreenPointToRay on two opposite corners of the rectangle drawn on screen, and create a Region3 from the furthest point on the first ray to the origin of the second ray, then use FindPartsInRegion3 to get everything within that region.

Too busy right now to write a proof of concept but the concept seems sound.

image

3 Likes

does it work? I really need a gui selectin box

Hi,

Sorry for the late reply,

I would recommend the solution suggested by blobbyblob. If this doesn’t work for you PeZsmistic’s solution wouldn’t work unless the Camera.FOV was 90. This is because, believe it or not, all camera instances look at instances with a given FOV angle (set by Camera.FOV). If you would want to use this approach universally, you would have to get a pyramid BasePart, set it to its proper size, somehow alter its curve so it matches the camera’s, and query it with Workspace:GetPartsInPart(). This is incredibly hard, and after many nights of frustration, I gave up.

However, a better alternative I found is using Camera:WorldToScreenPoint() as I did in my game. Get the corners of the objects you want to select, and check if they’re in bounds using that function. It worked pretty well for me, although it isn’t perfect.

Code snippet from my game:

	local Found = {}
	local Inset = game:GetService("GuiService"):GetGuiInset()

	local MaxX = math.max(p1.X,p2.X)
	local MaxY = math.max(p1.Y,p2.Y)
	local MinX = math.min(p1.X,p2.X)
	local MinY = math.min(p1.Y,p2.Y)

	for _ ,obj in ipairs(objs) do
		for num, worldLocation in pairs(getSearchLocations(obj)) do -- use your function for this to get positions of each object to query.
			local vector, inViewport = workspace.Camera:WorldToViewportPoint(worldLocation)
			vector-=Vector3.new(Inset.X,Inset.Y,0)

			if inViewport and MinX<vector.X and MaxX>vector.X and MinY<vector.Y and MaxY>vector.Y then
				table.insert(Found, obj)
				break
			end
		end
	end```
2 Likes

thank you! It work really well. Ty :smiley: