GridPack - Create Grid/Tetris Style Inventories

Iā€™m getting the same issue that heā€™s having, using the way shown in the guide on the website. Using this client code:

game.ReplicatedStorage.InventoryAdd.OnClientEvent:Connect(function(example)
print('k')
	local item = GridPack.createItem({
		Position = Vector2.new(0, 0),
		Size = example.SlotSize,
		Assets = {
			Item = nil,
		},
		Metadata = {
		},
	})
	item.Position = myFirstGrid:GetNextFreePositionForItem(item)
	myFirstGrid:AddItem(item)
end)

And when thereā€™s not enough space, itā€™ll just spit out this error instead of printing out thereā€™s no space:

ReplicatedStorage.Packages.GridPack.ItemManager.Grid:243: invalid argument #1 (Vector2 expected, got nil)

I traced it down to this line:

local collidingItems = self:GetItemsInRegion(at or item.Position, item.Size, withRotation or item.Rotation, ignoredItems)

And it appears that at some point, item.Position just turns up nil. Any idea what could be causing this?

this is so so so good

thanks :slight_smile: :slight_smile: :slight_smile:

I modified Grid:AddItem to look like this:

function Grid:AddItem(item: Types.ItemObject, at: Vector2?, useTween: boolean?)
	local itemPosition = at or item.Position
	local nextFreePosition = self:GetNextFreePositionForItem(item)
	if not nextFreePosition then
		warn("Grid is full, cannot add more items.")
		return
	end
	assert(item.ItemManager == nil, "Could not add item: Item is already in another ItemManager")
	assert(self:IsColliding(item, { item }, nextFreePosition) == false, "Could not add item: Item is colliding with an already added item")
	assert(self:IsRegionInBounds(nextFreePosition, item.Size, item.Rotation) == true, "Could not add item: Item is out of the grid's bounds")

	item.Position = nextFreePosition
	table.insert(self.Items, item)
	self.ItemAdded:Fire(item)

	item.ItemManagerChanged:Fire(self, useTween)
end

it doesnā€™t error when trying to add an item to a full grid

1 Like

Thank you, no longer spits out an error. This should hopefully help us out. Thanks!

1 Like

Awesome resource, Iā€™m curious how youā€™ve gone through the approach of server communication as you canā€™t send any of the tables over since its cyclic and I canā€™t seem to find anyway to do a sanity check without those tables.

Server Communication does not come packaged with the module, though it has support for custom implementations. I did this because not everyoneā€™s server backend looks the same. See: Server Communication | GridPack

Yah I saw that you got any advice on what to send to the server and how to check collisions from the server?

Copy the collision code from the module or write your own.

Hey, so i got something to work, but the only problem is

that if player1 drags tool into grid1
and player2 drags the same tool into grid2

the tool gets parented to grid2 on the server but for player2 they wonā€™t be able to see the tool since it gets parented to ā€œnilā€ cause in the code when moving the grid (SetItemManager) and it just doesnā€™t reparent it for some reason

hereā€™s my code for replication the stuff

...OnClientEvent:Connect(function(tool, newParent, pos, rotation)
    -- collision checks which aren't important in this context
    local item = ItemStorage[tool]
    if item == nil then return end
	item.ItemManager = grid
	item.PotentialRotation = rotation
	item.Rotation = rotation

	item.Position = pos
	item.Rotation = item.PotentialRotation
	item.PositionChanged:Fire(pos, grid)

	item.HoveringItemManager = nil
	item.HoveringItemManagerChanged:Fire(item.HoveringItemManager)

	item.ItemManagerChanged:Fire(grid, true)
end)

It seems like Roblox is garbage collecting the item GUI elements because it doesnā€™t need to be seen for the client. You can fix this by creating a new item when it doesnā€™t exist for the client.

1 Like

Do you plan on releasing an update where grids are supported inside scrolling frames? Wouldnā€™t it be better if the items were parented to another ScreenGui while dragging?

You can write your own system to be compatible with scrolling frames and items parented on Screen Gui. Tho it will require a lot of source code reading.

As a workaround, you could parent a holder frame to the ScrollingFrame

When you want to place an item in a grid and ensure it gets placed even if the current grid is full, you can use a function that attempts to place the item in a sequence of grids (e.g., a main grid and one or more backup grids). If the item canā€™t fit in any of the grids, the function will notify you that all grids are full.
In grid module script

--[=[
	@class Grid
]=]
-- (The rest of your Grid class remains the same)

--[=[
	@within Grid
	Attempts to add an item to the main grid and then to backup grids if necessary.
]=]

function Grid:AddItemToGrids(item: Types.ItemObject, grids: { Types.GridObject }, useTween: boolean?, itemPosition: Vector2?): boolean
	for _, grid in ipairs(grids) do
		local nextFreePosition = grid:GetNextFreePositionForItem(item)
		if nextFreePosition then
			grid:AddItem(item, nextFreePosition, useTween)
			return true
		end
	end

	
	assert(self:IsColliding(item, { item }, itemPosition) == false, "Could not add item: Item is colliding with an already added item")
	return false
