Ah this is very helpful! I’m still lost on the main problem I’ve stated; detecting which slots the object is hovering over when you’re dragging it that way you can actually check if its occupied using that matrix.
Sure! I’ll use the Roblox backpack source as an example again,
Let’s take a look at it. This time we’ll look at the Hotbar functionality instead, because the backpack itself self organizes (doesn’t have slots).
I’ll be combining some important information into a singular Lua block, but I will write its source line next it.
Source (click to open)
local HOTBAR_SLOTS_FULL = 10 -- 41
local FullHotbarSlots = 0 -- 143
local function CheckBounds(guiObject, x, y) -- 270
local pos = guiObject.AbsolutePosition
local size = guiObject.AbsoluteSize
return (x > pos.X and x <= pos.X + size.X and y > pos.Y and y <= pos.Y + size.Y)
end -- 274
-- Check where we were dropped (line 768)
if CheckBounds(InventoryFrame, x, y) then
if slot.Index <= NumberOfHotbarSlots then
slot:MoveToInventory()
end
-- Check for double clicking on an inventory slot, to move into empty hotbar slot
if slot.Index > NumberOfHotbarSlots and now - lastUpTime < DOUBLE_CLICK_TIME then
if LowestEmptySlot then
local myTool = slot.Tool
slot:Clear()
LowestEmptySlot:Fill(myTool)
slot:Delete()
end
now = 0 -- Resets the timer
end
elseif CheckBounds(HotbarFrame, x, y) then
local closest = {math.huge, nil}
for i = 1, NumberOfHotbarSlots do
local otherSlot = Slots[i]
local offset = GetOffset(otherSlot.Frame, Vector2.new(x, y))
if offset < closest[1] then
closest = {offset, otherSlot}
end
end -- 791
Not included in the above code is SlotFrame.DragStopped, which takes X,Y of stopping position. These X,Y values along with InventoryFrame pass to CheckBounds function.
What you’re actually interested in learning is the functionality of this CheckBounds function. The reason this custom function is used instead of UIObject.MouseEnter/MouseLeave is likely because that functionality will be prone to problems depending on device. If your game is exclusively on computer, you might as well use MouseEnter/MouseLeave and self solve the edge cases.
CheckBounds works like this:
→ Check if a Hotbar slot is available, if not move item to inventory
→ If a hotbar slot is available, run CheckBounds function, which compares X & Y of HotbarFrame to the X,Y where the mouse stopped dragging. These all return as a single statement that decides whether or not you dropped into the Hotbar frame.
→ Then, this statement iterates each Hotbar slot individually and updates closest
variable (one scope higher) to that specific Slot. GetOffset(otherSlot.Frame, Vector2.new(x, y)) -- line 787
- GetOffset will return the magnitude of the Vector2 between a Slot’s absolute position & and where the mouse stopped dragging.
- If the offset is less than the former lowest offset, it updates the closest frame.
→ These updates are made, and then tracked by top level variables for future referencing.
Keep in mind that this is for a nearly fullproof system, and you could simply write a function to check the MousePosition against Slot positioning to decide the closest slot and see perfectly fine functionality. Hope this helps.
I learned inventory from AlvinBlox’s Egg Hatching tutorial, realized how bad that was, then made my own. It’s a pretty limited perspective. But what I do know:
- You can store inventories based on a parent/child relationship. In the GUI, you drop the item into a frame and it populates the inventory graphically. Those individual frames have item values and that could be your table. BUT! This is a client side inventory system, so its garbage.
- You need a server side inventory system. A table will do, unless you introduce stacking, in which case, you are probably going to need a full dictionary. Use the table to control what ends up in the GUI, not use the GUI to control what’s in the table.
I start with “inventory[player] = {}” then everything else is a table/dictionary inside that.
The GUI needs to be able to be called to add/remove/modify the contents from the server. From the client to the server, I use a GUID (unique ID) to validate all requests. The GUI is a hollow shell.
Here’s the setup, which looks very close to the Egg Hatching tutorial. Template stored in the client side script and is dropped into the ScrollingFrame. The difference is the script doesn’t actually do anything but process the item passed along from the server.
The UIGridLayout sets up the rows and columns. You don’t need a matrix. Give each template the GUID it needs to communicate back to the server.
Dictionaries are tables, arrays and dictionaries are the same type and will be treated the same >>type({})
or >>type({["Dog"] = true}
, both are table types.
Also, the concept that you “don’t need a matrix” is a non-point. Caching inventories current state on Client is important, even if you validate updates through the server. You could have one or not have one, it doesn’t matter as long as your client can access the information.
OP’s main problem is recognizing what Slot an item should drop into, you still need a function that Checks your slot bounds to decide this. Entirely relying on UIGridLayout insinuates an inventory system that automatically places objects to a predetermined slot (does not dynamically decide or allow the user to organize their inventory).
May I reiterate that its not the inventory system that’s the issue. In fact I could explain exactly what I’ve done here with the actually inventory system itself (sever side).
The issue I’m having has to do with visuals and recognizing which slot(s) the item should drop into when drug to and frow. The problem is all client side (Gui based) not actual inventory system based. It’s the players interaction with the inventor GUI that I’m trying to figure out.
Well that explains how to tell which slots to drop into, but would I run the check on every slot in the inventory its in to tell if its within that slot?
The source code provided does that as well, I may not have explained it well:
local closest = {math.huge, nil}
for i = 1, NumberOfHotbarSlots do
local otherSlot = Slots[i]
local offset = GetOffset(otherSlot.Frame, Vector2.new(x, y))
if offset < closest[1] then
closest = {offset, otherSlot}
end
end
A higher scope variable is created, and then each slot is individually assessed based on its distance from the mouse’s position. A shorter Vector2 magnitude means the higher scope variable writes into that specific slot.
Mind you the calculation is not just mouse distance to AbsolutePosition of each slot, it calculates distance from where the box bounds reach to.
Oh wow that’s actually perfect! So that does mean basically every time the player moves their mouse, this check should be ran?
No, that would be pointless in most scenarios.
I explained this above, the code also has a custom wrote function “DragStopped”, which acts as the action event for running this function (player is dragging something and wants to drop it into a slot).
Oh yes, I miss understood. I’ll see what I can do and the get back to you.
So after reading a bit and looking at the core script source you provided, I can’t quite tell if I’ll need to make an OOP and each slot be an object created from an OOP object, then just have a check bounds function in each one and have them check themselves to see if the object being drug and stopped dragging is in the bound of itself and if it is, return true. Or in the end just do what I was doing before with the matrix thing…