Hello, I see you have found my post!
Sorry if I took long to reply to your post.
Before I explain everything I just want to tell you that I haven’t optimized this code much at all so sorry if it isn’t the best.
Step 1
Before we start we need to actually generate the grid.
Each grid part looks like this:
: What you see.
: What the actual grid is.
Later we need to define the items, which I did like this:
I then wrote a function which generates the grid parts:
local function loadInventoryGrid()
for gridPartNum = 1, gridPartAmount do
local gridPartFrame = assets.UI.InvGridPart:Clone()
gridPartFrame.Size = UDim2.new(0, gridPartSize, 0, gridPartSize)
gridPartFrame.LayoutOrder = gridPartNum
gridPartFrame.Name = gridPartNum
gridPartFrame.Parent = invGrid
end
invMain.Size = UDim2.new(0, invGrid.UIGridLayout.AbsoluteContentSize.X, 0, invGrid.UIGridLayout.AbsoluteContentSize.Y)
end
And that is everything you need to generate the grid.
Step 2
The next step is to generate the items.
For this step I wrote another function:
local function loadInventoryItems()
for _, item in pairs(itemInv:GetChildren()) do
item.InvPosX.Value = -10000
item.InvPosY.Value = -10000
end
sortItems(itemInv:GetChildren())
for _, item in pairs(itemInv:GetChildren()) do
local itemFrame = assets.UI.InvItem:Clone()
itemFrame.Size = UDim2.new(0, item.InvSizeX.Value * gridPartSize, 0, item.InvSizeY.Value * gridPartSize)
itemFrame.Position = UDim2.new(0, item.InvPosX.Value * 70 - 70, 0, item.InvPosY.Value * 70 - 70)
--I will go over this later:
itemFrame.Button.MouseButton1Down:Connect(function()
currentlyDraggingItemFrame = itemFrame
currentlyDraggingItem = item
currentlyDraggingItemFrame.ZIndex = 4
currentlyDraggingItemFrame.ItemImg.ZIndex = 5
currentlyDraggingItemFrame.Button.ZIndex = 5
local mousePos = userInputService:GetMouseLocation() - Vector2.new(0, 36)
local itemPosOffset = Vector2.new(mousePos.X - currentlyDraggingItemFrame.Position.X.Offset, mousePos.Y - currentlyDraggingItemFrame.Position.Y.Offset)
currentlyDraggingMouseOffset = itemPosOffset
end)
itemFrame.Parent = invMain
end
end
In this function I have the sortItems function that puts the items into the correct places without overlapping. I also made a UI item template hence the “local itemFrame = assets.UI.InvItem:Clone()” but I don’t really have to go over that since the UI is very simple, it’s just a Frame with an ImageLabel inside.
That looks something like this:
local function sortItems(items)
table.sort(items, function(a, b) return a.InvSizeX.Value * a.InvSizeY.Value > b.InvSizeX.Value * b.InvSizeY.Value end)
for _, item in pairs(items) do
local success, invPosX, invPosY = getNextEmptyDropPos(item)
if (success) then
item.InvPosX.Value = invPosX
item.InvPosY.Value = invPosY
end
end
end
And in here I have got even more functions:
local function getNextEmptyDropPos(item)
for y = 1, invGrid.UIGridLayout.AbsoluteCellCount.Y do
for x = 1, invGrid.UIGridLayout.AbsoluteCellCount.X do
if not (x >= invGrid.UIGridLayout.AbsoluteCellCount.X + 1) then
x = math.clamp(x, 1, invGrid.UIGridLayout.AbsoluteCellCount.X - item.InvSizeX.Value + 1)
end
if not (y >= invGrid.UIGridLayout.AbsoluteCellCount.Y + 1) then
y = math.clamp(y, 1, invGrid.UIGridLayout.AbsoluteCellCount.Y - item.InvSizeY.Value + 1)
end
local colliding = isColliding(item, x, y)
if not (colliding) then
return true, x, y
end
end
end
return false, nil, nil
end
And then finally the isColliding and getOccupiedPositions functions:
local function isColliding(colliderItem, posX, posY)
local occupiedPositions = getOccupiedPositions(itemInv, colliderItem)
for sizeOffsetX = 0, colliderItem.InvSizeX.Value - 1 do
for sizeOffsetY = 0, colliderItem.InvSizeY.Value - 1 do
if (table.find(occupiedPositions, Vector2.new(posX + sizeOffsetX, posY + sizeOffsetY))) then
return true
end
end
end
return false
end
local function getOccupiedPositions(inventory, ignoredItem)
local occupiedPositions = {}
for _, item in pairs(inventory:GetChildren()) do
if not (item == ignoredItem) then
for sizeOffsetX = 0, item.InvSizeX.Value - 1 do
for sizeOffsetY = 0, item.InvSizeY.Value - 1 do
table.insert(occupiedPositions, Vector2.new(item.InvPosX.Value + sizeOffsetX, item.InvPosY.Value + sizeOffsetY))
end
end
end
end
return occupiedPositions
end
The isColliding function will come in handy a lot of the time.
That is everything for generating the items.
Now onto the last step where we will add item interaction!
Step 3
So firstly I added a MouseButton1Down event to the loadInventoryItems function to get the item the player was interacting with:
--// Located in the loadInventoryItems function!
itemFrame.Button.MouseButton1Down:Connect(function()
currentlyDraggingItemFrame = itemFrame
currentlyDraggingItem = item
currentlyDraggingItemFrame.ZIndex = 4
currentlyDraggingItemFrame.ItemImg.ZIndex = 5
currentlyDraggingItemFrame.Button.ZIndex = 5
local mousePos = userInputService:GetMouseLocation() - Vector2.new(0, 36)
local itemPosOffset = Vector2.new(mousePos.X - currentlyDraggingItemFrame.Position.X.Offset, mousePos.Y - currentlyDraggingItemFrame.Position.Y.Offset)
currentlyDraggingMouseOffset = itemPosOffset
end)
Next, add two UserInputService events to update the item position:
userInputService.InputChanged:Connect(function(input, gameProcessed)
if (currentlyDraggingItemFrame) and (input.UserInputType == Enum.UserInputType.MouseMovement) then
local mousePos = input.Position
currentlyDraggingItemFrame.Position = UDim2.new(0, mousePos.X - currentlyDraggingMouseOffset.X, 0, mousePos.Y - currentlyDraggingMouseOffset.Y)
for _, gridPart in pairs(invGrid:GetChildren()) do
if not (gridPart:IsA("UIGridLayout")) then
gridPart.Cosmetic.BackgroundColor3 = Color3.fromRGB(20, 20, 20)
end
end
local dropPosX, dropPosY = getDropPos(currentlyDraggingItemFrame.Position.X.Offset, currentlyDraggingItemFrame.Position.Y.Offset, currentlyDraggingItem.InvSizeX.Value, currentlyDraggingItem.InvSizeY.Value)
local colliding = isColliding(currentlyDraggingItem, dropPosX, dropPosY)
for sizeOffsetX = 0, currentlyDraggingItem.InvSizeX.Value - 1 do
for sizeOffsetY = 0, currentlyDraggingItem.InvSizeY.Value - 1 do
local gridPart = getGridPartFromDropPos(dropPosX + sizeOffsetX, dropPosY + sizeOffsetY)
if (gridPart) then
if (colliding) then
gridPart.Cosmetic.BackgroundColor3 = Color3.new(1, 0.15, 0.15)
else
gridPart.Cosmetic.BackgroundColor3 = Color3.new(0.85, 0.85, 0.85)
end
end
end
end
end
end)
userInputService.InputEnded:Connect(function(input)
if (currentlyDraggingItemFrame) and (input.UserInputType == Enum.UserInputType.MouseButton1) then
local dropPosX, dropPosY = getDropPos(currentlyDraggingItemFrame.Position.X.Offset, currentlyDraggingItemFrame.Position.Y.Offset, currentlyDraggingItem.InvSizeX.Value, currentlyDraggingItem.InvSizeY.Value)
local colliding = isColliding(currentlyDraggingItem, dropPosX, dropPosY)
if not (colliding) then
currentlyDraggingItemFrame.Position = UDim2.new(0, dropPosX * 70 - 70, 0, dropPosY * 70 - 70)
currentlyDraggingItem.InvPosX.Value = dropPosX
currentlyDraggingItem.InvPosY.Value = dropPosY
else
currentlyDraggingItemFrame.Position = UDim2.new(0, currentlyDraggingItem.InvPosX.Value * 70 - 70, 0, currentlyDraggingItem.InvPosY.Value * 70 - 70)
end
currentlyDraggingItemFrame.ZIndex = 2
currentlyDraggingItemFrame.ItemImg.ZIndex = 3
currentlyDraggingItemFrame.Button.ZIndex = 3
currentlyDraggingItem = nil
currentlyDraggingItemFrame = nil
for _, gridPart in pairs(invGrid:GetChildren()) do
if not (gridPart:IsA("UIGridLayout")) then
gridPart.Cosmetic.BackgroundColor3 = Color3.fromRGB(20, 20, 20)
end
end
end
end)
In the first event we can see the getDropPos function which is used for the drop highlight. The function looks like this:
local function getDropPos(posX, posY, sizeX, sizeY)
local dropPosX = math.floor(posX / gridPartSize + 1.5)
local dropPosY = math.floor(posY / gridPartSize + 1.5)
local dropPosXClamp = math.clamp(dropPosX, 1, invGrid.UIGridLayout.AbsoluteCellCount.X - sizeX + 1)
local dropPosYClamp = math.clamp(dropPosY, 1, invGrid.UIGridLayout.AbsoluteCellCount.Y - sizeY + 1)
return dropPosXClamp, dropPosYClamp
end
Pretty easy to make function, just some rounding to make the grid work. We can also see the getGridPartFromDropPos function which as the name suggests gets the gridPart (The grid UI we made earlier in step 1), the function is also a very easy to make function:
local function getGridPartFromDropPos(dropPosX, dropPosY)
local gridPartPosYSum = (dropPosY - 1) * invGrid.UIGridLayout.AbsoluteCellCount.X
local gridPart = invGrid:FindFirstChild(dropPosX + gridPartPosYSum)
return gridPart
end
And that is everything for making a grid type inventory!
Code
local replicatedStorage = game:GetService("ReplicatedStorage")
local userInputService = game:GetService("UserInputService")
local assets = replicatedStorage:WaitForChild("Assets")
local itemInv = replicatedStorage:WaitForChild("Inventory")
local invMain = script.Parent
local invGrid = invMain:WaitForChild("InvGrid")
local gridPartSize = 70
local gridPartAmount = 98
local currentlyDraggingItem = nil
local currentlyDraggingItemFrame = nil
local currentlyDraggingMouseOffset = Vector2.new(0, 0)
local function getGridPartFromDropPos(dropPosX, dropPosY)
local gridPartPosYSum = (dropPosY - 1) * invGrid.UIGridLayout.AbsoluteCellCount.X
local gridPart = invGrid:FindFirstChild(dropPosX + gridPartPosYSum)
return gridPart
end
local function getDropPos(posX, posY, sizeX, sizeY)
local dropPosX = math.floor(posX / gridPartSize + 1.5)
local dropPosY = math.floor(posY / gridPartSize + 1.5)
local dropPosXClamp = math.clamp(dropPosX, 1, invGrid.UIGridLayout.AbsoluteCellCount.X - sizeX + 1)
local dropPosYClamp = math.clamp(dropPosY, 1, invGrid.UIGridLayout.AbsoluteCellCount.Y - sizeY + 1)
return dropPosXClamp, dropPosYClamp
end
local function getOccupiedPositions(inventory, ignoredItem)
local occupiedPositions = {}
for _, item in pairs(inventory:GetChildren()) do
if not (item == ignoredItem) then
for sizeOffsetX = 0, item.InvSizeX.Value - 1 do
for sizeOffsetY = 0, item.InvSizeY.Value - 1 do
table.insert(occupiedPositions, Vector2.new(item.InvPosX.Value + sizeOffsetX, item.InvPosY.Value + sizeOffsetY))
end
end
end
end
return occupiedPositions
end
local function isColliding(colliderItem, posX, posY)
local occupiedPositions = getOccupiedPositions(itemInv, colliderItem)
for sizeOffsetX = 0, colliderItem.InvSizeX.Value - 1 do
for sizeOffsetY = 0, colliderItem.InvSizeY.Value - 1 do
if (table.find(occupiedPositions, Vector2.new(posX + sizeOffsetX, posY + sizeOffsetY))) then
return true
end
end
end
return false
end
local function getNextEmptyDropPos(item)
for y = 1, invGrid.UIGridLayout.AbsoluteCellCount.Y do
for x = 1, invGrid.UIGridLayout.AbsoluteCellCount.X do
if not (x >= invGrid.UIGridLayout.AbsoluteCellCount.X + 1) then
x = math.clamp(x, 1, invGrid.UIGridLayout.AbsoluteCellCount.X - item.InvSizeX.Value + 1)
end
if not (y >= invGrid.UIGridLayout.AbsoluteCellCount.Y + 1) then
y = math.clamp(y, 1, invGrid.UIGridLayout.AbsoluteCellCount.Y - item.InvSizeY.Value + 1)
end
local colliding = isColliding(item, x, y)
if not (colliding) then
return true, x, y
end
end
end
return false, nil, nil
end
local function sortItems(items)
table.sort(items, function(a, b) return a.InvSizeX.Value * a.InvSizeY.Value > b.InvSizeX.Value * b.InvSizeY.Value end)
for _, item in pairs(items) do
local success, invPosX, invPosY = getNextEmptyDropPos(item)
if (success) then
item.InvPosX.Value = invPosX
item.InvPosY.Value = invPosY
end
end
end
userInputService.InputChanged:Connect(function(input, gameProcessed)
if (currentlyDraggingItemFrame) and (input.UserInputType == Enum.UserInputType.MouseMovement) then
local mousePos = input.Position
currentlyDraggingItemFrame.Position = UDim2.new(0, mousePos.X - currentlyDraggingMouseOffset.X, 0, mousePos.Y - currentlyDraggingMouseOffset.Y)
for _, gridPart in pairs(invGrid:GetChildren()) do
if not (gridPart:IsA("UIGridLayout")) then
gridPart.Cosmetic.BackgroundColor3 = Color3.fromRGB(20, 20, 20)
end
end
local dropPosX, dropPosY = getDropPos(currentlyDraggingItemFrame.Position.X.Offset, currentlyDraggingItemFrame.Position.Y.Offset, currentlyDraggingItem.InvSizeX.Value, currentlyDraggingItem.InvSizeY.Value)
local colliding = isColliding(currentlyDraggingItem, dropPosX, dropPosY)
for sizeOffsetX = 0, currentlyDraggingItem.InvSizeX.Value - 1 do
for sizeOffsetY = 0, currentlyDraggingItem.InvSizeY.Value - 1 do
local gridPart = getGridPartFromDropPos(dropPosX + sizeOffsetX, dropPosY + sizeOffsetY)
if (gridPart) then
if (colliding) then
gridPart.Cosmetic.BackgroundColor3 = Color3.new(1, 0.15, 0.15)
else
gridPart.Cosmetic.BackgroundColor3 = Color3.new(0.85, 0.85, 0.85)
end
end
end
end
end
end)
userInputService.InputEnded:Connect(function(input)
if (currentlyDraggingItemFrame) and (input.UserInputType == Enum.UserInputType.MouseButton1) then
local dropPosX, dropPosY = getDropPos(currentlyDraggingItemFrame.Position.X.Offset, currentlyDraggingItemFrame.Position.Y.Offset, currentlyDraggingItem.InvSizeX.Value, currentlyDraggingItem.InvSizeY.Value)
local colliding = isColliding(currentlyDraggingItem, dropPosX, dropPosY)
if not (colliding) then
currentlyDraggingItemFrame.Position = UDim2.new(0, dropPosX * 70 - 70, 0, dropPosY * 70 - 70)
currentlyDraggingItem.InvPosX.Value = dropPosX
currentlyDraggingItem.InvPosY.Value = dropPosY
else
currentlyDraggingItemFrame.Position = UDim2.new(0, currentlyDraggingItem.InvPosX.Value * 70 - 70, 0, currentlyDraggingItem.InvPosY.Value * 70 - 70)
end
currentlyDraggingItemFrame.ZIndex = 2
currentlyDraggingItemFrame.ItemImg.ZIndex = 3
currentlyDraggingItemFrame.Button.ZIndex = 3
currentlyDraggingItem = nil
currentlyDraggingItemFrame = nil
for _, gridPart in pairs(invGrid:GetChildren()) do
if not (gridPart:IsA("UIGridLayout")) then
gridPart.Cosmetic.BackgroundColor3 = Color3.fromRGB(20, 20, 20)
end
end
end
end)
local function loadInventoryItems()
for _, item in pairs(itemInv:GetChildren()) do
item.InvPosX.Value = -10000
item.InvPosY.Value = -10000
end
sortItems(itemInv:GetChildren())
for _, item in pairs(itemInv:GetChildren()) do
local itemFrame = assets.UI.InvItem:Clone()
itemFrame.Size = UDim2.new(0, item.InvSizeX.Value * gridPartSize, 0, item.InvSizeY.Value * gridPartSize)
itemFrame.Position = UDim2.new(0, item.InvPosX.Value * 70 - 70, 0, item.InvPosY.Value * 70 - 70)
itemFrame.Button.MouseButton1Down:Connect(function()
currentlyDraggingItemFrame = itemFrame
currentlyDraggingItem = item
currentlyDraggingItemFrame.ZIndex = 4
currentlyDraggingItemFrame.ItemImg.ZIndex = 5
currentlyDraggingItemFrame.Button.ZIndex = 5
local mousePos = userInputService:GetMouseLocation() - Vector2.new(0, 36)
local itemPosOffset = Vector2.new(mousePos.X - currentlyDraggingItemFrame.Position.X.Offset, mousePos.Y - currentlyDraggingItemFrame.Position.Y.Offset)
currentlyDraggingMouseOffset = itemPosOffset
end)
itemFrame.Parent = invMain
end
end
local function loadInventoryGrid()
for gridPartNum = 1, gridPartAmount do
local gridPartFrame = assets.UI.InvGridPart:Clone()
gridPartFrame.Size = UDim2.new(0, gridPartSize, 0, gridPartSize)
gridPartFrame.LayoutOrder = gridPartNum
gridPartFrame.Name = gridPartNum
gridPartFrame.Parent = invGrid
end
invMain.Size = UDim2.new(0, invGrid.UIGridLayout.AbsoluteContentSize.X, 0, invGrid.UIGridLayout.AbsoluteContentSize.Y)
end
loadInventoryGrid()
loadInventoryItems()
--// I didn't go over this but this is just the button in the top left corner that resets all of the items.
script.Parent.Parent.ReloadItems.MouseButton1Click:Connect(function()
for _, itemFrame in pairs(invMain:GetChildren()) do
if itemFrame.Name == "InvItem" then
itemFrame:Destroy()
end
end
loadInventoryItems()
end)
If you got any questions make a reply to my post!