Converting the Absolute Position of a UI object to Position

Currently I have a grid based UI that contains different containers. Each container has a specific number of slots position into grid. New items and positioned on that grid but parented to the container (a UI Frame) holding the slot UI’s (TextLabels). As im dragging the item through the inventory, I’m parenting the item to the container its hovering over.

The issue here is that it still applies the same UDim2 value to the position, but position is relative to the UI’s parent so it moves the UI from where the mouse is, to where the mouse would have been relative to the original parent (the original container).

Overall its a grid type inventory system which isn’t hard but I’ve never implemented multiple containers which is where everything is going wrong. If you have a different method than parenting the item to the container its hovering over, by all means lets discuss it! Any suggestions or solutions??

2 Likes

If you are using scale, the position of an element is that decimal of its parent container. You can find the global size by multiplying the size of the parent container by the size of the child element. The position is slightly more complicated. To find the world distance from the edge of the container to the edge of the element, we can take the position of the element and multiply it by the size of the container. We can then add the position of the container to get the world position.

function AbsolutePosition(container, element)
    local position = UDim2.new(element.Position.X.Scale*container.Size.X.Scale+container.Position.X.Scale, element.Position.Y.Scale*container.Size.Y.Scale+container.Position.Y.Scale)
    local size = UDim2.fromScale(container.Size.X.Scale*element.Size.X.Scale, container.Size.Y.Scale*element.Size.Y.Scale)
    return position, size
end

It is quite long since I didn’t use any variables but I hope this helps!

1 Like

I agree this would work, however; while the element is being drug around using:

-- dragStart being then position of the input when the element is initially clicked to drag the element
element.InputChanged:Connect(function(input)
	local delta = input.Position - dragStart
	element.Position = UDim2.new(0, delta.X, 0, delta.Y)
end

When you change the position using what we apply when going over anther container, the actual position is all messed up.

One solution to this issue would be to use a separate container to hold the dragged item while it is being moved. This container can be positioned relative to the mouse position using mouse events and UDim2 values.

When the item is initially clicked, you can create a new UI container and add the dragged item to it as a child. Then, as the item is being dragged, you can update the position of the container to follow the mouse position.

Once the item is dropped on a valid container slot, you can calculate the new position of the item within the container based on the slot position and update the item’s position relative to the container accordingly. Finally, you can remove the item from the temporary container and add it to the target container.

This approach would allow you to move items between different containers without the need to parent them to the container while dragging, which should solve the issue you’re experiencing.

Here’s some sample code to get you started:

local itemDragging = nil
local itemContainer = nil
local tempContainer = nil

-- function to create a new temporary container to hold the dragged item
local function createTempContainer()
    local container = Instance.new("Frame")
    container.Size = UDim2.new(0, itemDragging.Size.X.Offset, 0, itemDragging.Size.Y.Offset)
    container.Position = UDim2.new(0, 0, 0, 0)
    container.ZIndex = 10 -- make sure the temp container is above all other UI elements
    container.Parent = game:GetService("Players").LocalPlayer.PlayerGui -- or wherever you want to place the container
    
    itemDragging.Parent = container
    return container
end

-- function to handle the item being dragged
local function handleItemDragging()
    local mouse = game.Players.LocalPlayer:GetMouse()
    local dragging = true
    
    while dragging do
        -- update position of temporary container based on mouse position
        tempContainer.Position = UDim2.new(0, mouse.X - itemDragging.Size.X.Offset/2, 0, mouse.Y - itemDragging.Size.Y.Offset/2)
        wait()
    end
end

-- function to handle the item being dropped on a container
local function handleItemDropped(container, slotIndex)
    -- calculate the position of the item within the container based on the slot index
    local itemPosition = Vector2.new((slotIndex-1) % numSlotsPerRow, math.floor((slotIndex-1) / numSlotsPerRow))
    local itemOffset = Vector2.new(itemPosition.X*slotSize.X, itemPosition.Y*slotSize.Y)
    local containerOffset = Vector2.new(container.Position.X.Offset, container.Position.Y.Offset)
    local newPosition = containerOffset + itemOffset
    
    -- update the position of the item relative to the container
    itemDragging.Position = UDim2.new(0, newPosition.X, 0, newPosition.Y)
    itemDragging.Parent = container
    
    -- cleanup temporary container
    tempContainer:Destroy()
    tempContainer = nil
    
    -- reset dragging state
    itemDragging = nil
end

-- connect mouse events to handle dragging and dropping
itemDragging.MouseButton1Down:Connect(function()
    itemDragging = script.Parent
    itemContainer = itemDragging.Parent
    tempContainer = createTempContainer()
    handleItemDragging()
end)

for i, container in ipairs(containers) do
    for j, slot in ipairs(container.Slots:GetChildren()) do
        slot.MouseButton1Up:Connect(function()
            if itemDragging and container:FindFirstChild("Slots") then
                handleItemDropped(container, j)
            end
        end)
    end
end

Note that this is just an example