ScrollingFrame ClipDescendants interferring with drag and drop

I’m implementing a drag-and-drop mechanic for my inventory system, allowing items in the backpack to be dragged into the hotbar and whatnot. The problem is that my backpack GUI is a ScrollingFrame with ClipDescendants enabled, which entirely clips off the dragged item when moving an item from the backpack to the hotbar.

I’ve seen other inventory systems pull this off, like Deepwoken and even the default roblox inventory, where you can drag the item from the backpack and into the hotbar without it being clipped as you go out of the ScrollingFrame.

How can I go about this? cuz i got nothing

1 Like

Try changing the parent of the UI you’re dragging to something else while it’s being dragged, and change the parent back when it’s dropped

How can I make sure that it keeps the same position on the screen, though? Bc when changing parents, the position will change as well, since its position was UDim2.new(0,0,0,0) relative to its parent.
I’m using UIGridLayout, as well.

Since you’re using GridLayout, you can make a clone when dragging begins and make the actual item become black/a placeholder.

As for position, can I see your dragging script? What I would do is make a new ScreenGui object, and position the UI object with offset based on mouse position.

yeah, here’s the basic dragging script.

Dragging script
local draggable = {}
draggable.__index = draggable

local players = game:GetService("Players")
local player =  players.LocalPlayer
local mouse = player:GetMouse()

local dragStartedBE = script.dragStarted
local dragEndedBE = script.dragEnded
local dragMovedBE = script.dragMoved

local sharedVariables = require(script.Parent.Parent.sharedVariables)


function draggable.new(gui: GuiObject, name: string)
	local self = setmetatable({}, draggable)
	
	self.Draggable   	= false
	self.Name			= name
	self.Connections 	= {}
	self.GUI		 		= gui
	self.Absolute		= false  	-- if true, then we need to bypass the clipdescendants property of the parent
	self.AbsoluteParent	= nil
	self.RelativeParent = nil
	
	self.DragStarted = dragStartedBE:Clone()
	self.DragEnded   = dragEndedBE:Clone()
	self.DragMoved   = dragMovedBE:Clone()
	
	
	return self
end




function draggable:Enable(guiOverride: GuiObject?)
	self.Draggable = true
	print("draggable enabled for:", self.Name)
	
	local gui: GuiObject 	= if guiOverride ~= nil then guiOverride else self.GUI
	local _dragging 			= false
	local x0, y0				= nil, nil
	
	
	self.Connections["inputBegan"] = gui.InputBegan:Connect(function(input)
		if input.UserInputType == Enum.UserInputType.MouseButton1 or 
			input.UserInputType == Enum.UserInputType.Touch then
			
			if sharedVariables.FullViewMode == false then 
				warn("trying to drag with fullviewmode off")
				return	
			end
			
			_dragging = true
			
			x0 = mouse.X
			y0 = mouse.Y
			
			local pos0 = gui.Position  - UDim2.new(0, x0, 0, y0)

			if (self.Absolute) then
				local absPos = UDim2.new(0, math.ceil(gui.AbsolutePosition.X), 0, math.ceil(gui.AbsolutePosition.Y))
				gui.Parent = self.AbsoluteParent
				gui.Position = absPos
				pos0 = gui.Position - UDim2.new(0, x0, 0, y0)				
			end
			
			self.DragStarted:Fire(x0, y0)

			self.Connections["mouseCon"] = mouse.Move:Connect(function()
				self.DragMoved:Fire(mouse.X, mouse.Y)

				gui.Position = pos0 + UDim2.new(0, mouse.X, 0, mouse.Y)
			end)
		end
	end)
	
	
	
	self.Connections["inputEnded"] = gui.InputEnded:Connect(function(input)
		if input.UserInputType == Enum.UserInputType.MouseButton1 or 
			input.UserInputType == Enum.UserInputType.Touch then
			
			_dragging = false
			
			if (self.Absolute) then
				gui.Parent = self.RelativeParent
			end
			
			self.DragEnded:Fire(mouse.X, mouse.Y)
			
			if self.Connections["mouseCon"] then
				self.Connections["mouseCon"]:Disconnect()
				self.Connections["mouseCon"] = nil
			end
			
		end
	end)
	
