How To Make A Draggable GUI Object!

Hey guys! Today I will be showing you how you can make any GUI object draggable, it’s really not hard!

This tutorial should work with any UI object, no matter the setup. Let’s get into it!

For this tutorial I will simple be using a frame with a local script and a text label inside of it

Now, we will open up the local script.
The first step, as always, is to grab our services, for this we will need the Players Service and UserInputService

--// Services
local Players = game:GetService('Players')
local UIS = game:GetService("UserInputService")

Next it’s time for our variables, we got quite a few of them.

--// Variables
local UI = script.Parent

local Player = Players.LocalPlayer
local Mouse = Player:GetMouse()

local Hovered = false
local Holding = false
local MoveCon = nil

local InitialX, InitialY, UIInitialPos

The UI variable just references our frame, or our object that we want to become draggable.

We need the Player so we can access it’s mouse object which will allow us to know where on the screen the mouse is.

Next up we have the hovered and holding variables, the hovered is used to tell us if we are currently hovering over the frame and the holding will tell us when we hold down our mouse button.

The MoveCon will represent a Mouse.Move connection (runs everytime we move our mouse), we are storing it in a variable so we can disconnect it once the user stops holding down their mouse.

The InitialX,Y, and InitialUI are all just going to be used while calculating the position to drag to, you’ll see what I mean later.

Next up what we want to do is set up our connections that will control our hovered and holding variables. To know when we our mouse starts (and stops) hovering the frame we will use the MouseEnter and MouseLeave events:

UI.MouseEnter:Connect(function()
	Hovered = true
end)

UI.MouseLeave:Connect(function()
	Hovered = false
end)

Simple, when our mouse enters, hover becomes true. When the mouse leaves, hover becomes false.

Now we want to do a similar thing, but this time for holding. Now this is the part we need to use UserInputService, if we used Mouse.Button1Down then it wouldn’t run since we would be hovering a gui object, but with user input service it’ll run either way.

