You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Roblox背包与Hotbar物品移动脚本单向失效问题求助

Roblox库存系统双向移动功能故障修复

问题描述

开发Roblox库存系统,目标实现物品在快捷栏(Hotbar)与背包之间的双向移动。初始运行脚本正常,但执行一次移动操作后,另一项操作便无法生效,问题集中在脚本末尾的两个移动逻辑循环中。

原代码

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

local replicatedStorage = game.ReplicatedStorage
local userInputService
local toolFolder = replicatedStorage.ToolFolder
local images = replicatedStorage.Images
local uis = game:GetService("UserInputService")
local player = game.Players.LocalPlayer
local playergui = player.PlayerGui

local inventory = playergui:WaitForChild("Inventory")
local hotbar = inventory.Hotbar
local inventoryFrame = inventory.Inventory
local itemFrame = inventoryFrame.ScrollingFrame

local hotbarTable = {}
local inventoryTable = {}
local insertedItems = {}
local keyConversions = {
    [Enum.KeyCode.One] = 1,
    [Enum.KeyCode.Two] = 2,
    [Enum.KeyCode.Three] = 3,
    [Enum.KeyCode.Four] = 4,
    [Enum.KeyCode.Five] = 5,
    [Enum.KeyCode.Six] = 6,
    [Enum.KeyCode.Seven] = 7,
    [Enum.KeyCode.Eight] = 8,
    [Enum.KeyCode.Nine] = 9,
    [Enum.KeyCode.Zero] = 10,
}

local function pickSlot()
    for i, v in pairs(hotbarTable) do
        if v.Item == "None" then
            return i
        end
    end
end

local function addItem(item)
    local slot = pickSlot()
    if slot ~= nil then
        hotbarTable[slot].Item = item
        hotbarTable[slot].Count = 1
        hotbarTable[slot].Image = images[item].Texture
        table.insert(insertedItems, item)
    elseif slot == nil then
        inventoryTable[item] = {
            Count = 1;
            Image = images[item].Texture
        }
    end
end


local function fillInventoryTables()
    for i = 1, 10 do
        hotbarTable[i] = {
            Item = "None";
            Count = 0;
            Image = "None"
        }
    end
    for i, v in pairs(toolFolder:GetChildren()) do
        if #insertedItems > 0 then
            if table.find(insertedItems, v.Name) then
                for x, c in pairs(hotbarTable) do
                    if c.Item == v.Name then
                        c.Count += 1
                    end
                end
            else
                addItem(v.Name)
            end
        else
            addItem(v.Name)
        end
    end
end

local function display()
    for i, v in pairs(hotbarTable) do
        hotbar[i].Image = v.Image
        hotbar[i].Counter.Text = v.Count
    end 
    for i, v in pairs(inventoryTable) do
        local clone = inventoryFrame.ScrollingFrame.Sample:Clone()
        clone.Visible = true
        clone.Name = tostring(i)
        clone.Parent = inventoryFrame.ScrollingFrame
        clone.Image = v.Image
        clone.Counter.Text = v.Count
    end
end

local function Equip(key)
    local tool = hotbarTable[key].Item
    if player.Character:FindFirstChildOfClass("Tool") then
        if player.Character:FindFirstChildOfClass("Tool").Name == tool then
            player.Character:FindFirstChild("Humanoid"):UnequipTools()
            player.Backpack[tool]:Destroy()
        elseif player.Character:FindFirstChildOfClass("Tool").Name ~= tool then
            player.Character:FindFirstChild("Humanoid"):UnequipTools()
            player.Backpack:GetChildren()[1]:Destroy()
            toolFolder[tool]:Clone().Parent = player.Backpack
            player.Character:FindFirstChild("Humanoid"):EquipTool(player.Backpack:GetChildren()[1])
        end
    elseif not player.Character:FindFirstChild(tool) then
        toolFolder[tool]:Clone().Parent = player.Backpack
        player.Character:FindFirstChild("Humanoid"):EquipTool(player.Backpack:GetChildren()[1])
    end
end

local function removeInventoryFrames()
    for i, v in pairs(inventoryFrame.ScrollingFrame:GetChildren()) do
        if v.ClassName == "ImageButton" and v.Name ~= "Sample" then
            v:Destroy()
        end
    end
end

