Making a Custom Inventory

You can write your topic however you want, but you need to answer these questions:

  1. What do you want to achieve? Keep it simple and clear!
    :sparkles: Make a super custom inventory! :sparkles:
  2. What is the issue? Include screenshots / videos if possible!

I have 2 problems that may be super simple to solve but I don’t realize

This is how the inventory gui it’s looks

The biggest problem is that when I drop an item or equip it, the slot it belongs to is still in use for some reason but when I grab a new item the slot is updated with the information of the new item but I don’t want that exactly and I can’t find a way to make the slot empty if there is no item in the inventory folder, I already tried several things but ended up discarding them, such as conditions etc, but without result.

The small problem what i imagine is super easy to resolve is that the items when equipping or throwing them stop working

local Player = game.Players.LocalPlayer
local Mouse = Player:GetMouse()
local Inventory = Player:WaitForChild("Inventory")


local Manager = script.Parent:WaitForChild("Manager")

local Gui = script.Parent
local Frame = Gui.Manager.ScrollingFrame
local Slots = Frame:GetChildren()

local Sound = Player.PlayerGui:WaitForChild("InventoryOpen")

Mouse.KeyDown:Connect(function(Key)
	if Key == "b" then
		Manager.Visible = not Manager.Visible
		Sound:Play()
	end
end)

local function Clear()
	for _,v in pairs(Slots) do
		if v:IsA("TextButton") then
			v.AutoButtonColor = false
			v.Text = ""
		end
	end
end

local function Drop(Object)
	for _,v in pairs(Slots) do --i made a big mistake here, and i dont know how to figure how to drop only the selected item
		if v:IsA("TextButton") then
			local Tool = Inventory:WaitForChild(v.Text)
			Tool.Parent = game.Workspace
			Tool.Handle.CFrame = Player.Character.HumanoidRootPart.CFrame * CFrame.new(0,0,4.5) --items drops near of player but they can't be picked up again, why?
			v:ClearAllChildren()
		end
	end
end

local function Grab(Object)
	for _,v in pairs(Slots) do
		if v:IsA("TextButton") then
			local Tool = Inventory:WaitForChild(v.Text)
			Tool.Parent = Player.Backpack --It works fine but the tools don't work, as well as how to pick them up --Sorry my bad english
			v:ClearAllChildren()
		end
	end
end

local function Cancel()
	for _,v in pairs(Slots) do
		if v:IsA("TextButton") then
			v:ClearAllChildren()
		end
	end
end

local function GetOptions(Object)
	local Options = {}
	
	table.insert(Options, {
		"Grab",
		function()
			Grab(Object)
		end,
	})
	
	table.insert(Options, {
		"Drop",
		function()
			Drop(Object)
		end,
	})
	
	table.insert(Options, {
		"Cancel",
		function()
			Cancel()
		end,
	})
	
	return Options
end

