Satchel // Open-source modern backpack system

Any ETA on .Draggable usage being removed?
I use satchel in a project of mine but I constantly have the thought of the fact that deprecated stuff is being used. (Well just one thing in this case but still)
I’m somebody who spends hours just trying to fix typechecking issues so maybe thats just me but I’m still not happy about a deprecated property being used.

I would try to do it myself but I’m kinda busy doing other stuff, also I didnt write satchel so I don’t have a full enough understanding of what everything does.

Nicely done— just one suggestion: an additional button when an item is added to your inventory rather than your hotbar with an ellipses (…) icon which can be pressed to easily open the inventory.

1 Like

This is an interesting way of dropping the tool instead of just using the existing functions for dropping the tool.

I would be interested in seeing the PR and how it would be implemented. With Satchel we need to ensure compatibility with it’s target platforms: computer, phone, tablet, console, and VR. So heads up that the PR would need to be compatible with it’s targert platforms to be merged.

Currently there is no ETA on removal .Draggable. While I did plan to remove the deprecated property, I’ve ran into some issues implementing it, especially on VR platforms.

You can track the progress at the below GitHub issue.

That is unfortunate. However, your comment saying that there are no problems is incorrect. After trying it on a lower framerate (like 30FPS), this issue is more visible:

I believe it is necessary to use .InputBegan to your implementation.

local UserInputService = game:GetService("UserInputService")

local gui = script.Parent

local dragging
local dragInput
local dragStart
local startPos

gui:GetPropertyChangedSignal("CanvasPosition"):Connect(function()
	dragging = false
end)

local function update(input)
	local delta = input.Position - dragStart
	gui.Position = UDim2.new(startPos.X.Scale, startPos.X.Offset + delta.X, startPos.Y.Scale, startPos.Y.Offset + delta.Y)
end

gui.InputBegan:Connect(function(input)
	if input.UserInputType == Enum.UserInputType.MouseButton1 or input.UserInputType == Enum.UserInputType.Touch then
		dragging = true
		dragStart = input.Position
		startPos = gui.Position
		input.Changed:Connect(function()
			if input.UserInputState == Enum.UserInputState.End then
				dragging = false
			end
		end)
	end
end)

gui.InputChanged:Connect(function(input)
	if input.UserInputType == Enum.UserInputType.MouseMovement or input.UserInputType == Enum.UserInputType.Touch then --for VR, there is no dragging implementation, so remove this line
		dragInput = input
	end
end)

UserInputService.InputChanged:Connect(function(input)
	if input == dragInput and dragging then
		update(input)
	end
end)

This is a modified implementation of the one that Roblox staff have sent. I’m unsure if it works on VR, but if not, that may be a limitation of the inputObject.Position property and should not behave differently from GuiObject.Draggable

2 Likes

Hi again, get ready for another suggestion! This one will involve the ScrollingFrame, let’s begin!


(Tools are copied from Satchel Playground and duplicated for demonstration purposes.)

You may already notice that if a player scrolls down a little bit, they’ll start seeing the attachments below.


So if you want to change that and you want it to look like the attachments below


Here’s how we can do it, but keep in mind this might cause some problems in some platforms like VR and XBox, so don’t forget to fix them. (If it turns out it actually has problems)


Code 1

We are going to change this

local MainFrame = nil
local HotbarFrame = nil
local InventoryFrame = nil
local VRInventorySelector = nil
local ScrollingFrame: ScrollingFrame = nil
local UIGridFrame: Frame = nil
local UIGridLayout: UIGridLayout = nil
local ScrollUpInventoryButton = nil
local ScrollDownInventoryButton = nil

To this, here we have added two instances/things; a UIListLayout, and a Frame called SpacingFrame

local MainFrame = nil
local HotbarFrame = nil
local InventoryFrame = nil
local VRInventorySelector = nil
local ScrollingFrame: ScrollingFrame = nil
local UIListLayout: UIListLayout = nil -- [[ADDED]]
local SpacingFrame: Frame = nil -- [[ADDED]]
local UIGridFrame: Frame = nil
local UIGridLayout: UIGridLayout = nil
local ScrollUpInventoryButton = nil
local ScrollDownInventoryButton = nil

Code 2

We are going to change this

