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 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.
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
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:
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)