How would I go to getting the models selected with a frame?

I am trying to achieve a model selecting system, like those used in roblox studio where you click and drag.
The issue about this though, is that I don’t know what I should do to get the models selected.
So far, I have searched google and also the developer hub, but to no avail.

Here’s an example of what I mean by selection:

image not mine, googled it

TL;DR: I need a way of getting models selected with a frame on a gui.
I got the basic frame click and drag, but it doesn’t know what got selected.
Could anyone point me to the solution?

Thanks for reading,
bryancololee | Scripter

3 Likes

I think you maybe have to use the Mouse Hover script so when it hovers over the model with the MouseButton1 Holded, the blue selector is visible.

But the thing is, they dont have to hover over it, and im not sure how I would get to doing that.

If you already have the click-and-drag effect down, it seems the next step would be to create a method to determine if two frames are intersecting. Each mouse movement event should then run this method to find the frames that are colliding with the selection frame.

1 Like

The thing is, i’m trying to get the 3d models selected, not 2d gui objects.

2 Likes

Edit 8/9/2021: I’ve cleaned up some the wording and improved the code example a bit.


Have you seen this post it pretty much answers the Main part of your question:

Probably one of the easiest and cheapest ways to get a model, in particular, would be just to get its’ AABB or Bounding Box instead of its’ individual parts. Here’s a code example with the math used above:

local UserInputService = game:GetService("UserInputService")
local RunService = game:GetService("RunService")
local GuiService = game:GetService('GuiService')


local Inset = GuiService:GetGuiInset()
local Camera = workspace.CurrentCamera
local selectionFrame = script.Parent
local searchTable = workspace.searchable_objs:GetChildren() --// array of searchable objs
local mouseDown = false
local lastPos 


local function To3dSpace(pos)
	return Camera:ScreenPointToRay(pos.x, pos.y).Origin 
end


local function CalcSlope(vec)
	local rel = Camera.CFrame:pointToObjectSpace(vec)
	return Vector2.new(rel.x/-rel.z, rel.y/-rel.z)
end


local function Overlaps(cf, a1, a2)
	local rel = Camera.CFrame:ToObjectSpace(cf)
	local x, y = rel.x / -rel.z, rel.y / -rel.z

	return (a1.x) < x and x < (a2.x) 
		and (a1.y < y and y < a2.y) and rel.z < 0 
end


local function Swap(a1, a2)
	return 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))
end


local function Search(objs, p1, p2)
	local Found = {}
	local a1 = CalcSlope(p1)
	local a2 = CalcSlope(p2)
	
	a1, a2 = Swap(a1, a2)
	
	for _ ,obj in ipairs(objs) do
		
		local cf = obj:IsA("Model") --// for models use GetBoundingBox() 
			and obj:GetBoundingBox() or obj.CFrame
		
		if Overlaps(cf,a1, a2) then
			table.insert(Found, obj)
		end
	end

	return Found
end

	
	
UserInputService.InputBegan:Connect(function(input) 
	if input.UserInputType == Enum.UserInputType.MouseButton1 then
		lastPos = Vector2.new(input.Position.x, input.Position.y) 
		mouseDown = true
	end
end)


UserInputService.InputEnded:Connect(function(input)
	if input.UserInputType == Enum.UserInputType.MouseButton1 then
		
		local pos = Vector2.new(input.Position.x, input.Position.y)
		local result = Search(searchTable, To3dSpace(lastPos), To3dSpace(pos))
		mouseDown = false; selectionFrame.Visible = false 
		
		print(result) --// print "found" table
	end
end)


RunService.Heartbeat:Connect(function() --// render selectionFrame gui
	if mouseDown then
		local pos = UserInputService:GetMouseLocation()
		
		local lastPos = lastPos + Inset
		local Center = ((lastPos+ pos) * .5) - Inset --// if no inset remove ' - Inset'
	
		local DistX = math.abs(lastPos.X - pos.X)  
		local DistY = math.abs(lastPos.Y - pos.Y)  
	
		selectionFrame.Position = UDim2.new(0, Center.X,0, Center.Y)
		selectionFrame.Size =  UDim2.new(0, DistX,0, DistY)
		
		selectionFrame.Visible = true --// if not visible
	end
end)
Old/Orginal Code

local Mouse = game.Players.LocalPlayer:GetMouse()
local RunService = game:GetService("RunService")
local camera = workspace.CurrentCamera
local UIS = game:GetService("UserInputService")
local LastHit = Vector2.new(Mouse.x, Mouse.y)
local  Boxselect = script.Parent ---- Selection Gui
local Cache = {} -- For the Mouse Start and end positions



local function SearchForObjects(Objects,HitCache, r1, r2) ---- Math  and conditions and stuff....(Adapted from source above)
	if r1 and r2 then
   local a1, a2 = Vector2.new(math.min(r1.x, r2.x), math.min(r1.y, r2.y)), Vector2.new(math.max(r1.x, r2.x), math.max(r1.y, r2.y));
	local rel, x, y;
	 local ObjectsFound = HitCache or {}
	for i, obj in pairs(Objects) do
		rel = camera.CoordinateFrame:ToObjectSpace(obj.CFrame);
		 x, y = rel.x / -rel.z, rel.y / -rel.z;
		if (a1.x) < x and x < (a2.x) and (a1.y < y and y < a2.y) and rel.z < 0  then
			ObjectsFound[obj] = obj
		end
	  end
	return ObjectsFound
  end
end



local function HitCache(ModelFolder) ----Function For adding mouse target to the hit cache
	if Mouse.Target and table.find(ModelFolder, Mouse.Target)  then
	Cache[Mouse.Target] = Mouse.Target
   end
end



local function CalcSlope(vec) --- Calculate Slope of Object space cords
	local rel=workspace.CurrentCamera.CoordinateFrame:pointToObjectSpace(vec)
     return Vector2.new(rel.x/-rel.z, rel.y/-rel.z)
end







local function ModelsToPart( ModelFolder ) -- Transform any models into parts
local Table = {}
for _, obj in  pairs(ModelFolder:GetChildren()) do
	if obj:IsA("Model") then
		local BoundingBox = Instance.new("Part")
		BoundingBox.Name = obj.Name
		BoundingBox.Anchored = true
		BoundingBox.Transparency = 1
		BoundingBox.CanCollide = false
		local AABB_Pos, AABB_Size = obj:GetBoundingBox()
          BoundingBox.Size = AABB_Size
		BoundingBox.CFrame = AABB_Pos
		BoundingBox.Parent = obj
		table.insert(Table , BoundingBox)
		elseif obj:IsA("Part") then
		table.insert(Table , obj)
	end
   end
  return Table 
end

local  AllModels = ModelsToPart(game.Workspace.Folder)
local MouseDown = false
local StartSlope
local EndSlope




UIS.InputBegan:Connect(function(input)
       if input.UserInputType == Enum.UserInputType.MouseButton1 then
		 local MousePos =  Vector2.new(Mouse.x, Mouse.y)
			Cache  = {} ---Reset Cache
			Boxselect.Visible = true
             	StartSlope = CalcSlope(Mouse.Hit.p) 
		     LastHit = MousePos
		  HitCache(AllModels)
		MouseDown = true
	 end
end)




UIS.InputEnded:Connect(function(input)
       if input.UserInputType == Enum.UserInputType.MouseButton1 then
			local MousePos =  Vector2.new(Mouse.x, Mouse.y)
		     LastHit = MousePos
             EndSlope = CalcSlope(Mouse.Hit.p) 
           HitCache(AllModels)
	         local Objects = SearchForObjects(AllModels, Cache , StartSlope, EndSlope)
		for _, Obj in pairs(Objects) do
			print(Obj.Name.." Found!") --Print What we found
			---Do whatever from here.....
	   end
		MouseDown =false
	   Boxselect.Visible = false
     end
end)



RunService.RenderStepped:Connect(function() --- For Selection GUI
	if MouseDown then
local Hit = Vector2.new(Mouse.x, Mouse.y)
local 	Distance = (LastHit - Hit).Magnitude
local Center = (LastHit + Hit) / 2
local DistX = math.abs(LastHit.X - Hit.X) 
 local DistZ = math.abs(LastHit.Y - Hit.Y)
Boxselect.Position = Boxselect.Position:Lerp( UDim2.new(0, Center.X,0,  Center.Y), 1) 
Boxselect.Size =   Boxselect.Size:Lerp( UDim2.new(0, DistX,0, DistZ ), 1) 
   end
end)

if you wanted to get all models in the workspace you can use get descendants, i used the DescendantAdded event in replicated first it seemed to not lag at all, but i haven’t tested get descendants, so you’ll probably have to do some testing on that part

For reference here’s a place file too:
SelectionBox.rbxl (57.0 KB)

10 Likes

The code given in your example seems to be inverted, and i’ve tried using negatives and different numbers. In the end, it didn’t seem to work as expected, could you help me out there?

i’m not sure what you mean by inverted (sorry), could you possibly elaborate a little more?

So, when i click and drag, it doesn’t clip to the mouse pointer and resizes in a weird way. Reproducing: Just copy and paste your script inside a localscript in a frame.

Make sure that the anchor point is .5,.5

2 Likes