Backpack System Help

Hey everyone,

I started working on a small project that requires a custom backpack system. I watched a couple of tutorials and read through posts here on the forum to create this, and I am now running into an issue that I cannot resolve.

When the player equips a tool, the backpack works normally. It equips and unequips the tool as expected. However, when multiple tools are added to the backpack, an issue appears.

For example, if we add one extra tool, the first already existing tool acts as a toggle for tool 1 and tool 2 inside of the backpack, while tool 2 itself equips and unequips the correct tool normally.

Any help is very much appreciated :slightly_smiling_face:

Server:

local ReplicatedStorage = game:GetService("ReplicatedStorage")
local HotbarManagerEvent = ReplicatedStorage.Events.HotbarManagerEvent

HotbarManagerEvent.OnServerEvent:Connect(function(player, tool)
	if not tool or not tool:IsA("Tool") then return end

	local character = player.Character or player.CharacterAdded:Wait()
	local humanoid = character:WaitForChild("Humanoid")

	if tool.Parent ~= character then
		humanoid:UnequipTools()
		humanoid:EquipTool(tool)
	else
		tool.Parent = player.Backpack
	end

	HotbarManagerEvent:FireClient(player)
end)

Local:

game:GetService("StarterGui"):SetCoreGuiEnabled(Enum.CoreGuiType.All, false)

local UIS = game:GetService("UserInputService")
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local HotbarManagerEvent = ReplicatedStorage.Events.HotbarManagerEvent

local player = game:GetService("Players").LocalPlayer
local character = player.Character or player.CharacterAdded:Wait()
local backpack = player:WaitForChild("Backpack")
local hotbar = script.Parent

local MAX_SLOTS = 10
local tools = {}

local numToWord = {
	[1] = "One",
	[2] = "Two",
	[3] = "Three",
	[4] = "Four",
	[5] = "Five",
	[6] = "Six",
	[7] = "Seven",
	[8] = "Eight",
	[9] = "Nine",
	[10] = "Zero"
}

local function rebuildTools()
	tools = {}

	for _, child in ipairs(backpack:GetChildren()) do
		if child:IsA("Tool") then
			table.insert(tools, child)
		end
	end

	for _, child in ipairs(character:GetChildren()) do
		if child:IsA("Tool") and not table.find(tools, child) then
			table.insert(tools, child)
		end
	end
end

local function updateHotbar()
	rebuildTools()

	for _, child in pairs(hotbar:GetChildren()) do
		if child.Name == "Slot" then
			child:Destroy()
		end
	end

	for i, tool in ipairs(tools) do
		if i > MAX_SLOTS then break end

		local slotClone = script.Slot:Clone()
		slotClone.Number.Text = tostring(i)

		if tool.Parent == character then
			slotClone.BackgroundColor3 = Color3.fromRGB(88, 88, 88)
		else
			slotClone.BackgroundColor3 = Color3.fromRGB(57, 57, 57)
		end

		slotClone.Parent = hotbar
	end
end

updateHotbar()

UIS.InputBegan:Connect(function(input, gameProcessed)
	if gameProcessed then return end

	for i = 1, MAX_SLOTS do
		local word = numToWord[i]
		if word and input.KeyCode == Enum.KeyCode[word] then
			local tool = tools[i]
			if tool then
				HotbarManagerEvent:FireServer(tool)
			end
			break
		end
	end
end)

backpack.ChildAdded:Connect(function(child)
	if child:IsA("Tool") then
		updateHotbar()
	end
end)

backpack.ChildRemoved:Connect(function(child)
	if child:IsA("Tool") then
		updateHotbar()
	end
end)

character.ChildAdded:Connect(function(child)
	if child:IsA("Tool") then
		updateHotbar()
	end
end)

character.ChildRemoved:Connect(function(child)
	if child:IsA("Tool") then
		updateHotbar()
	end
end)

HotbarManagerEvent.OnClientEvent:Connect(function()
	rebuildTools()

	for _, slot in pairs(hotbar:GetChildren()) do
		if slot:IsA("Frame") and slot.Name == "Slot" then
			local index = tonumber(slot.Number.Text)
			local tool = tools[index]

			if tool and tool.Parent == character then
				slot.BackgroundColor3 = Color3.fromRGB(88, 88, 88)
			else
				slot.BackgroundColor3 = Color3.fromRGB(57, 57, 57)
			end
		end
	end
end)

3 Likes

server:

local ReplicatedStorage = game:GetService("ReplicatedStorage")
local HotbarManagerEvent = ReplicatedStorage:WaitForChild("Events"):WaitForChild("HotbarManagerEvent")