local function ShowOptions(Slot, Object)
	local Options = GetOptions(Object)
	
	for i = 1, #Options do
		local Button = Instance.new("TextButton")
		Button.Size = UDim2.new(1, 0, 1/#Options, 0)
		Button.Position = UDim2.new(0,0,i/#Options - 1/#Options, 0)
		Button.Text = Options[i][1]
		Button.MouseButton1Click:Connect(Options[i][2])
		Button.Parent = Slot
	end
end

local function Update()
	local InventoryTab = Inventory:GetChildren()
	for i = 1, #InventoryTab do
		Slots[i].Text = InventoryTab[i].Name
		Slots[i].AutoButtonColor = true 
														--here the problem is what inventory no detect when there's no tool in inventory folder and the slot is still in use
		
		Slots[i].MouseButton1Down:Connect(function()
			Cancel()
			ShowOptions(Slots[i], InventoryTab[i])
		end)
	end
end

Manager:GetPropertyChangedSignal("Visible"):Connect(function()
	Update()
end)

I hope someone help me resolving this T-T

CustomInventory.rbxl (68,8 KB)

3 Likes

Hi, please explain how the inventory GUI you’re making is related to Tools. Are all objects Tools, and how are they organized in Player.Inventory? Screenshots of the Explorer hierarchy would be helpful here. I’m also not sure if InventoryTab means “inventory table” or if it means you’re planning on having a tabbed interface later on (tabbed as in it has tabs, like browser tabs). And do you always want inventory slots to be in the same order as things in the “InventoryTab”? Because in some inventory systems you can have empty slots before filled ones and if you also want that then that changes how it should be programmed.

2 Likes

Hello, thanks for answering, im sending the screenshots

local Inventory = Player:WaitForChild(“Inventory”)
InventoryTab = Inventory:GetChildren()

1 Like


image
Im using UiGridLayout for all the slots in LayoutOrder

1 Like

How about the very last question I asked?

1 Like

I want them to be in the same order with no empty slots pls

1 Like

I spent some time trying to figure out out and had to make some changes to be able to understand it all. Here’s a version with those changes and lots of comments explaining what I did. A lot of it is just for my own sake to better understand what your script does. The stuff related to the main question is at line 88, in the Update function.

local Player = game.Players.LocalPlayer
local Mouse = Player:GetMouse()
local Inventory = Player:WaitForChild("Inventory")

local OpenCloseSound = Player.PlayerGui:WaitForChild("InventoryOpen")

local Gui = script.Parent --I'd rename it to ScreenGui and have *all* GUIs be children of it, otherwise you'd need to have multiple ScreenGuis
local InventoryGui = Gui.Manager --I'd rename it to just Inventory
local SlotContainer = InventoryGui.ScrollingFrame

local Tools = {} --Populated in Update(), so every time the inventory opens or when the Inventory items change.
local Slots = SlotContainer:GetChildren()
--If it's called Slots then it shouldn't contain things other than Slots. This simplifies AND shortens other functions that use Slots
for i = #Slots, 1, -1 do --Backwards iteration is needed because the loop changes the table.
	if not Slots[i]:IsA("TextButton") then
		table.remove(Slots, i)
	end
end

local function SlotGetTool(Tool)
	local SlotNumber = tonumber(Slot.Name) 
	local Tool = Tools[SlotNumber]
	assert(Tool ~= nil, ("No Tool for Slot %s!"):format(Slot:GetFullName())) --Shouldn't happen because Options only opens on Slots with Tools, so probably unnecessary.
	return Tool
end

local function SlotDrop(Slot)
	--Don't need to do it for all Slots since we now know which Slot we're working on

	local Tool = GetSlotTool(Slot)
	Tool.Parent = game.Workspace
	Tool.Handle.CFrame = Player.Character.HumanoidRootPart.CFrame * CFrame.new(0, 0, 4.5) --TODO: items drops near of player but they can't be picked up again, why?
	
	SlotCloseOptions(Slot)
end

local function SlotGrab(Slot)
	--Don't need to do it for all Slots since we now know which Slot we're working on
	
	local Tool = GetSlotTool(Slot)
	Tool.Parent = Player.Backpack --TODO: It works fine but the tools don't work, as well as how to pick them up --Sorry my bad english
	
	SlotCloseOptions(Slot)
end

local function SlotCloseOptions(Slot)
	--Don't need to do it for all Slots since we now know which Slot we're working on (well, except at the very button of the script. Still worth.)
	Slot:ClearAllChildren()
end

local function GetOptions()
	--Using a table literal to construct the table rather than lots of table.insert calls, shorter and simpler.
	--Also just using the option name as the key in a dictionary.
	--Also not having the option handlers tied to a specific Tool, just figure out which one it is when the handler is run based on the Slot.
	local Options = {
		["Grab"] = SlotGrab,
		["Drop"] = SlotDrop,
		["Cancel"] = SlotCloseOptions,
	}
	return Options
end

local function SlotOpenOptions(Slot)
	local Options = GetOptions(Object)

	--Having these variable names instead of Options[i][1] and Options[i][2] is lots more readable
	for optionName, optionHandler in pairs(Options) do
		local Button = Instance.new("TextButton")
		Button.Size = UDim2.new(1, 0, 1/#Options, 0)
		Button.Position = UDim2.new(0,0,i/#Options - 1/#Options, 0)
		Button.Text = optionName
		Button.MouseButton1Click:Connect(function() optionHandler(Slot) end) --Change how we call the option handler to pass along which Slot it's related to
		Button.Parent = Slot
	end
end

local function Update()
	--This needs to be shared by all the other functions, because we can't rely on GetChildren returning children in the same order
	Tools = Inventory:GetChildren()

	--I changed the loop to be foreach Slot instead of foreach Tool, because we also want to process empty Slots
	for _, Slot in ipairs(Slots) do --Can't use the iterator variable because GetChildren isn't always in order
		local SlotNumber = tonumber(Slot.Name) 
		local Tool = Tools[SlotNumber]

		if Tool then
			Slot.AutoButtonColor = true
			Slot.Text = Item.Name
		else
			Slot.AutoButtonColor = false
			Slot.Text = ""
		end

		--I'm not setting up the click event listener here, because then you'd have to disconnect the connections again each time.
		--Instead doing it at the bottom of the script.
	end
end

Mouse.KeyDown:Connect(function(Key)
	if Key == "b" then
		InventoryGui.Visible = not InventoryGui.Visible
		OpenCloseSound:Play()
	end
end)

InventoryGui:GetPropertyChangedSignal("Visible"):Connect(function()
	if not InventoryGui.Visible then return end --No point updating if it's closed
	Update()
end)

--It's not enough to update the inventory when it's opened. Also update it when a tool is added to or removed from Inventory.
Inventory.ChildAdded:Connect(Update)
Inventory.ChildRemoved:Connect(Update)

for _, Slot in ipairs(Slots) do
	--Don't need to check it's actually a TextButton because we did that eariler
	Slot.MouseButton1Down:Connect(function()
		for _, OtherSlot in ipairs(Slot) do
			SlotCloseOptions(OtherSlot)
		end

		--Don't open the options if there's no Tool in this Slot
		local SlotNumber = tonumber(Slot.Name) 
		local Tool = Tools[SlotNumber]
		if not Tool then return end

		--Actually, having Slots[i] and InventoryTab[i] here before was a BUG, because those tables might change in the mean time before 
		--	this function runs, so i is no longer the right index. Putting the Slot and Item in variables like this means they'll always
		--	refer to the same things (unless we set them to something else).
		SlotOpenOptions(Slot) --Ended up not using Item anyway
	end)
end

I can’t test it so there might be bugs, sorry. And I don’t know about your secondary question.

4 Likes

I’m going to test it, thanks for trying to help me!

1 Like

Yeah i found a bug in 118 line
image
so near of the end of the script and wow you made a lot of work

1 Like

Thanks, everything works fine except for line 118, the script detects when the tool is no longer found and vice versa, thank you, you’re a genius but I still can’t finish the thread

1 Like

Whoops missed an “s”, it should be Slots and not Slot on line 118.

2 Likes

Oh I didn’t realize too, uhh there’s another bug and do you have an idea how to fix it if it’s not too much to ask?
Sin título

image
I try using optionName like in my older script with for i = 1,

2 Likes

rewriting i by 1, it fixes the error but the options don’t appear

1 Like

Hi, yeah since i was the iterator variable in the old script, there’s no way in the new script to see which number option it is. Add this before that loop:

local i = 0

and this on the first line of the loop:

i += 1
1 Like

Works but i can’t explaing it but this appears in the properties of the 3 buttons, and that would explain why they do not appear

Changing changing them manually in-game works.
What if i add a UIGridLayout inside Slots?
image

1 Like

Adding UIGridLayout correctly fixes display of buttons

1 Like

Thank you for everything, it works correctly I really appreciate it

1 Like

This topic was automatically closed 14 days after the last reply. New replies are no longer allowed.