Making custom hotbar: why does every other button on it not work?

Getting the hang of things and perhaps got too ambitious after learning about ipairs, lists and for loops, etc, and decided to try and replace my very broken inventory GUI i got from elsewhere with something made from scratch. It’s a bit messy I think, but so far my script works… mostly (save that I suspect I’ll actually have to change how equipping tools work to run through remote events, like the wearables do).

The problem I am having right now is that every other button on the hotbar (the second, fourth, sixth and eighth) seem to be doing nothing at all, while the odd-numbered ones are doing exactly what they should. The item moves to the hotbar and can be swapped to another item just fine, but the broken buttons on the hotbar don’t effect what’s in the player’s hand.

Any other advice on how to make this smoother is of course very appreciated, as I can’t say I have much experience in either making a UI or scripting overall! Please pardon my excessive commenting

local StarterGui = game:GetService("StarterGui")

StarterGui:SetCoreGuiEnabled(Enum.CoreGuiType.Backpack, false)

--getting contents of player's backpack
local plr = game:GetService("Players")
local pack = plr.LocalPlayer:WaitForChild("Backpack")

--parts of the hotbar
local hb = script.Parent.hotbar
local openInv = hb.openinvButton


--parts of the inventory frames
local inv = script.Parent.inventory

local iDisplay = inv.itemDisplay.display
local BaseButton = iDisplay.baseButton

local toinv = inv.toHotbar:GetChildren()


--item info

local iInfo = inv.infoFrame
local useButton = iInfo["wear/activate"]
local iNameSpace = iInfo.itemName
local iDescSpace = iInfo.desc


--item categories

local catAcc = inv.catBtns.aButton.Accesories --3
local catToy = inv.catBtns.tButton.Toys --2
local catItem = inv.catBtns.iButton.Items --1

--states
invopen = false
currentCat = 0 --none/closed

--currently selected item
local selItem = "" 
local selTex = ""

--uisounds
local blip = script.Parent.sfx.blip
local bloop = script.Parent.sfx.bloop
local tic = script.Parent.sfx.tick



local function PopulateList() -- populate the list with whatever is in the player's backpack
	local bp = pack:GetChildren() -- by taking stock of what's in the players backpack
	for i, item in ipairs(bp) do
		local cat = item:GetAttribute("Category")
		if cat == currentCat then
			print("Making button for " .. item.Name .. " in category " .. tostring(cat))
			local newButton = BaseButton:Clone() --and making a button with the relevent information needed for that button
			newButton.Name = "invbutton"
			newButton.Image = item.TextureId
			newButton.Visible = true
			newButton.Parent = iDisplay --parent it to the display frame for the inventory
			newButton.Item.Value = item.Name -- and set a value to the name of the asset the item uses
			
			local iname = item:GetAttribute("ItemName") --the public facing name of the item
			local idesc = item:GetAttribute("ItemDesc") --item's description. sometimes contains hints as to the item's use, or bits of lore.
			--probably could have just used the tooltip property. oops
			
			
			local selName = ""
			local selDesc = ""
			

			newButton.Activated:Connect(function()
				
				--when activated, this item info displays
				selTex = item.TextureId --texture ID fo the tool
				selName = iname --item's name cosmetically
				selDesc = idesc --item's description
				selItem = item.Name --item's name in the script/workspace
				
				if cat == 3 then --cat three is wearables, they should not be moved to that hot bar
					useButton.Text = "Wear" --so the text reflects this
				else
					useButton.Text = "Equip" --maybe should consider changing the phrasing here for clarity idk
				end
			end)
			
			newButton.MouseLeave:Connect(function() -- when the mouse leaves a button, display the info for the item last selected
				iDescSpace.Text = selDesc
				iNameSpace.Text = selName
			end)
			
			newButton.MouseEnter:Connect(function() -- display the info for the moused over item
				iNameSpace.Text = iname
				iDescSpace.Text = idesc
			end)
			--probably will make this just show the selected items info later since this seems wonky as heck in practice
				
				
			print("button made for" .. item.Name .. ". ItemName: " .. iname .. ". Item Desc:" .. idesc )
		end
	end