HotbarManagerEvent.OnServerEvent:Connect(function(player, tool)
	if not tool or not tool:IsA("Tool") then return end

	local character = player.Character or player.CharacterAdded:Wait()
	local humanoid = character:WaitForChild("Humanoid")

	if tool.Parent ~= character then
		-- Only unequip the currently equipped tool
		local currentTool = humanoid:FindFirstChildOfClass("Tool")
		if currentTool then
			currentTool.Parent = player.Backpack
		end

		-- Equip the clicked tool
		humanoid:EquipTool(tool)
	else
		tool.Parent = player.Backpack
	end

	HotbarManagerEvent:FireClient(player)
end)

2 Likes

Your LocalScript determines which tool to fire to the server based on the number of its slot, but the tools in the Tools table shuffle order when you rebuild them. So, if I equip the Sword tool in slot 1, and I try to unequip slot 1, the slot would have changed to a Laser Gun tool, and it’ll equip the Laser Gun.

You can just sort the table each time you create it so that it’s always built in the correct order.

Here’s the LocalScript using alphabetic sort:

View
game:GetService("StarterGui"):SetCoreGuiEnabled(Enum.CoreGuiType.All, false)

local UIS = game:GetService("UserInputService")
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local HotbarManagerEvent = ReplicatedStorage.Events.HotbarManagerEvent

local player = game:GetService("Players").LocalPlayer
local character = player.Character or player.CharacterAdded:Wait()
local backpack = player:WaitForChild("Backpack")
local hotbar = script.Parent

local MAX_SLOTS = 10
local tools = {}

local numToWord = {
	[1] = "One",
	[2] = "Two",
	[3] = "Three",
	[4] = "Four",
	[5] = "Five",
	[6] = "Six",
	[7] = "Seven",
	[8] = "Eight",
	[9] = "Nine",
	[10] = "Zero"
}

local function rebuildTools()
	tools = {}

	for _, child in ipairs(backpack:GetChildren()) do
		if child:IsA("Tool") then
			table.insert(tools, child)
		end
	end

	for _, child in ipairs(character:GetChildren()) do
		if child:IsA("Tool") and not table.find(tools, child) then
			table.insert(tools, child)
		end
	end

	-- Sort tools alphabetically by Name
	table.sort(tools, function(a, b)
		return a.Name < b.Name
	end)
end

local function updateHotbar()
	rebuildTools()

	for _, child in pairs(hotbar:GetChildren()) do
		if child.Name == "Slot" then
			child:Destroy()
		end
	end

	for i, tool in ipairs(tools) do
		if i > MAX_SLOTS then break end

		local slotClone = script.Slot:Clone()
		slotClone.Number.Text = tostring(i)

		if tool.Parent == character then
			slotClone.BackgroundColor3 = Color3.fromRGB(88, 88, 88)
		else
			slotClone.BackgroundColor3 = Color3.fromRGB(57, 57, 57)
		end

		slotClone.Parent = hotbar
	end
end

updateHotbar()

UIS.InputBegan:Connect(function(input, gameProcessed)
	if gameProcessed then return end

	for i = 1, MAX_SLOTS do
		local word = numToWord[i]
		if word and input.KeyCode == Enum.KeyCode[word] then
			local tool = tools[i]
			if tool then
				print(tool)
				HotbarManagerEvent:FireServer(tool)
			end
			break
		end
	end
end)

backpack.ChildAdded:Connect(function(child)
	if child:IsA("Tool") then
		updateHotbar()
	end
end)

backpack.ChildRemoved:Connect(function(child)
	if child:IsA("Tool") then
		updateHotbar()
	end
end)

character.ChildAdded:Connect(function(child)
	if child:IsA("Tool") then
		updateHotbar()
	end
end)

character.ChildRemoved:Connect(function(child)
	if child:IsA("Tool") then
		updateHotbar()
	end
end)

HotbarManagerEvent.OnClientEvent:Connect(function()
	rebuildTools()

	for _, slot in pairs(hotbar:GetChildren()) do
		if slot:IsA("Frame") and slot.Name == "Slot" then
			local index = tonumber(slot.Number.Text)
			local tool = tools[index]

			if tool and tool.Parent == character then
				slot.BackgroundColor3 = Color3.fromRGB(88, 88, 88)
			else
				slot.BackgroundColor3 = Color3.fromRGB(57, 57, 57)
			end
		end
	end
end)
2 Likes

I really appreciate it, thanks :slight_smile:

2 Likes

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