What is a good way to make a slot-based Inventory like Minecraft?

So I’ve been working on my Game with my Team, as the Lead scripter I’ve run into a problem with the Inventory System. We want to model off Minecraft’s slot-based Inventory and Hotbar. Is there any defined way we can achieve this?

6 Likes

Do you need help with creating the GUI or scripting it to make it work? Is there anything specific you’re stuck on? And do you need it to work with any existing systems in your game?

1 Like

Honestly your topic lacks context. You have told us there is a problem but never defined what the problem was. Also creating an inventory system like minecraft is pretty easy if you already know how to script an inventory; there is minor changes.

3 Likes

Well, you have 2 main components, the hotbar and the inventory. I would lay it out like this:

local inventory = {
    {["Apple"] = 64, ["Orange"] = 24}, -- the first row is the hotbar
    {["Apple"] = 64, ["Orange"] = 24} -- all the other rows are in the main inventory
}
2 Likes

I believe 2 tables would be unnecessary since the hotbar/inventory basically intertwine with each other, also a dictionary based inventory wouldn’t work since it wouldn’t be very friendly with listing stacks of the same items but with varying quantities. Therefore an array-based inventory system would be the more practical option.

1 Like

Yeah, i forgot you could have multiple stacks of the same object :man_facepalming:. Array based is probably easier if you have a fixed grid system, no idea what I was thinking when i came up with this

2 Likes
local inventory = {
	[1] = {"item_1", 20}
}
inventory[1][2] -- quantity
inventory[1][1] -- item name

now, to detect whether an item is in the hotbar, you simply just check whether the first index is 1-(max hotbar slots)

2 Likes

Thats a pretty good start, thank you!

1 Like

I’m looking for a base to start off from if you will, I don’t need any particular GUI just a base to start scripting off of. And currently, there is no system in development to intertwine with the Inventory in Development.

2 Likes

Well, you’ll need to have the inventory data in a table, as you (pretty much) need that to store it in a data store later. It’s also the most convenient way to work with the data IMO (as opposed to having lots of StringValue or whatever objects).

Since inventory spots don’t interact with their neighbors or anything like that, you don’t need a 2D representation. So you can just have a (1D) list of inventory slots and their contents. Since each slot can only hold one item type, but several items of that type, the contents can be stored as just an item type ID and a count. This is more efficient that storing say a table of 64 identical objects. But for e.g. damaged items you need additional data (the health) which varies per item even of the same item type, so in that case you do need an object per item. Let’s just store a single object per slot and a count, and optional additional data inside the object, and disallow stacking objects with additional data.

So an item might look like {itemTypeId} or {itemTypeId, health}. Each inventory slot looks like {itemReference, itemCount}. And the completee inventory data table looks like {backpack={slot, slot, ..., slot}, hotbar={slot, slot, ..., slot}} since there are two regions in the inventory that we want to differentiate between.

Picking up a stackable item means looking for a slot of the same item type with room for more and incrementing that slot’s count. An unstackable item, or a stackable one with no non-full existing stack means finding the first empty slot, setting the item type and setting the count to 1. If there are no empty slots in the latter case, the item can’t be picked up. Well, and there’s the two zones to take care of.

function findAvailableSlot(inventory, item)
    --1st pass, look for existing stacks
    for _, zone in {inventory.hotbar, inventory.backpack} do
        for _, slot in zone do
            if slot.itemTypeId == item.itemTypeId and slot.count < MAX_STACK_SIZE then
                return slot, zone
            end
        end
    end

    --2nd pass, look for empty slots
    for _, zone in {inventory.hotbar, inventory.backpack} do
        for _, slot in zone do
            if slot.itemCount == 0 then
                return slot, zone
            end
        end
    end
    
    return nil
end

function tryPickingUp(player, item)
    local inventory = playerInventories[player]
    assert(not item.pickedUp)
    local slot, slotZone = findAvailableSlot(inventory, item)
    if slot then
        if slot.count == 0 then slot.itemTypeId = item.itemTypeId end
        slot.count += 1
        item.pickedUp = true --Or find some other way of preventing picking up an item more than once.
        item.model:Destroy()
        updateSlotGui(slot, slotZone)
    end
end

game.Players.PlayerAdded:Connect(function(player)
    local inventory = {
        hotbar={}, 
        backpack={},
    }
    for i = 1, 10 do
        local slot = {zone = inventory.hotbar, slotNumber = i, itemCount = 0, itemTypeId = -1}
        inventory.hotbar[i] = slot
    end
    for i = 1, 100 do -- or however many
        local slot = {zone = inventory.horbar, slotNumber = i, itemCount = 0, itemTypeId = -1}
        inventory.backpack[i] = slot
    end
    playerInventories[player] = inventory
end)

To connect the GUI to the data, you can store each slot GUI in an array:

local slotGuis = {
    hotbar={},
    backpack={},
}
function setupInventoryGui()
    for i, slotGui in screenGui:FindFirstChild("Hotbar", true):GetChildren() do
        if slotGui.Name == "Slot" then
            slotGuis.hotbar[i] = slotGui
        end
    end
    for i, slotGui in screenGui:FindFirstChild("Backpack", true):GetChildren() do
        if slotGui.Name == "Slot" then
            slotGuis.backpack[i] = slotGui
        end
    end
end

function updateSlotGui(slot)
    local gui
    if slot.zone == "hotbar" then
        gui = slotGuis.hotbar
    else
        gui = slotGuis.backpack
    end
    gui.ImageLabel.Image = ITEM_TYPE_ICONS[slot.itemTypeId]
    gui.TextLabel.Text = slot.itemCount
end

As usual you’ll have to run some code on the server and some on the client, and use RemoteEvents/RemoteFunctions to communicate between them.

2 Likes

Wow, that makes alot of sense, Tysm!

1 Like

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