fillInventoryTables()
hotbarTable[1].Item = "None"
hotbarTable[1].Count = 0
hotbarTable[1].Image = "None"
display()

uis.InputBegan:Connect(function(key)
    if key.UserInputType == Enum.UserInputType.Keyboard then
        Equip(keyConversions[key.KeyCode])
    end
end)

-- Moving items from backpack to hotbar
for i, v in ipairs(inventoryFrame.ScrollingFrame:GetChildren()) do
    if v.ClassName == "ImageButton" then
        v.MouseButton1Click:Connect(function()
            print(1)
            for x, c in pairs(hotbarTable) do
                if c.Item == "None" then
                    c.Item = v
                    c.Count = v.Counter.Text
                    c.Image = v.Image
                    inventoryTable[v.Name] = nil
                    removeInventoryFrames()
                    display()
                end
            end
        end)
    end
end 

-- Moving items from hotbar to backpack
for i, v in ipairs(hotbar:GetChildren()) do
    if v.ClassName == "ImageButton" then
        v.MouseButton1Click:Connect(function()
            inventoryTable[v.Name] = {
                Count = hotbarTable[tonumber(v.Name)].Count;
                Image = hotbarTable[tonumber(v.Name)].Image
            }
            hotbarTable[tonumber(v.Name)].Item = "None"
            hotbarTable[tonumber(v.Name)].Count = 0
            hotbarTable[tonumber(v.Name)].Image = "None"
            print(v.Name)
            removeInventoryFrames()
            display()
        end)
    end
    print(hotbarTable)
end 

问题分析

  1. 背包到快捷栏的事件绑定仅初始化一次:原代码中第一个for循环仅在脚本启动时遍历滚动框内的控件并绑定点击事件,后续调用display()生成的新背包物品控件没有绑定事件,导致点击无响应。
  2. 快捷栏数据存储错误:背包到快捷栏的逻辑中,c.Item = v将控件实例存入了hotbarTable,而非物品名称,后续判断c.Item == "None"会失效,因为存储的是Instance对象而非字符串。
  3. 循环逻辑冗余:寻找空快捷栏时未在找到后终止循环,可能导致多个槽位被修改。

修复后的代码

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

local replicatedStorage = game.ReplicatedStorage
local toolFolder = replicatedStorage.ToolFolder
local images = replicatedStorage.Images
local uis = game:GetService("UserInputService")
local player = game.Players.LocalPlayer
local playergui = player.PlayerGui

local inventory = playergui:WaitForChild("Inventory")
local hotbar = inventory.Hotbar
local inventoryFrame = inventory.Inventory
local itemFrame = inventoryFrame.ScrollingFrame

local hotbarTable = {}
local inventoryTable = {}
local insertedItems = {}
local keyConversions = {
    [Enum.KeyCode.One] = 1,
    [Enum.KeyCode.Two] = 2,
    [Enum.KeyCode.Three] = 3,
    [Enum.KeyCode.Four] = 4,
    [Enum.KeyCode.Five] = 5,
    [Enum.KeyCode.Six] = 6,
    [Enum.KeyCode.Seven] = 7,
    [Enum.KeyCode.Eight] = 8,
    [Enum.KeyCode.Nine] = 9,
    [Enum.KeyCode.Zero] = 10,
}

local function pickSlot()
    for i, v in pairs(hotbarTable) do
        if v.Item == "None" then
            return i
        end
    end
end

local function addItem(item)
    local slot = pickSlot()
    if slot ~= nil then
        hotbarTable[slot].Item = item
        hotbarTable[slot].Count = 1
        hotbarTable[slot].Image = images[item].Texture
        table.insert(insertedItems, item)
    elseif slot == nil then
        inventoryTable[item] = {
            Count = 1;
            Image = images[item].Texture
        }
    end
end

local function fillInventoryTables()
    for i = 1, 10 do
        hotbarTable[i] = {
            Item = "None";
            Count = 0;
            Image = "None"
        }
    end
    for i, v in pairs(toolFolder:GetChildren()) do
        if #insertedItems > 0 then
            if table.find(insertedItems, v.Name) then
                for x, c in pairs(hotbarTable) do
                    if c.Item == v.Name then
                        c.Count += 1
                    end
                end
            else
                addItem(v.Name)
            end
        else
            addItem(v.Name)
        end
    end
end