end

and this is an example on how to use it

local grids = {mainGrid, backupGrid1, backupGrid2}
local newItemPosition = MainGrid:GetNextFreePositionForItem(YourItem)
Maingrid:AddItemToGrids(YourItem, grids, newItemPosition)

I made a Working phone the app is getting correctly placed in the 3 pages https://youtu.be/4ju0VaqO91E

2 Likes

You should use Fusion to generate the UIs, maybe even make a module to get a grid as a Fusion object

1 Like

Yeah Iā€™ve thought of doing that but since the current UI isnā€™t that advanced I still create it manually. If I ever change my mind about this, I would still wait for Fusion to get a bit more stable in terms of changes, or I would just use react-lua.

can someone help me changing the looks of items inside of the grid?? I just cant get it to work

Hey there! Is there any way to make this compatible with regular Roblox Tools e.g. dropping system within inventory drops tools, and you can equip items in your inventory? I am not very good scripter.
Sorry for my bad english.

1 Like

Yes, this would be possible with SingleSlots which let you a single item in it. And if you want the player to equip a tool when an item has been dragged in you can use the SingleSlot.ItemChanged event to see what item was equipped!

Remember if you want to store any information on the GridPack item then use the itemā€™s metadata property to do so!

Is there a function to toggle the visibility of Items?

Currently i set it up where if you press g the background or grid will disappear. However the items are still there and Iā€™ve no idea how to toggle their visibility

My Code:

--//UtilityFunctions
function GridUi:NewGrid(Visible, GridSize, SlotAspectRatio, AnchorPoint, Position, Size)
	
	--//MainScreen
	local ScreenGui = Instance.new("ScreenGui")
	ScreenGui.Name = "GridPack"
	ScreenGui.ResetOnSpawn = false
	ScreenGui.ZIndexBehavior = Enum.ZIndexBehavior.Sibling
	ScreenGui.Parent = game:GetService("Players").LocalPlayer.PlayerGui

	--//Grids
	local GridPack = GridModule.createGrid({
		Parent = ScreenGui, -- Parent of the grid container.

		Visible = Visible, -- If the grid is visible, changes the containers visible property. Also disables item interaction on all items inside. BY DEFAULT THIS IS SET TO FALSE to prevent the inventory being shown when first creating the Grid.

		Assets = {
			Slot = nil, -- Add your own CanvasGroup here to customize the slots in the grid.
		},

		GridSize = GridSize, -- How many slots the grid has on the X and Y axes.
		SlotAspectRatio = SlotAspectRatio, -- Aspect ratio of one slot in the grid, helps with different resolutions if you're using scale instead of offset.

		AnchorPoint = AnchorPoint, -- Anchor point of the grid container.
		Position = Position, -- Position of the grid container.
		Size = Size, -- Size of the grid container.

		Metadata = {
			-- Here you are free to store any values you want.
		},
	})
	
	local TransferGrid = GridModule.createGrid({
		Parent = ScreenGui,

		Visible = true,

		GridSize = Vector2.new(8, 15),
		SlotAspectRatio = 1,

		AnchorPoint = Vector2.new(1, 0.5),
		Position = UDim2.new(1, -20, 0.5, 0),
		Size = UDim2.fromScale(0.25, 0.5),
	})

	--//TransferLinks
	local TransferLink = GridModule.createTransferLink({}) -- Create TransferLink
	GridPack:ConnectTransferLink(TransferLink) -- Connect TransferLink to our first grid.
	TransferGrid:ConnectTransferLink(TransferLink) -- Connect the TransferLink to our new grid.
	
	--//Items
	GridUi:NewItem(GridPack, Vector2.new(0, 0), Vector2.new(2, 2))
	GridUi:NewItem(GridPack, Vector2.new(0, 2), Vector2.new(4, 4))
	GridUi:NewItem(GridPack, Vector2.new(0, 6), Vector2.new(6, 6))
	
	GridUi:NewItem(TransferGrid, Vector2.new(0, 0), Vector2.new(2, 4))
	GridUi:NewItem(TransferGrid, Vector2.new(0, 4), Vector2.new(1, 3))
	GridUi:NewItem(TransferGrid, Vector2.new(0, 8), Vector2.new(2, 2))
	
	--//Functions
	function GridUi:ToggleInventory()

		GridPack.GuiElement.Visible = not GridPack.GuiElement.Visible
		TransferGrid.GuiElement.Visible = not TransferGrid.GuiElement.Visible
		
	end
	
	
end

Userinput code:

UserInputService.InputBegan:Connect(function(Input, IsTyping, GPE)
	
	if IsTyping == true or GPE == true then return end

	Maid:GiveTask(task.spawn(function()
		
		if Input.KeyCode == Enum.KeyCode.G then
			GridModule.ToggleInventory()
		end
		
	end))
	
end)

GridModule:NewGrid(true,Vector2.new(8, 15),1,Vector2.new(0, 0.5),UDim2.new(0, 20, 0.5, 0),UDim2.fromScale(0.25, 0.5))
1 Like