end

local function ClearList() --clear out what was previously displayed
	local iDisplayContents = iDisplay:getChildren()
	local layout = iDisplay.layout
	for i, items in ipairs(iDisplayContents) do
		if items.Name ~= BaseButton.Name and items.Name ~= layout.Name then --please don't delete the layout and button to copy. i promise we need those for really real
			items:Destroy() --get rid of all the buttons Except That One, so that the display can be repopulated with items from a new category
		end
	end
end

openInv.Activated:Connect(function()
	if invopen == false then
		plr.LocalPlayer.CameraMinZoomDistance = 1 --makes sure PC players can't trap themselves in scrollable inventory gui by entering first person withem it open
		ClearList() --GET RID OF IT
		task.wait(0.1)
		currentCat = 1
		PopulateList() --DO IT OVER AGAIN GRAAH
		invopen = true
		inv.Visible = true
				
	elseif invopen == true then
		plr.LocalPlayer.CameraMinZoomDistance = 0.5 --lets the player enter first person again. fly fly be free
		inv.Visible = false
		invopen = false
		currentCat = 0
		ClearList()
	end
end)


catItem.Activated:Connect(function() --consumable items, temporary things player is meant to lose
	ClearList()
	currentCat = 1
	PopulateList()
end)

catToy.Activated:Connect(function() --toys, permanent prizes for doing things in game. 
	ClearList()
	currentCat = 2
	PopulateList()
end)

catAcc.Activated:Connect(function() --accessories/wearables. like prizes, but wearable insead. pins hats etc
	ClearList()
	currentCat = 3
	PopulateList()
end)


useButton.Activated:Connect(function()	
	local hbButtons = hb.buttons:GetChildren()
	
	local function refreshToHB(spaces,buttons) --not sure if this and setImage really need to be their own nested functions but it didn't workl before i did this for some reason so we keep on truckin
		--but this sets up the buttons so that functions are connected to their events
		
		spaces.Activated:Connect(function() 
			if spaces.Name == buttons.Name then
				for i, buttons in ipairs(hbButtons) do --not actually sure if i need to go through all the buttons in the hotbar. is it excessive? should i add a return if there's a match so the loop stops?
					if buttons.ClassName == "ImageButton" and spaces.Name == buttons.Name then
						buttons.Image = selTex
						buttons.ImageTransparency = 0
						buttons.item.Value = selItem

						buttons.Activated:Connect(function()		 --sets up the hotbar buttons to equip the item (tool) they're connected to			
							local hum = plr.LocalPlayer.Character:FindFirstChildWhichIsA("Humanoid")
							local targetItem = buttons.item.Value
							local olditem = hum.Parent:FindFirstChildWhichIsA("Tool") --check what, if any, item is equipped
							

							if olditem and olditem.Name == buttons.item.Value then --if you press the button and are already holding the connected item, unequip the tool
								olditem.Parent = pack
							elseif olditem then -- else if you have an old item and are replacing it with a new one, move the equipped tool to the backpack before equipping the new one
								olditem.Parent = pack
								hum:EquipTool(plr.LocalPlayer.Backpack[targetItem])
							else --if there is no old item at all, just equip the new one guilt-free. hurray, we didn't cast anything into the void to get here!
								hum:EquipTool(plr.LocalPlayer.Backpack[targetItem])
							end
						end)
						return
					end
				end
			end
			inv.toHotbar.Visible = false --hide the buttons to change the hotbar's contents
		end)
	end
	
	local function setImg()		
		inv.toHotbar.Visible = true
		for i, spaces in ipairs(toinv) do
			if spaces.ClassName == "ImageButton" then
				for i, buttons in ipairs(hbButtons) do
					if buttons.Name == spaces.Name then  -- if the names for the spaces match, 
						spaces.Image = buttons.Image -- set the image for the hotbar button selection... button, which is supposed to display what's already on your hotbar
						if spaces.Image ~= "rbxasset://textures/ui/GuiImagePlaceholder.png" then --if it's still just a placeholer image tho, the image should be hidden for befuglyness
							spaces.ImageTransparency = 0
						end
					end
					refreshToHB(spaces,buttons) --do the funtion we stuffed alk the meat and potatoes into in a fit of desperation, it's refreshHB's problem now. if it works i will channel todd howard energy into my body at this point to hockeysticks with it
				end
			end
		end
	end
	

--wearables category should toggle the wearable instead of move the item to the hotbar
		if currentCat == 3 then
			local acc = selItem
			local isworn = true
			local wearEvnt = game.ReplicatedStorage.Events.change
			game.ReplicatedStorage.Events.change:FireServer(acc,isworn) --do out event for putting on accessories instead
			print(acc .. "equipped!")
	else
		print("Should have done stuff to side buttons!") -- it sure does

		setImg() --get on the carousel of functions above. should this exist within god's creation?
	end
end)

But I think it’s probably?? Something to do with this part:

useButton.Activated:Connect(function()	
	local hbButtons = hb.buttons:GetChildren()
	
	local function refreshToHB(spaces,buttons) --not sure if this and setImage really need to be their own nested functions but it didn't workl before i did this for some reason so we keep on truckin
		--but this sets up the buttons so that functions are connected to their events
		
		spaces.Activated:Connect(function() 
			if spaces.Name == buttons.Name then
				for i, buttons in ipairs(hbButtons) do --not actually sure if i need to go through all the buttons in the hotbar. is it excessive? should i add a return if there's a match so the loop stops?
					if buttons.ClassName == "ImageButton" and spaces.Name == buttons.Name then
						buttons.Image = selTex
						buttons.ImageTransparency = 0
						buttons.item.Value = selItem

						buttons.Activated:Connect(function()		 --sets up the hotbar buttons to equip the item (tool) they're connected to			
							local hum = plr.LocalPlayer.Character:FindFirstChildWhichIsA("Humanoid")
							local targetItem = buttons.item.Value
							local olditem = hum.Parent:FindFirstChildWhichIsA("Tool") --check what, if any, item is equipped
							

							if olditem and olditem.Name == buttons.item.Value then --if you press the button and are already holding the connected item, unequip the tool
								olditem.Parent = pack
							elseif olditem then -- else if you have an old item and are replacing it with a new one, move the equipped tool to the backpack before equipping the new one
								olditem.Parent = pack
								hum:EquipTool(plr.LocalPlayer.Backpack[targetItem])
							else --if there is no old item at all, just equip the new one guilt-free. hurray, we didn't cast anything into the void to get here!
								hum:EquipTool(plr.LocalPlayer.Backpack[targetItem])
							end
						end)
						return
					end
				end
			end
			inv.toHotbar.Visible = false --hide the buttons to change the hotbar's contents
		end)
	end
	
	local function setImg()		
		inv.toHotbar.Visible = true
		for i, spaces in ipairs(toinv) do
			if spaces.ClassName == "ImageButton" then
				for i, buttons in ipairs(hbButtons) do
					if buttons.Name == spaces.Name then  -- if the names for the spaces match, 
						spaces.Image = buttons.Image -- set the image for the hotbar button selection... button, which is supposed to display what's already on your hotbar
						if spaces.Image ~= "rbxasset://textures/ui/GuiImagePlaceholder.png" then --if it's still just a placeholer image tho, the image should be hidden for befuglyness
							spaces.ImageTransparency = 0
						end
					end
					refreshToHB(spaces,buttons) --do the funtion we stuffed alk the meat and potatoes into in a fit of desperation, it's refreshHB's problem now. if it works i will channel todd howard energy into my body at this point to hockeysticks with it
				end
			end
		end
	end
	

--wearables category should toggle the wearable instead of move the item to the hotbar
		if currentCat == 3 then
			local acc = selItem
			local isworn = true
			local wearEvnt = game.ReplicatedStorage.Events.change
			game.ReplicatedStorage.Events.change:FireServer(acc,isworn) --do out event for putting on accessories instead
			print(acc .. "equipped!")
	else
		print("Should have done stuff to side buttons!") -- it sure does

		setImg() --get on the carousel of functions above. should this exist within god's creation?
	end
end)

