[PLEASE READ BEFORE MOVING ON]:
I’d like to make it very clear that this tutorial + the model are extremely unoptimized and should not be used anymore. I made this post back when I had less experience with what all that goes into keeping a game healthy with lag and memory. This module has many minor and maybe some major (haven’t looked back at it in months) issues that could cost your game’s performance. I’m keeping this post and tutorial + module up in case anyone wants to use it for reference, but if you do end up using it as it is, please be warned that it was written very poorly. I do apologize for anyone who went off of this tutorial, as there are many bad practices that I didn’t know I was making back when I posted this.
Thanks for reading.
OLD POST:
Hey everyone!
This is going to be my first community tutorial/resource guide to the public. I wanted to pick something in the middle grounds. Something that isn’t extremely basic, and something that isn’t over the top, especially since it’s my first time posting a tutorial like this.
That’s why I have chosen to put out to the public a fully functional advanced inventory system, as it’s something that I think not a ton has been covered about, especially with Roblox’s default inventory system already in place, and there being no way to customize that.
One more thing before I start the tutorial. I want to make it clear that I intend this to be a learning tool, and not just a mindless copy and paste where you don’t know what’s going on. I also want to make it clear that this tutorial is directed more towards intermediate scripters, or people who don’t quite understand how to do what this post is about.
[If you’re stuck, I’ve provided a link to the the entire system/model so you can take a look yourself (down below)]
Alright, enough of my introduction. Let’s get into it!
First things first, open up Studio, and insert a ScreenGui asset into the StarterGui.
- This will allow us to have a custom inventory GUI.
- This will also be the holder for the system (everything related should go inside here).
Next, we need to insert a Frame asset into the ScreenGui, and resize and center it. You should also insert a UIAspectRatioConstraint into the frame, and set the aspect ratio so that the frame is elongated on the X (horizontal) axis.
Note : you may want to resize the frame on the Scale factor, so it fits nicely on all devices. Make sure to also set the AnchorPoint for the frame to be 0.5, 0.5, and then drag it back to where you need it.
- This is going to be the hotbar for the inventory
Now we need to have “Slots” for the hotbar. We can do this by adding a UIListLayout set on the horizontal axis. Then we can add frames for however many slots you want.
This is what the system should look like so far:
- You can customize it however you like, as long as it follows the basics of the system.
Next we need to create the “Bag”, or in other words, the whole inventory frame that stores your items (if they’re not in the hotbar). We can do this by creating a box frame, just like the hotbar frame, but this time have the UIAspectRatioConstraint’s AspectRatio set to 1.5
- Again, you can customize it how you like as long as it follows the basics just like I said before.
We can then add in a ScrollingFrame to the bag frame, and a UIGridLayout to the ScrollingFrame and create a seperate frame identical to one of the slot frames (without the slotNum text). This will be our ItemTemplate frame, which we can duplicate and place inside the bag frame whenever a new item gets added to our inventory.
Note : It is important that the ScrollingFrame is inside of the bag frame, and set to the size of (1, 0, 1, 0) so that it is the full size of the bag frame. And the UIGridLayout goes inside the ScrollingFrame. The ItemTemplate frame must be outside of the ScrollingFrame, placed inside of the bag frame itself. (Also don’t forget to change the visibility to false)
Alright! That should be it for the base GUI setup. Now for fun part, scripting! However before we begin, here is what the entire system should look like now:
The bag frame should have its visibility set to false
To start, we can insert a LocalScript into the GUI, and call it whatever you want. We also need to add a ModuleScript inside the LocalScript, and call it whatever you want (keep in mind it will be referenced within the LocalScript)
First, let’s define some variables, beginning with important Services that we’re going to use, all the way down to the GUI itself and the enum table:
----[[VARIABLES]]----
local PLRS = game:GetService("Players") -- the players service
local UIS = game:GetService("UserInputService") -- the user input service, used for detecting inputs from the player/client
local SG = game:GetService("StarterGui")
local inventory_module = require(script:WaitForChild("INVENTORY_TASKS")) -- requring the module script
local player = PLRS.LocalPlayer -- the local player
local char = player.Character -- the player's character
local hum = char:WaitForChild("Humanoid") -- the character's humanoid
local backpack = player.Backpack -- the player's backpack (used to store all tools by default)
local gui = script.Parent -- the gui
local bag = gui:WaitForChild("BAG") -- the bag/inventory frame
local hotbar = gui:WaitForChild("HOTBAR") -- the hotbar frame
local itemTemplate = bag:WaitForChild("ItemTemplate")
local slotDragger = gui:WaitForChild("SLOT_DRAGGER")
local justEquipped = false
local currentFrameBeingHoveredOn = nil
local currentItemSelected = nil
local isDraggingItem = false
local currentMousePos = nil
-- a table used to convert all the slots to enums (this will be helpful when detecting player inputs)
local slotsToEnum = {
[1] = Enum.KeyCode.One,
[2] = Enum.KeyCode.Two,
[3] = Enum.KeyCode.Three,
[4] = Enum.KeyCode.Four,
[5] = Enum.KeyCode.Five,
[6] = Enum.KeyCode.Six,
}
SG:SetCoreGuiEnabled(Enum.CoreGuiType.Backpack, false) -- disabling the default roblox backpack
Once we have that down, we can start on the basic functions:
----[[FUNCTIONS]]----
local function toggleBag(toggle)
if toggle == true then
bag.Visible = true
elseif toggle == false then
bag.Visible = false
end
end
local function inputBegan(input, processed)
if justEquipped == true then
justEquipped = false
end
for _, item in pairs(backpack:GetChildren()) do
if item:IsA("Tool") then
if item.slotIn.Value ~= 0 then
if input.KeyCode == slotsToEnum[item.slotIn.Value] then
justEquipped = true
inventory_module.ItemUnequip(hum, hotbar)
inventory_module.ItemEquip(item, hum, hotbar)
end
end
end
end
if justEquipped == false then
for _, item in pairs(char:GetChildren()) do
if item:IsA("Tool") then
if item.slotIn.Value ~= 0 then
if input.KeyCode == slotsToEnum[item.slotIn.Value] then
inventory_module.ItemUnequip(hum, hotbar)
end
end
end
end
end
if input.KeyCode == Enum.KeyCode.G and not processed then
if bag.Visible == true then
toggleBag(false)
elseif bag.Visible == false then
toggleBag(true)
end
elseif input.UserInputType == Enum.UserInputType.MouseButton1 then
isDraggingItem = true
inventory_module.MouseDown(currentMousePos, slotDragger, backpack, bag, hotbar, itemTemplate)
end
end
local function inputEnded(input, processed)
if input.UserInputType == Enum.UserInputType.MouseButton1 then
isDraggingItem = false
inventory_module.MouseUp(slotDragger, hotbar, bag, backpack, itemTemplate)
end
end
bag.MouseEnter:Connect(function()
inventory_module.HoverFrameChange(true, bag)
end)
bag.MouseLeave:Connect(function()
if isDraggingItem == false then
inventory_module.HoverFrameChange(false)
end
end)
for _, slot in pairs(hotbar:GetChildren()) do
if slot:IsA("Frame") then
slot.MouseEnter:Connect(function()
inventory_module.HoverFrameChange(true, slot)
end)
slot.MouseLeave:Connect(function()
if isDraggingItem == false then
inventory_module.HoverFrameChange(false)
end
end)
end
end
backpack.ChildAdded:Connect(function(child)
if child:IsA("Tool") then
inventory_module.BagLoad(bag, backpack, itemTemplate)
end
end)
backpack.ChildRemoved:Connect(function(child)
if child:IsA("Tool") then
inventory_module.BagLoad(bag, backpack, itemTemplate)
end
end)
UIS.InputBegan:Connect(inputBegan)
UIS.InputEnded:Connect(inputEnded)
inventory_module.BagLoad(bag, backpack, itemTemplate)
inventory_module.HotbarLoad(hotbar, backpack)
And here is the ModuleScript as well:
local module = {}
local UIS = game:GetService("UserInputService")
local slotsToNums = {
["SLOT1"] = 1,
["SLOT2"] = 2,
["SLOT3"] = 3,
["SLOT4"] = 4,
["SLOT5"] = 5
}
currentFrameBeingHoveredOn = nil
currentItemSelected = nil
isDraggingItem = false
function module.HoverFrameChange(toggle, frame)
if toggle == true then
currentFrameBeingHoveredOn = frame
else
currentFrameBeingHoveredOn = nil
end
end
function module.BagLoad(bag, backpack, itemTemplate)
for _, frame in pairs(bag.ScrollingFrame:GetChildren()) do
if frame:IsA("Frame") then
frame.Visible = false
end
end
for _, item in pairs(backpack:GetChildren()) do
if item:IsA("Tool") and item.slotIn.Value == 0 then
if not bag.ScrollingFrame:FindFirstChild(item.Name) then
local newItemFrame = itemTemplate:Clone()
newItemFrame.Name = item.Name
newItemFrame.itemImage.Image = item.TextureId
newItemFrame.Visible = true
newItemFrame.Parent = bag.ScrollingFrame
newItemFrame.MouseEnter:Connect(function()
module.HoverFrameChange(true, newItemFrame)
end)
newItemFrame.MouseLeave:Connect(function()
if isDraggingItem == false then
module.HoverFrameChange(false)
end
end)
elseif bag.ScrollingFrame:FindFirstChild(item.Name) then
bag.ScrollingFrame:FindFirstChild(item.Name).Visible = true
bag.ScrollingFrame:FindFirstChild(item.Name).MouseEnter:Connect(function()
module.HoverFrameChange(true, bag.ScrollingFrame:FindFirstChild(item.Name))
end)
bag.ScrollingFrame:FindFirstChild(item.Name).MouseLeave:Connect(function()
if isDraggingItem == false then
module.HoverFrameChange(false)
end
end)
end
end
end
end
function module.HotbarLoad(hotbar, backpack)
for _, item in pairs(backpack:GetChildren()) do
if item:IsA("Tool") and item.slotIn.Value ~= 0 then
hotbar:FindFirstChild("SLOT"..tostring(item.slotIn.Value)).itemImage.Image = item.TextureId
end
end
end
function module.MouseDown(currentMousePos, slotDragger, backpack, bag, hotbar, itemTemplate)
if isDraggingItem == false and currentFrameBeingHoveredOn then
if currentFrameBeingHoveredOn:IsDescendantOf(bag) == true then
isDraggingItem = true
slotDragger.itemImage.Image = currentFrameBeingHoveredOn.itemImage.Image
slotDragger.Visible = true
currentFrameBeingHoveredOn.Visible = false
for _, item in pairs(backpack:GetChildren()) do
if item:IsA("Tool") then
if item.Name == currentFrameBeingHoveredOn.Name then
currentItemSelected = item
end
end
end
print(currentFrameBeingHoveredOn.Name)
while isDraggingItem == true do
currentMousePos = UIS:GetMouseLocation()
slotDragger.Position = UDim2.fromOffset(currentMousePos.X, currentMousePos.Y)
task.wait()
end
module.BagLoad(bag, backpack, itemTemplate)
elseif currentFrameBeingHoveredOn:IsDescendantOf(hotbar) == true then
for _, item in pairs(backpack:GetChildren()) do
if item:IsA("Tool") then
if item.slotIn.Value == slotsToNums[currentFrameBeingHoveredOn.Name] then
currentItemSelected = item
break
else
currentItemSelected = nil
end
end
end
if currentItemSelected then
isDraggingItem = true
slotDragger.itemImage.Image = currentFrameBeingHoveredOn.itemImage.Image
slotDragger.Visible = true
currentFrameBeingHoveredOn.itemImage.Image = ""
while isDraggingItem == true do
currentMousePos = UIS:GetMouseLocation()
slotDragger.Position = UDim2.fromOffset(currentMousePos.X, currentMousePos.Y)
task.wait()
end
end
end
end
end
function module.MouseUp(slotDragger, hotbar, bag, backpack, itemTemplate)
isDraggingItem = false
if currentItemSelected and currentFrameBeingHoveredOn then
if currentFrameBeingHoveredOn:IsDescendantOf(hotbar) == true then
for _, otherItem in pairs(backpack:GetChildren()) do
if otherItem:IsA("Tool") and otherItem ~= currentItemSelected and otherItem.slotIn.Value == slotsToNums[currentFrameBeingHoveredOn.Name] then
if currentItemSelected.slotIn.Value == 0 and otherItem.slotIn.Value ~= 0 then
hotbar:FindFirstChild("SLOT"..tostring(otherItem.slotIn.Value)).itemImage.Image = ""
end
otherItem.slotIn.Value = currentItemSelected.slotIn.Value
end
end
currentItemSelected.slotIn.Value = slotsToNums[currentFrameBeingHoveredOn.Name]
elseif currentFrameBeingHoveredOn == bag then
currentItemSelected.slotIn.Value = 0
end
end
module.BagLoad(bag, backpack, itemTemplate)
module.HotbarLoad(hotbar, backpack)
slotDragger.Visible = false
currentItemSelected = nil
end
function module.ItemEquip(item, hum, hotbar)
hum:EquipTool(item)
hotbar:FindFirstChild("SLOT"..tostring(item.slotIn.Value)).UIStroke.Color = Color3.fromRGB(255, 255, 255)
end
function module.ItemUnequip(hum, hotbar)
hum:UnequipTools()
for _, slot in pairs(hotbar:GetChildren()) do
if slot:IsA("Frame") then
slot.UIStroke.Color = Color3.fromRGB(20, 20, 20)
end
end
end
return module
If you’re wanting to add items for the inventory, you’ll need to add an IntValue, called “slotIn”
And it’s as simple (well, not really, but you get the point) as that!
I hope you learned something from this, and if not, I hope I helped you if you were in need of a custom inventory system. It’s fairly basic, but it gets the job done, and if you want, you can expand on it as much as you like.
If you have any questions, please by all means, feel free to ask me!
Here is the link to the system/model as well as a showcase of the final product: