How to connect two UI objects with a line

:wave:

Hey devs! Recently, I needed to connect two GUI objects with a line, but I ran into some challenges.
There was unfortunately not enough ressources and the forums were not clear enough and that will help my case. However, I managed to figure it out and just wanted to share how I solved it. I hope this helps anyone who might be facing a similar issue!

In this forum, I’ll be going over how to:
Connect 2 GUI Objects with a frame line
Connect a frame with the mouse location using a frame line

However, when it comes to making these 2,
it may be a bit tricky because its important we take in consideration the the 'IgnoreGuiInset' property in the screenGUI.

You can learn more about IgnoreGuiInset and :GetGuiInset() here.

Anyways, Here’s the function that I made for Connect 2 GUI Objects with a frame line:

local gui_service = game:GetService("GuiService")

 -- Dont mind the function name, it works with any UI Object, not just frames
local function Connect_frames(F1, F2, Frame)
	local guiInset = gui_service:GetGuiInset()
	local screen_ui :ScreenGui = F1:FindFirstAncestorWhichIsA("ScreenGui")
	local inset_enabled = screen_ui.IgnoreGuiInset

	local F1Pos = F1.AbsolutePosition
	local F1Size = F1.AbsoluteSize
	-- Calculate the center of the first frame
	local F1Center = F1Pos + (F1Size / 2)

	local F2Pos = F2.AbsolutePosition
	local F2Size = F2.AbsoluteSize
	-- Calculate the center of the second frame
	local F2Center = F2Pos + (F2Size / 2)

	-- Take the IgnoreGuiInset in consideration
	F1Center = F1Center + (inset_enabled and guiInset or Vector2.zero)
	F2Center = F2Center + (inset_enabled and guiInset or Vector2.zero)

	-- Calculate the difference in X and Y coordinates between the centers of F1 and F2
	local Distance = Vector2.new(F1Center.X, F1Center.Y) - Vector2.new(F2Center.X, F2Center.Y)
	local Difference = F1Center - F2Center
	-- Calculate the midpoint between the centers of F1 and F2
	local CenterPosition = (F2Center + F1Center) / 2

	-- The angle between F1 and F2
	Frame.Rotation = math.deg(math.atan2(Difference.Y, Difference.X))
	-- Position the frame at the midpoint between F1 and F2
	Frame.Position = UDim2.fromOffset(CenterPosition.X, CenterPosition.Y)
	-- Set the size of the frame to span the distance between F1 and F2
	Frame.Size = UDim2.fromOffset(Distance.Magnitude, 1)
end

And here’s how it should be used:

local F1 = ... -- Your frame or label or whatever
local F2 = ...
local Frame = Instance.new("Frame", Parent) -- the connecting frame
Frame.AnchorPoint = Vector2.new(0.5, 0.5)

Connect_frames(F1, F2, Frame)

Here is a video showcasing how Connect_frames works:

However, when it comes to Connect a frame with the mouse location using a frame line the state of IgnoreGuiInset affects both the positions of the mouse location and the frame,
I managed to find a way to make the mouse and the frame connect correctly no matter the state of that property, here’s the script:

local uis = game:GetService("UserInputService")
local gui_service = game:GetService("GuiService")

local function Connect_frame_mouse(F1, Frame)
	-- Get the current mouse location on the screen
	local mouse_location = uis:GetMouseLocation()
	-- Get the GUI inset to adjust for any screen GUI offsets
	local guiInset = gui_service:GetGuiInset()
	local screen_ui :ScreenGui = F1:FindFirstAncestorWhichIsA("ScreenGui")
	local inset_enabled = screen_ui.IgnoreGuiInset

	local F1Pos = F1.AbsolutePosition
	local F1Size = F1.AbsoluteSize
	local F1Center = F1Pos + (F1Size / 2)

	-- Adjust the mouse position for GUI insets to get the correct position on the screen
	F1Center = F1Center + (inset_enabled and guiInset or Vector2.zero)
	
	local mousePos = mouse_location - (not inset_enabled and guiInset or Vector2.zero)
	local Difference = F1Center - mousePos
	local Distance = Vector2.new(F1Center.X, F1Center.Y) - Vector2.new(mousePos.X, mousePos.Y)
	-- Calculate the midpoint between the frame center and the mouse position
	local CenterPosition = (mousePos + F1Center) / 2

	-- Set the rotation of the frame to match the angle between the frame center and the mouse
	Frame.Rotation = math.deg(math.atan2(Difference.Y, Difference.X))
	Frame.Position = UDim2.fromOffset(CenterPosition.X, CenterPosition.Y)
	Frame.Size = UDim2.fromOffset(Distance.Magnitude, 20)
