So I found this pretty old post which explains how to make a grid inventory system.
Screenshot
I want to modify the grid to a 5x5 and move it to the right but I cannot, for the life of me, figure out how to do so.
Sort of like this:
Here’s my script:
-- Services and global variables setup
-- Get essential Roblox services for accessing replicated storage and user input.
local replicatedStorage = game:GetService("ReplicatedStorage");
local userInputService = game:GetService("UserInputService");
-- References to game assets and the inventory system stored in ReplicatedStorage.
local assets = replicatedStorage:WaitForChild("Assets");
local itemInv = replicatedStorage:WaitForChild("Inventory");
-- References to the main inventory UI components.
local invMain = script.Parent
local invGrid = invMain:WaitForChild("InvGrid");
-- Constants for grid part size and the total number of parts, plus variables for drag-and-drop.
local gridPartSize = 70; -- Change grid part size here.
local gridPartAmount = 98; -- Change amount of grid parts here.
local currentlyDraggingItem = nil;
local currentlyDraggingItemFrame = nil;
local currentlyDraggingMouseOffset = Vector2.new(0, 0);
-- Determine which grid part is beneath the item being dropped.
local function getGridPartFromDropPos(dropPosX, dropPosY)
-- This function calculates the grid part's ID based on the drop position.
local gridPartPosYSum = (dropPosY - 1) * invGrid.UIGridLayout.AbsoluteCellCount.X;
local gridPart = invGrid:FindFirstChild(dropPosX + gridPartPosYSum);
return gridPart;
end
-- Calculate the appropriate drop position based on the current mouse position and item size.
local function getDropPos(posX, posY, sizeX, sizeY)
-- Converts mouse position to grid coordinates, clamping within the grid's bounds.
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
-- Get positions currently occupied by items in the inventory, excluding a specified item.
local function getOccupiedPositions(inventory, ignoredItem)
-- Creates a list of positions occupied by items, useful for collision detection.
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
-- Check if placing an item at a given position will collide with existing items.
local function isColliding(colliderItem, posX, posY)
-- Uses the occupied positions to determine if the new position is already taken.
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
-- Find the next available position in the grid that can accommodate the item's size.
local function getNextEmptyDropPos(item)
-- Searches the grid systematically for an empty spot that fits the 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
-- Sorts items by their size and attempts to place them in the inventory grid efficiently.
local function sortItems(items)
-- A basic sorting algorithm that organizes items to minimize gaps in the inventory.
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
-- Event listener for when the user is dragging an item.
userInputService.InputChanged:Connect(function(input, gameProcessed)
-- Manages the visual feedback of dragging an item across the inventory grid.
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);
-- Event listener for when the user releases the mouse button to drop an item.
userInputService.InputEnded:Connect(function(input)
-- Finalizes the position of the dragged item based on where it's dropped.
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);
-- Initializes and populates the inventory UI with items.
local function loadInventoryItems()
-- Clones item frames from a template, adjusting their position based on inventory data.
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.ItemImg.Image = item.Image.Value
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
-- Initializes the grid layout for the inventory UI.
local function loadInventoryGrid()
-- Dynamically creates the grid parts based on specified size and amount.
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
-- Initial loading of the inventory UI components.
loadInventoryGrid();
loadInventoryItems();
-- Event listener for a UI button designed to reload inventory items.
script.Parent.Parent.ReloadItems.MouseButton1Click:Connect(function()
-- Clears and repopulates the inventory, useful for refreshing or updating the UI.
for _, itemFrame in pairs(invMain:GetChildren()) do
if itemFrame.Name == "InvItem" then
itemFrame:Destroy();
end
end
loadInventoryItems()
end)
Here’s a download for the .rblx file:
GridInventorySystem.rbxl (64.4 KB)