end

function draggable:SetAbsoluteParent(parent)
	self.AbsoluteParent = parent
end

function draggable:SetRelativeParent(parent)
	self.RelativeParent = parent
end



function draggable:Destroy()
	self.Draggable = false
	print("draggable disabled for:", self.Name)
	
	if self.Connections["inputBegan"] then
		self.Connections["inputBegan"]:Disconnect()
		self.Connections["inputBegan"] = nil
	end
	
	if self.Connections["inputEnded"] then
		self.Connections["inputEnded"]:Disconnect()
		self.Connections["inputEnded"] = nil
	end
	
	if self.Connections["mouseCon"] then
		self.Connections["mouseCon"]:Disconnect()
		self.Connections["mouseCon"] = nil
	end
	
	self.DragStarted:Destroy()
	self.DragMoved:Destroy()
	self.DragEnded:Destroy()
	
	
	if self.GUI then
		self.GUI:Destroy()
	end
	
	table.clear(self)
	self = nil
end






return draggable

I don’t understand your suggestion tho, with the ScreenGui

self.Connections["inputBegan"] = gui.InputBegan:Connect(function(input)
		if input.UserInputType == Enum.UserInputType.MouseButton1 or 
			input.UserInputType == Enum.UserInputType.Touch then
			
			if sharedVariables.FullViewMode == false then 
				warn("trying to drag with fullviewmode off")
				return	
			end
			
			_dragging = true

-- CHANGES ----------------------------------------------------------
			local TempDraggingGui = Instance.new("ScreenGui")
			TempDraggingGui.Parent = player.PlayerGui
			TempDraggingGui.Name = "TempDraggingGui"
			gui.Parent = TempDraggingGui
			
			x0 = mouse.X
			y0 = mouse.Y
			
			local pos0 = UDim2.new(0, x0, 0, y0)

			if (self.Absolute) then
				local absPos = UDim2.new(0, math.ceil(gui.AbsolutePosition.X), 0, math.ceil(gui.AbsolutePosition.Y))
				gui.Parent = self.AbsoluteParent
				gui.Position = absPos
				pos0 = UDim2.new(0, x0, 0, y0)				
			end
-- CHANGES END ------------------------------------------------------
			
			self.DragStarted:Fire(x0, y0)

			self.Connections["mouseCon"] = mouse.Move:Connect(function()
				self.DragMoved:Fire(mouse.X, mouse.Y)

				gui.Position = pos0 + UDim2.new(0, mouse.X, 0, mouse.Y)
			end)
		end
	end)

You’re solution for bypassing the ClipDescendants works. But, another problem has occurred. There’s like the TINIEST offset when starting the dragging. You can see it here:

https://gyazo.com/6773c468c18d36c8260767aa01cc0371

So, it seems the cause of that tiny offset is a rounding off of numbers. When I set the UI to the absolute position, it rounds to the nearest whole number even though the original has a few decimals when it’s in the UIGridLayout. I’m not even rounding anything. And, yeah I did remove the math.ceil in that dragging script, but it’s still being rounded when I set absPos to gui.Position.

It has to do with the way Vector2 and UDim2 store their numbers.

I was able to solve my problem by creating an exact copy of the backpack GUI scrolling frame, that lies underneath the original one and with clipDescendants off. This is so it can keep the same position. I can then parent the UI that’s being dragged to the backpack copy space without creating some weird offset. I won’t have to mess with absolutePosition since the backpack copy space is located at the exact same space as the original, so the UI’s position will always start at UDim2.new(0,0,0,0)

1 Like

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