It look like that your script just making Activated event over and over again causing the effect where every prime number will be Equip > UnEquip tool really fast causing you to see like nothing happen

(Example:
on 4th slot these event will happen in sequence equip > unequip > equip >unequip

on 3rd slot this will happen equip > unequip > equip

on 6th slot this will happen equip > unequip > equip > unequip > equip > unequip)

my recommended advise is to rework the loop ipair part but here is my temporary solution it might help you understand a bit more

local Connections = {}
useButton.Activated:Connect(function()	
	local hbButtons = hb.buttons:GetChildren()

	local function refreshToHB(spaces,buttons) --not sure if this and setImage really need to be their own nested functions but it didn't workl before i did this for some reason so we keep on truckin
		--but this sets up the buttons so that functions are connected to their events
		
		spaces.Activated:Once(function()
			buttons.Image = selTex
			buttons.ImageTransparency = 0
			buttons.item.Value = selItem
			
			if Connections[spaces.Name] then --Disconnect old connection before binding new one
				Connections[spaces.Name]:Disconnect()
			end

			Connections[spaces.Name] = buttons.Activated:Connect(function()		 --sets up the hotbar buttons to equip the item (tool) they're connected to			
				local hum = plr.LocalPlayer.Character:FindFirstChildWhichIsA("Humanoid")
				local targetItem = buttons.item.Value
				local olditem = hum.Parent:FindFirstChildWhichIsA("Tool") --check what, if any, item is equipped

				if olditem and olditem.Name == buttons.item.Value then --if you press the button and are already holding the connected item, unequip the tool
					olditem.Parent = pack
				elseif olditem then -- else if you have an old item and are replacing it with a new one, move the equipped tool to the backpack before equipping the new one
					olditem.Parent = pack
					hum:EquipTool(plr.LocalPlayer.Backpack[targetItem])
				else --if there is no old item at all, just equip the new one guilt-free. hurray, we didn't cast anything into the void to get here!
					hum:EquipTool(plr.LocalPlayer.Backpack[targetItem])
				end
			end)
			
			inv.toHotbar.Visible = false --hide the buttons to change the hotbar's contents
		end)
	end

	local function setImg()		
		inv.toHotbar.Visible = true
		
		
		for i, spaces in ipairs(toinv) do
			if spaces.ClassName == "ImageButton" then
				for i, buttons : ImageButton in ipairs(hbButtons) do

					if buttons.Name == spaces.Name then  -- if the names for the spaces match, 
						spaces.Image = buttons.Image -- set the image for the hotbar button selection... button, which is supposed to display what's already on your hotbar
						if spaces.Image ~= "rbxasset://textures/ui/GuiImagePlaceholder.png" then --if it's still just a placeholer image tho, the image should be hidden for befuglyness
							spaces.ImageTransparency = 0
						end
						
						
						buttons.ImageColor3 = Color3.fromRGB(math.random(0,255),math.random(0,255),math.random(0,255))
						
						refreshToHB(spaces,buttons) --do the funtion we stuffed alk the meat and potatoes into in a fit of desperation, it's refreshHB's problem now. if it works i will channel todd howard energy into my body at this point to hockeysticks with it
					end
					
				end
			end
		end
	end


	--wearables category should toggle the wearable instead of move the item to the hotbar
	if currentCat == 3 then
		local acc = selItem
		local isworn = true
		local wearEvnt = game.ReplicatedStorage.Events.change
		game.ReplicatedStorage.Events.change:FireServer(acc,isworn) --do out event for putting on accessories instead
		print(acc .. "equipped!")
	else
		print("Should have done stuff to side buttons!") -- it sure does

		setImg() --get on the carousel of functions above. should this exist within god's creation?
	end
end)

Ohh, I was wondering if I should try to find a stop to that loop or if it was safe to let it run - guess it’s not! Thanks for making sense of this for me, and for the temp fix. I’ll see what I can’t do about that…

1 Like

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