local function UpdateScrollingFrameCanvasSize(): ()
	local countX = math.floor(ScrollingFrame.AbsoluteSize.X / (ICON_SIZE_PIXELS + ICON_BUFFER_PIXELS))
	local maxRow = math.ceil((#UIGridFrame:GetChildren() - 1) / countX)
	local canvasSizeY = maxRow * (ICON_SIZE_PIXELS + ICON_BUFFER_PIXELS) + ICON_BUFFER_PIXELS
	ScrollingFrame.CanvasSize = UDim2.new(0, 0, 0, canvasSizeY)
end

To this, here we have changed one line of code, which is the ScrollingFrame’s CanvasSize, I just added 35 into the code, 35 I believe is the most consistent, 36 is less consistent, and 40 is the most inconsistent but kind of looks fine (I tried my best to make ScrollingFrame’s CanvasSize constant), ehh I don’t know but moving on!

local function UpdateScrollingFrameCanvasSize(): ()
	local countX = math.floor(ScrollingFrame.AbsoluteSize.X / (ICON_SIZE_PIXELS + ICON_BUFFER_PIXELS))
	local maxRow = math.ceil((#UIGridFrame:GetChildren() - 1) / countX)
	local canvasSizeY = maxRow * (ICON_SIZE_PIXELS + ICON_BUFFER_PIXELS) + ICON_BUFFER_PIXELS
	ScrollingFrame.CanvasSize = UDim2.new(0, 0, 0, canvasSizeY + 35) -- [[EDITED/FORKED]], or 36 or 40, UDim2.new(0, 0, 0, canvasSizeY)
end

Code 3 (the longest code here)

We going to change this

local function UpdateBackpackLayout(): ()
	HotbarFrame.Size = UDim2.new(
		0,
		ICON_BUFFER_PIXELS + (NumberOfHotbarSlots * (ICON_SIZE_PIXELS + ICON_BUFFER_PIXELS)),
		0,
		ICON_BUFFER_PIXELS + ICON_SIZE_PIXELS + ICON_BUFFER_PIXELS
	)
	HotbarFrame.Position = UDim2.new(0.5, -HotbarFrame.Size.X.Offset / 2, 1, -HotbarFrame.Size.Y.Offset)
	InventoryFrame.Size = UDim2.new(
		0,
		HotbarFrame.Size.X.Offset,
		0,
		(HotbarFrame.Size.Y.Offset * NumberOfInventoryRows)
			+ INVENTORY_HEADER_SIZE
			+ (IsVR and 2 * INVENTORY_ARROWS_BUFFER_VR or 0)
	)
	InventoryFrame.Position = UDim2.new(
		0.5,
		-InventoryFrame.Size.X.Offset / 2,
		1,
		HotbarFrame.Position.Y.Offset - InventoryFrame.Size.Y.Offset
	)

	ScrollingFrame.Size = UDim2.new(
		1,
		ScrollingFrame.ScrollBarThickness + 1,
		1,
		-INVENTORY_HEADER_SIZE - (IsVR and 2 * INVENTORY_ARROWS_BUFFER_VR or 0)
	)
	ScrollingFrame.Position = UDim2.new(0, 0, 0, INVENTORY_HEADER_SIZE + (IsVR and INVENTORY_ARROWS_BUFFER_VR or 0))
	AdjustHotbarFrames()
	AdjustInventoryFrames()
end

To this, here we have made the ScrollingFrame’s Size to be the same height as the Inventory Frame, and we have made the ScrollingFrame’s Position to be completely Zero, or UDim2.new(0, 0, 0, 0). This is the part that could possibly cause problems with VR, but since I don’t have a headset (or VR headset), I don’t have a solution to fix the problem if I knew there was a problem, so keep note of that I guess, moving on

local function UpdateBackpackLayout(): ()
	HotbarFrame.Size = UDim2.new(
		0,
		ICON_BUFFER_PIXELS + (NumberOfHotbarSlots * (ICON_SIZE_PIXELS + ICON_BUFFER_PIXELS)),
		0,
		ICON_BUFFER_PIXELS + ICON_SIZE_PIXELS + ICON_BUFFER_PIXELS
	)
	HotbarFrame.Position = UDim2.new(0.5, -HotbarFrame.Size.X.Offset / 2, 1, -HotbarFrame.Size.Y.Offset)
	InventoryFrame.Size = UDim2.new(
		0,
		HotbarFrame.Size.X.Offset,
		0,
		(HotbarFrame.Size.Y.Offset * NumberOfInventoryRows)
			+ INVENTORY_HEADER_SIZE
			+ (IsVR and 2 * INVENTORY_ARROWS_BUFFER_VR or 0)
	)
	InventoryFrame.Position = UDim2.new(
		0.5,
		-InventoryFrame.Size.X.Offset / 2,
		1,
		HotbarFrame.Position.Y.Offset - InventoryFrame.Size.Y.Offset
	)

	ScrollingFrame.Size = UDim2.new(
		1,
		ScrollingFrame.ScrollBarThickness + 1,
		1,
		0) -- -INVENTORY_HEADER_SIZE - (IsVR and 2 * INVENTORY_ARROWS_BUFFER_VR or 0) -- [[EDITED/FORKED]]
	-- ) -- [[EDITED/FORKED]]
	ScrollingFrame.Position = UDim2.new(0, 0, 0, 0) -- UDim2.new(0, 0, 0, INVENTORY_HEADER_SIZE + (IsVR and INVENTORY_ARROWS_BUFFER_VR or 0)) -- [[EDITED/FORKED]]
	AdjustHotbarFrames()
	AdjustInventoryFrames()
end

Code 4 (and Lastly)

We are going to change this

-- Make the ScrollingFrame, which holds the rest of the Slots (however many)
ScrollingFrame = NewGui("ScrollingFrame", "ScrollingFrame")
ScrollingFrame.Selectable = false
ScrollingFrame.ScrollingDirection = Enum.ScrollingDirection.Y
ScrollingFrame.ScrollBarThickness = 8
ScrollingFrame.ScrollBarImageColor3 = Color3.new(1, 1, 1)
ScrollingFrame.VerticalScrollBarInset = Enum.ScrollBarInset.ScrollBar
ScrollingFrame.CanvasSize = UDim2.new(0, 0, 0, 0)
ScrollingFrame.Parent = InventoryFrame

To this, here we have added two of the things/instances I have mentioned in Code 1; a UIListLayout, and a Frame called SpacingFrame, I have also finally added their properties here. (I tried my best to make SpacingFrame’s Size consistent as well)

-- Make the ScrollingFrame, which holds the rest of the Slots (however many)
ScrollingFrame = NewGui("ScrollingFrame", "ScrollingFrame")
ScrollingFrame.Selectable = false
ScrollingFrame.ScrollingDirection = Enum.ScrollingDirection.Y
ScrollingFrame.ScrollBarThickness = 8
ScrollingFrame.ScrollBarImageColor3 = Color3.new(1, 1, 1)
ScrollingFrame.VerticalScrollBarInset = Enum.ScrollBarInset.ScrollBar
ScrollingFrame.CanvasSize = UDim2.new(0, 0, 0, 0)
ScrollingFrame.Parent = InventoryFrame

UIListLayout = Instance.new("UIListLayout") -- [[ADDED]]
UIListLayout.SortOrder = Enum.SortOrder.LayoutOrder
UIListLayout.HorizontalAlignment = Enum.HorizontalAlignment.Center
UIListLayout.Parent = ScrollingFrame

SpacingFrame = NewGui("Frame", "SpacingFrame") -- [[ADDED]]
SpacingFrame.Selectable = false
SpacingFrame.Size = UDim2.new(1, 0, 0, 40)
--SpacingFrame.Position = UDim2.new(0, ICON_BUFFER_PIXELS, 0, 0)
SpacingFrame.Parent = ScrollingFrame

And there you go, that’s how we can achieve it. Here’s another place file if you wanna see how it works
Im_Andr3i’s Suggestions or Forks for Satchel 2.rbxl (654.5 KB)
What’s new in there is there’s a new TestBackpack ScreenGui thing that doesn’t work, an AllForks + ScrollingFrame Fork Script, and a ScrollingFrame Fork Script.

To see the changes I’ve made in a SatchelScript, use the Find feature by clicking the Find button or by pressing Ctrl/Control + G then type “Added” or “Edited/Forked”.

I hope this is helpful and don’t forget to take breaks, take care!

1 Like

By the way, you can change:

gui.Changed:Connect(function(c)
	if c=="CanvasPosition" then
		dragging = false
	end
end)

To:

gui:GetPropertyChangedSignal("CanvasPosition"):Connect(function()
	dragging = false
end)
1 Like

Yes, the code I shared was made before that method was available, and I forgot to change it. Thank you for the information.

It was a mistake from my end while making that comment and not researching enough into how buggy .Draggable is.

Thanks for the code, I will look into it and test the compatibility across platforms.

The clipping is intentional behavior to help improve the visibility of the search bar. I do like how nicely it was put together but for now your solution won’t be implemented officially. Thanks for the help though.

1 Like

Hey, Is there any way I could make the hotbar always visible?
Thank you.

The hotbar should be visible by default. Can you specify what you mean?

I notice that the hotbar will go visible if you have a tool in backpack.

oh… my… gosh… i was literally JUST working on a backpack gui (which wasnt going so great) and before i log off i see THIS thread? You lifesaver!

I’ll look into making it always show the hotbar even when empty. Ideally this can be toggled by an attribute.

Very nice module, was able to quickly implement it to my game to solve an issue with the base backpack interfering with GUI. Though I wish the Hotbar’s size and offset could quickly be edited within the Attributes. But it’s not like its hard at all to edit.

Would also love for an easy setup to allow the hotbar to be displayed and ordered vertically instead of the default horizontal.
But overall really good!

Are these errors fine for them to be there?

Yes, they shouldn’t affect anything.

I used Visual Studio Code for the development of Satchel, and warnings from Roblox’s IDE don’t properly display in VSCode so that is why they are warnings.

They should be no errors (red) but warnings are fine (orange).

2 Likes

What would I use to disable the backpack in a script? I have a handcuff system which disables the coregui backpack when you are cuffed but I dont know how to make it so it disables the satchel backpack instead.

You would use :SetBackpackEnabled(false) to disable Satchel.