So if I’m understanding you correctly, this grid is supposed to represent 16 different cubes where stuff goes in you inventory. You want to know how to find the positions of this grid on the screen. If you look at the person example in the video, they have grey squares for their grid. You should do this as well, make a separate GUI for every 0 and reference it’s position. For example you could make an invisible GUI on the upper left corner of the gui, and use it as the first 0’s position. So when someone picks something up, if the first 0 in the table is 0, it will put a picture of the thing they picked up (I’m assuming you can do that) where the corresponding GUI is and turn the 0 to 1. Now if you’re talking about how they did the multiple square items, you can just take two squares, and turn two 0s to ones (putting the image of the item in between the two). This might be unprofessional to say, and I know that big block of writing might be confusing, so I will provide examples later when I can respond thoroughly.
You can represent the space that each item takes up as a similar grid:
local stoneInvShape = {
{ 1 }
}
local daggerInvShape = {
{ 1 },
{ 1 },
}
local scytheInvShape = {
{ 1, 1, 1 },
{ 0, 0, 1 },
{ 0, 0, 1 },
{ 0, 0, 1 },
}
You can then test if a shape fits into a grid like this:
local SLOT_EMPTY = 0
local SLOT_FILLED = 1
function shapeFitsInGridAtPoint(shape, grid, gridPointX, gridPointY)
--Check that every 1 in the shape fits inside the grid and doesn't overlap with 1s, if the shape were to be placed with its upper left point at (gridPointX, gridPointY).
for shapeX = 1, #shape[1] do
for shapeY = 1, #shape do
local gridX, gridY = gridPointX + shapeX, gridPointY + shapeY
--If a point in the shape is outside the grid, then it doesn't fit
if gridX < 1 or gridY < 1 or gridX > #grid[1] or gridY > #grid then
return false
end
--0s in the grid have no effect on whether the shape fits or not
if grid[gridY][gridX] == SLOT_EMPTY then continue end
--Neither do 0s in the shape
if shape[y][x] == SLOT_EMPTY then continue end
--If both the shape and the grid have 1s at this point, there's an overlap so shape doesn't fit here
return false
end
end
--None of the 1s in the shape overlapped with 1s in the grid, so the shape fits
return true
end
function getPointWhereShapeFitsInGrid(shape, grid)
for gridX = 1, #shape[1] do
for gridY = 1, #shape do
if shapeFitsInGridAtPoint(shape, grid, gridX, gridY) then
return {X = gridX, Y = gridY}
end
end
end
return nil
end
y and x here is not defined in the rest of the script, am I right in assuming y and x here is just shapeX and shapeY?
Also, is this line only necessary for tool shapes such as the example below?
Also what exactly should be returned when going through the functions?
This is the shape of an example tool I’m using.
self.ToolShape = {
{1},
{1},
{1},
{1},
}
And this is the result I get printed out.
Not exactly sure what I’m seeing here.
Bumping this post again, any response would be good.
To the best of my knowledge - considering the context which is given - yes, the x and y variables refer to shapeX and shapeY, respectively.
What type of tool shapes are you planning to use? This is intended to be - I believe - used for any tool that can be described using a matrix of zeros and ones. It can support any dimensions of shapes, whether if it’s a column or row of 1s, or if something more complex. This way, you can add any new items in the case of, say, an update. If this is not what you want, you can optimize @ThanksRoBama’s solution to your specific shapes of inventories.
The return value is essentially the first available point in which the shape can be fit into the grid. That is, we can place the shape (starting from the top left corner) into the returned coordinates and it will not cause any problems with fitting.
If you want a list of points where you can place your shape, for example, you can append any matching points into an array and return that array containing your results.
function getPointWhereShapeFitsInGrid(shape, grid)
local points = {}
for gridX = 1, #shape[1] do
for gridY = 1, #shape do
if shapeFitsInGridAtPoint(shape, grid, gridX, gridY) then
table.insert(points, {X = gridX, Y = gridY})
end
end
end
return points
end
-- Unittesting
local urShape = ur.Shape
local urGrid = ur.Grid
for _, coordinate in ipairs(getPointWhereShapeFitsInGrid(urShape, urGrid)) do
print(coordinate.X, ",", coordinate.Y)
end
local InvenTable =
{
{1, 0, 0, 0},
{0, 0, 0, 0},
{0, 0, 0, 0},
{0, 0, 0, 0},
}
self.ToolShape = {
{1},
{1},
{1},
{1},
}
I tried testing the code out with the first top left spot being already occupied.
This returns no results. How would I go about checking the next spot available going from left to right?
Can I see your implementation? I haven’t tested the code that was posted, so I’ll try doing some testing and get back to you.
local InvenTable =
{
{1, 0, 0, 0, 0},
{0, 0, 0, 0, 0},
{0, 0, 0, 0, 0},
{0, 0, 0, 0, 0},
}
local InventoryUI = {}
InventoryUI.__index = InventoryUI
function InventoryUI.new(tool)
local self = setmetatable({}, InventoryUI)
self.Tool = tool
self.ToolShape = {
{1},
{1},
{1},
{1},
}
return self
end
function InventoryUI:CreateFrame() -- Create frame for the item
self:FindAvailableSpace()
end
function InventoryUI:ItemFitsIndividualPoint(gridPointX, gridPointY)
for ToolShapeX = 1, #self.ToolShape[1] do
for ToolShapeY = 1, #self.ToolShape do
local gridX, gridY = gridPointX + ToolShapeX, gridPointY + ToolShapeY
--If a point in the shape is outside the grid, then it doesn't fit
if gridX < 1 or gridY< 1 or gridX > #InvenTable[1] or gridY > #InvenTable then
return false
end
--0s in the grid have no effect on whether the shape fits or not
if InvenTable[gridY][gridX] == 0 then continue end
--Neither do 0s in the shape
if self.ToolShape[ToolShapeY][ToolShapeX] == 0 then continue end
--If both the shape and the grid have 1s at this point, there's an overlap so shape doesn't fit here
return false
end
end
--None of the 1s in the shape overlapped with 1s in the grid, so the shape fits
return true
end
function InventoryUI:FindAvailableSpace()
for gridX = 1, #self.ToolShape[1] do -- gridX is the first row
for gridY = 1, #self.ToolShape do -- Columns across the first row going down
if self:ItemFitsIndividualPoint(gridX, gridY) then
print(gridX, gridY)
end
end
end
end
return InventoryUI
I modified the original implementation, accounting for edge cases. Try the following:
local SLOT_EMPTY = 0
local SLOT_FILLED = 1
function shapeFitsInGridAtPoint(shape, grid, gridPointX, gridPointY)
--Check that every 1 in the shape fits inside the grid and doesn't overlap with 1s, if the shape were to be placed with its upper left point at (gridPointX, gridPointY).
for shapeX = 1, #shape[1] do
for shapeY = 1, #shape do
local gridX, gridY = gridPointX + shapeX - 1, gridPointY + shapeY - 1
--0s in the grid have no effect on whether the shape fits or not
if grid[gridY][gridX] == SLOT_EMPTY then continue end
--Neither do 0s in the shape
if shape[shapeY][shapeX] == SLOT_EMPTY then continue end
--If both the shape and the grid have 1s at this point, there's an overlap so shape doesn't fit here
return false
end
end
--None of the 1s in the shape overlapped with 1s in the grid, so the shape fits
return true
end
function getPointWhereShapeFitsInGrid(shape, grid)
local points = {}
for gridX = 1, #grid[1]-#shape[1]+1 do
for gridY = 1, #grid-#shape+1 do
if shapeFitsInGridAtPoint(shape, grid, gridX, gridY) then
table.insert(points, {X = gridX, Y = gridY})
end
end
end
return points
end
This is the result printed out from the points table.
I just wanted to confirm the keys in the points table are the individual points from the tool shape and the values are the co-ords from the inventory matrix?
Also:
I meant tool shapes as in shapes like a scythe in this example:
Would that require a rectangular shape with 0’s in the tool shape matrix, or can it be written as:
local scytheInvShape = {
{ 1, 1, 1 },
{ 1 },
{ 1 },
{ 1 },
}
I’m assuming not, but I’m afraid of situations where there’s already an item in the player’s inventory, and to fit the scythe into the player inventory, the code wouldn’t work due to the 0’s in the item’s shape.
The index simply is an address that points to a coordinate in which the shape can fit into the grid - it is just an arbitrary number. The value, as you say, is the respective coordinate from the grid matrix (keep in mind that the literal starting point that the coordinate is referring would be grid[coordinate.Y][coordinate.X]
.
The shape does need to be rectangular, which is why we include zeros in the areas where the shape does NOT take up space. In other words, it would, in a graphical sense, be identical to:
local scytheInvShape = {
{ 1, 1, 1 },
{ 1 },
{ 1 },
{ 1 },
}
If you really wanted to use a shape like the above, you can simply check if the element that is indexed in the shape is nil
(in the shapeFitsInGridAtPoint
function), and then break out of the inner loop. In my opinion, this type of structure is superfluous, and it is better include the zeros for the sake of clarity and simplicity.
However, when running the code again with the same inventory table and a tool shape of:
self.ToolShape = {
{1, 1, 1},
{1, 0, 0},
{1, 0, 0},
{1, 0, 0},
}
The resulting points table prints out only two indexes?
I think this is expected? Based on the code you have given previously, (2, 1) and (3, 1) seem to be the only valid coordinates. I am not sure how the previous result outputted more coordinates. Can you run more tests and get back to me?
The previous result of
local scytheInvShape = {
{ 1, 1, 1 },
{ 1 },
{ 1 },
{ 1 },
}
did not print out any more results, in fact, it was the same results as the ones including the 0’s in the tool shape.
But this only gives coordinates to two points in the tool shape, how is this to be expected? Shouldn’t it print out co-ords for all of the points in the tool shape?
Also adding onto this, I tested more item shapes that contained more than one value horizontally.
self.ToolShape = {
{1, 1},
}
https://gyazo.com/ea6a5e194b1ad22bd9a16ac462881d4e
The code appears to not work in this scenario.
The coordinates is not related to the contents of the shape, but rather the point (which would be the top left corner of the shape) where the shape can legally fill the grid.
Note that the results here would be displayed in the output as {X = 1, Y = 1} and {X = 2, Y = 1}, respectively.
- In that case, how would I change the values of the inventory matrix so that the shape is placed in it?
- I still don’t see why
self.ToolShape = {
{1, 1},
}
would print out this result, could you explain?
You would iterate starting from the coordinate that you obtained to the bottom right of the shape, iterating through the elements (0s and 1s) of the shape.
local coord = {X = something, Y = somethingElse}
for i = 1, #shape do
for j = 1, #shape[i] do
grid[coord.Y+i-1][coord.X+j-1] = shape[i][j]
end
end
Admittedly, this will take a bit to write out as a diagram. Essentially, picture your 4x5 inventory table, and using the coordinate system in the diagram I’ve shown above, circle all the 1x2 group of 0s (all the groups that can be filled with your item) in that diagram. Write down every top left coordinate of each group, what you should end up with is an array of coordinates whose elements are equal to the elements shown in your output.
This works great, however, for item shapes such as
self.ToolShape = {
{1, 1},
}
How would I prevent it from filling up the entire inventory matrix?
Edit: Scratch that, I’ve fixed it. If anyone wants to know the solution just ask as it needs a bit of explaining.
Yeah, my mistake sorry Just replying in case someone in the future wants to know for sure.