UIS.InputBegan:Connect(function(input)
	if input.UserInputType == Enum.UserInputType.MouseButton1 or input.UserInputType == Enum.UserInputType.Touch then
		Holding = Hovered
    end
end

UIS.InputEnded:Connect(function(input)
	if input.UserInputType == Enum.UserInputType.MouseButton1 or input.UserInputType == Enum.UserInputType.Touch then
		Holding = false
	end
end)

For both the InputBegan and InputEnded we check if the user is (or was) either Holding down MouseButton1 (left mouse button) or touch which means pressing down on the screen (with like a finger or stylus), this makes it both mobile and pc friendly! Now notice in the InputBegan we set Holding = Hovered this is because we know we are holding the mouse button but we only want to set Holding to true when Hovered == true (when we are hovering). So if hovered is false, even if we are holding down our mouse, holding will also be false.

Now, in our InputEnded function, directly after Holding = Hovered (before the end) we want to continue onto our dragging logic. The first thing we need is to check that Hovered == true. If hovered is true we would then want to set the InitialX and InitialY variables to the Mouse’s X and Y values, aswell as setting the UIInitialPos equal to the UI’s position. These are the inital, as in before dragging, values.

        if Holding then
			InitialX, InitialY = Mouse.X, Mouse.Y
			UIInitialPos = UI.Position
		end

Next we want to assign our MouseCon variable to a Mouse.Move event that connects to a function called Drag:

            MoveCon = Mouse.Move:Connect(Drag)

You guessed it! It’s time to make the Drag function!

The first thing we want to do in this drag function is make sure that Holding == true if it’s not then we want to Disconnect the MoveCon and exit the function using a return:

function Drag()
	if Holding == false then MoveCon:Disconnect(); return end
end

Next we want to calculate the distance we moved our mouse, this is where our InitialX and InitialY variables come into play. The distance is equal to Initial - Current, for example to get the distance moved on the X axis it is InitialX - Mouse.X since Mouse.X is the current X position.

    local distanceMovedX = InitialX - Mouse.X
	local distanceMovedY = InitialY - Mouse.Y

Now, the FINAL step is very similar to the previous one. We now just need to set the UI’s position. Once again Pos = Initial - Current. So if we translate that pseudocode into our script it’ll look like this:

    UI.Position = UIInitialPos - UDim2.new(0, distanceMovedX, 0, distanceMovedY)

The important thing here is that we must use offset and not scale.

Here is the final code:

--// Services
local Players = game:GetService('Players')
local UIS = game:GetService("UserInputService")

--// Variables
local UI = script.Parent

local Player = Players.LocalPlayer
local Mouse = Player:GetMouse()

local Hovered = false
local Holding = false
local MoveCon = nil

local InitialX, InitialY, UIInitialPos

--// Functions

local function Drag()
	if Holding == false then MoveCon:Disconnect(); return end
	local distanceMovedX = InitialX - Mouse.X
	local distanceMovedY = InitialY - Mouse.Y

	UI.Position = UIInitialPos - UDim2.new(0, distanceMovedX, 0, distanceMovedY)
end

--// Connections

UI.MouseEnter:Connect(function()
	Hovered = true
end)

UI.MouseLeave:Connect(function()
	Hovered = false
end)

UIS.InputBegan:Connect(function(input)
	if input.UserInputType == Enum.UserInputType.MouseButton1 or input.UserInputType == Enum.UserInputType.Touch then
		Holding = Hovered
		if Holding then
			InitialX, InitialY = Mouse.X, Mouse.Y
			UIInitialPos = UI.Position

			MoveCon = Mouse.Move:Connect(Drag)
		end
	end
end)

UIS.InputEnded:Connect(function(input)
	if input.UserInputType == Enum.UserInputType.MouseButton1 or input.UserInputType == Enum.UserInputType.Touch then
		Holding = false
	end
end)

And for the final results:
ezgif.com-gif-maker

Drag Relative To Parent

So this is pretty similar aswell, the only thing that gets changed is in our drag function. We also need to add two more variables for our viewport size and the speed (you’ll see why we need the speed):

local ViewPortSize = workspace.Camera.ViewportSize
local Speed = 2

Ok so now into our drag function we keep our distanceMoved variables, but multiply them by the Speed variable or else it moves too slow when using scale, and then make a new variable called Pos

Pos = UIInitialPos - UDim2.new(distanceMovedX/ViewPortSize.X, 0, distanceMovedY/ViewPortSize.Y, 0)

This is basically the same thing we did last time except we are converting the offset value (provided by the distanceMoved X and Y variables and converting them to Scale by diving by ViewPortSize. X or Y. Credits to this solution for proving the information on converting to scale

Now the last thing we want to do is set the position of the UI object, however, we want it to stay inbounds of it’s parent, to do this we will use math.clamp

UI.Position = UDim2.new(math.clamp(Pos.X.Scale, 0, 1-UI.Size.X.Scale), 0, math.clamp(Pos.Y.Scale, 0, 1-UI.Size.Y.Scale), 0)

The minimum scale value is 0, this will be when the object is all the way to the left (or top), maximum scale is 1 all the way right (or bottom). Now based off this you’d probably come up with math.clamp(Pos.X.Scale, 0, 1), we are clamping the Scale value to a minimum of 0 and a max of 1, this works for the minimum, but you can make the object go it’s full width (or height) outside of the box since we didn’t take into account the size of the object itself, that’s why you see in code above we subtract 1 from the UI objects size. Now these values will need adjusting if you change the anchor point.

Final Code

--// Services
local Players = game:GetService('Players')
local UIS = game:GetService("UserInputService")

--// Variables
local UI = script.Parent

local Player = Players.LocalPlayer
local Mouse = Player:GetMouse()

local Hovered = false
local Holding = false
local MoveCon = nil

local InitialX, InitialY, UIInitialPos

local ViewPortSize = workspace.Camera.ViewportSize
local Speed = 2

--// Functions

local function Drag()
	if Holding == false then MoveCon:Disconnect(); return end
	local distanceMovedX = (InitialX - Mouse.X)*Speed
	local distanceMovedY = (InitialY - Mouse.Y)*Speed
	
	local Pos = UIInitialPos - UDim2.new(distanceMovedX/ViewPortSize.X, 0, distanceMovedY/ViewPortSize.Y, 0)
	UI.Position = UDim2.new(math.clamp(Pos.X.Scale, 0, 1-UI.Size.X.Scale), 0, math.clamp(Pos.Y.Scale, 0, 1-UI.Size.Y.Scale), 0)
end

--// Connections

UI.MouseEnter:Connect(function()
	Hovered = true
end)

UI.MouseLeave:Connect(function()
	Hovered = false
end)

UIS.InputBegan:Connect(function(input)
	if input.UserInputType == Enum.UserInputType.MouseButton1 or input.UserInputType == Enum.UserInputType.Touch then
		Holding = Hovered
		if Holding then
			InitialX, InitialY = Mouse.X, Mouse.Y
			UIInitialPos = UI.Position

			MoveCon = Mouse.Move:Connect(Drag)
		end
	end
end)

UIS.InputEnded:Connect(function(input)
	if input.UserInputType == Enum.UserInputType.MouseButton1 or input.UserInputType == Enum.UserInputType.Touch then
		Holding = false
	end
end)

Results:

ezgif.com-gif-maker

How’d I do?
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

0 voters

15 Likes

bro just use the draggable property :skull:

1 Like

The draggable property is depreciated and should not be used…

5 Likes

It’s deprecated but it still works well. There’s not even an explanation for why it was deprecated. Not all deprecated objects aren’t supposed to be used, some might still be great for use.

2 Likes

There’s a draggable property? Why have I never heard of this lol

1 Like

The draggable property is unfortunately still used in the Roblox backpack, and it is proved here:
https://devforum.roblox.com/t/concerning-the-current-state-of-the-coregui/2051222

2 Likes

MyFrame.Draggable = true

:skull: easy af

Except when you move your mouse more than the frame’s length in less than a frame the frame isn’t dragged with your mouse :skull:

10 Likes