-- 新增:绑定背包物品点击事件的函数
local function bindInventoryItemEvents()
    for i, v in ipairs(inventoryFrame.ScrollingFrame:GetChildren()) do
        if v.ClassName == "ImageButton" and v.Name ~= "Sample" then
            -- 先清除已有事件,避免重复绑定
            v.MouseButton1Click:DisconnectAll()
            v.MouseButton1Click:Connect(function()
                local emptySlot = pickSlot()
                if emptySlot then
                    -- 存储物品名称而非控件
                    hotbarTable[emptySlot].Item = v.Name
                    hotbarTable[emptySlot].Count = tonumber(v.Counter.Text)
                    hotbarTable[emptySlot].Image = v.Image
                    inventoryTable[v.Name] = nil
                    removeInventoryFrames()
                    display()
                end
            end)
        end
    end
end

local function display()
    -- 更新快捷栏显示
    for i, v in pairs(hotbarTable) do
        hotbar[i].Image = v.Image
        hotbar[i].Counter.Text = v.Count
    end 
    -- 生成背包物品控件
    for i, v in pairs(inventoryTable) do
        local clone = inventoryFrame.ScrollingFrame.Sample:Clone()
        clone.Visible = true
        clone.Name = tostring(i)
        clone.Parent = inventoryFrame.ScrollingFrame
        clone.Image = v.Image
        clone.Counter.Text = v.Count
    end
    -- 生成控件后立即绑定事件
    bindInventoryItemEvents()
end

local function Equip(key)
    local tool = hotbarTable[key].Item
    if tool == "None" then return end -- 空槽位不执行操作
    if player.Character:FindFirstChildOfClass("Tool") then
        local equippedTool = player.Character:FindFirstChildOfClass("Tool")
        if equippedTool.Name == tool then
            player.Character.Humanoid:UnequipTools()
            player.Backpack[tool]:Destroy()
        else
            player.Character.Humanoid:UnequipTools()
            player.Backpack[equippedTool.Name]:Destroy()
            toolFolder[tool]:Clone().Parent = player.Backpack
            player.Character.Humanoid:EquipTool(player.Backpack[tool])
        end
    else
        toolFolder[tool]:Clone().Parent = player.Backpack
        player.Character.Humanoid:EquipTool(player.Backpack[tool])
    end
end

local function removeInventoryFrames()
    for i, v in pairs(inventoryFrame.ScrollingFrame:GetChildren()) do
        if v.ClassName == "ImageButton" and v.Name ~= "Sample" then
            v:Destroy()
        end
    end
end

fillInventoryTables()
hotbarTable[1].Item = "None"
hotbarTable[1].Count = 0
hotbarTable[1].Image = "None"
display()

uis.InputBegan:Connect(function(key)
    if key.UserInputType == Enum.UserInputType.Keyboard then
        Equip(keyConversions[key.KeyCode])
    end
end)

-- 绑定快捷栏到背包的事件(仅需初始化一次,因为快捷栏控件不会被销毁)
for i, v in ipairs(hotbar:GetChildren()) do
    if v.ClassName == "ImageButton" then
        v.MouseButton1Click:Connect(function()
            local slotNum = tonumber(v.Name)
            local itemData = hotbarTable[slotNum]
            if itemData.Item ~= "None" then
                inventoryTable[itemData.Item] = {
                    Count = itemData.Count;
                    Image = itemData.Image
                }
                hotbarTable[slotNum].Item = "None"
                hotbarTable[slotNum].Count = 0
                hotbarTable[slotNum].Image = "None"
                removeInventoryFrames()
                display()
            end
        end)
    end
end

修复要点说明

  1. 新增bindInventoryItemEvents函数:每次调用display()生成新背包控件后,自动绑定点击事件,确保新生成的物品也能响应移动操作。
  2. 修正数据存储逻辑:背包到快捷栏时,将物品名称存入hotbarTable而非控件实例,保证后续空槽位判断正常。
  3. 优化空槽位查找:直接调用pickSlot()获取第一个空槽位,找到后终止逻辑,避免重复修改。
  4. 完善Equip函数:增加空槽位判断,避免无效操作;直接通过物品名称查找背包工具,替代GetChildren()[1]的不稳定写法。

内容的提问来源于stack exchange,提问作者TrulyBlue2

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.10 20:42:01