end

And here’s how it’s setup:

local uis = game:GetService("UserInputService")
local gui_service = game:GetService("GuiService")

local F1 = ... -- Your frame or label or whatever
local Frame = Instance.new("Frame", Parent) -- the connecting frame
Frame.AnchorPoint = Vector2.new(0.5, 0.5)

Connect_frame_mouse(F1, Frame)

Here is a video showcasing how Connect_frame_mouse works:

And those are pretty much all the use cases
for connecting 2 GUI objects, you can do it with anything
'TextLabels', 'TextButtons', etc...

And of course you can change the connecting line visually:

local Frame = Instance.new("Frame", Parent) -- the connecting frame
Frame.AnchorPoint = Vector2.new(0.5, 0.5)
Frame.BackgroundColor = ... -- Any color you wish!

As for the size of the line, you just need to do the following inside the function:
Needs to be adjusted manually, you can set it as a parameter in the function if you wish.

...
local my_size = 10
Frame.Size = UDim2.fromOffset(Distance.Magnitude, my_size)

With both functions connected and some creativity
You can achieve something similar to the Among Us wire connecting puzzle like so :tada: :

Hope this helps fellow developers!
If you have any questions or run into any issues, feel free to ask. :wave:

13 Likes

Some unnecessary stuff you did but here is how I would do it:

local function Connect_frame(F1, F2, Frame)
	local F1Pos = F1.AbsolutePosition
	local F1Size = F1.AbsoluteSize
	-- Calculate the center of the first frame
	local F1Center = F1Pos + (F1Size / 2)

	local F2Pos = F2.AbsolutePosition
	local F2Size = F2.AbsoluteSize
	-- Calculate the center of the second frame
	local F2Center = F2Pos + (F2Size / 2)

	local Difference = F1Center - F2Center

	-- The angle between F1 and F2
	Frame.Rotation = math.deg(math.atan2(Difference.Y, Difference.X))
	-- Position the frame at the midpoint between F1 and F2
	Frame.Position = F1Center:Lerp(F2Center,0.5)
	-- Set the size of the frame to span the distance between F1 and F2
	Frame.Size = UDim2.fromOffset(Difference.Magnitude, 1)
end

Good post though! Good for beginners. Also, you could use the new Path2d instance as well now which is more performance friendly!

2 Likes

Appreciate the comment. The rotation is now calculated like you suggested
However your lerping part wont work because F2Center is a vector2
And thanks for pointing out the Path2d instance, never know about it

1 Like

Oh yes! I was actually on mobile so I didn’t got time to double check that, but you could just lerp it as well and it would be much more simpler.

1 Like

Alright, I’ll try your approach thanks anyways! :slight_smile:

1 Like

Cool tutorial, yes im the guy from developer hangout

1 Like

For some reason this wont work for me no idea why…

can you show me what you put in your script and what happens?

I used both of the functions you provided, I used the first function (connect_frames) and I tried to connect the red frames together, but the line isn’t anywhere near the actual frames as shown in the 1st screenshot

I also used the other function (connect_frame_mouse) and it just made the line at a random position as show in the 2nd screenshot, is there any requirements for the UI?

(The scripts are the same ones as you provided I made the F1 and F2 variables for the frames)


hmmm, I think it has to do with your ui structure, can you show me how that looks
if this problem persists, a good alternative is 2D Path
which I didn’t know about while making this, anyways I’m pretty sure it has to do with the ui structure and I dont have any time to make a fix around that issue, so 2D path should do the job.

1 Like

Oh!! I forgot about the 2D Path as well, I will just use that as I don’t wanna cause any inconvenience. Thank you for your help!

1 Like

is there way that we can make the line from offset into scale? so mobile Users can use it?

i already tried but i don’t have idea about that yet