Hello, I am new to the developer forum. If I make any mistake, please tell me.
So I have this frame made of cubes. Once I complete the frame, how can I find the empty spaces in the frame and fill it in with more cubes like the cube farming mechanic in Antichamber?
The position of the cubes are stored in a 3D array.
Here is an example of what I am trying to make:
Incomplete frame
Thank for your reply. This seems like a really good solution. But there is one more problem. How can I find the position to start the flooding? Because I want it to start filling in immediately after the frame is completed.
Btw here is the code that manages cube data storage
-- The table that contains the cubes
CubeList = {}
-- Returns the cubes at the specified position, or nil if it does not exist
function Data.Cube.Get(x:number, y:number, z:number):{Part}
local CubeX = CubeList[x]
if CubeX then
local CubeY = CubeX[y]
if CubeY then
local CubeZ = CubeY[z]
if CubeZ then
return CubeZ
else
return nil
end
else
return nil
end
else
return nil
end
end
-- Set the position in the table with a newly created cube
function Data.Cube.Set(cube:Part, x:number, y:number, z:number)
local CubeX = CubeList[x]
if not CubeX then
local NewX = {}
CubeList[x] = NewX
CubeX = NewX
end
local CubeY = CubeX[y]
if not CubeY then
local NewY = {}
CubeX[y] = NewY
CubeY = NewY
end
local CubeZ = CubeY[z]
if not CubeZ then
local NewZ = {}
CubeY[z] = NewZ
CubeZ = NewZ
end
table.insert(CubeZ, cube)
end
-- Remove the cubes at the specified position from the table
function Data.Cube.Remove(x:number, y:number, z:number)
local CubeX = CubeList[x]
if CubeX then
local CubeY = CubeX[y]
if CubeY then
local CubeZ = CubeY[z]
if CubeZ then
CubeY[z] = nil
-- GetNumberOf() is a function that checks the number of objects in the table.
-- I'm not using # to get the number of values in the table because it's technically a dictionary
if GetNumberOf(CubeY) == 0 then
CubeX[y] = nil
if GetNumberOf(CubeX) == 0 then
CubeList[x] = nil
end
end
end
end
end
end
Because the cube can exist anywhere in the world, there is no size limit to the grid. The shapes can be as big as the player want to make them, but I will probably add a limit to it later so big shapes wouldn’t lag the game.
To define flat shapes on a 3D grid, I can focus on only two axis. For example, the X and Z axis and ignoring the Y axis for shapes made on the floor.
It’s kind of hard to explain a shape being “closed”, but I’ll try my best.
A “closed” shape is a shape where all the corners filled in. Every cube that makes up the frame must have at lease 2 cubes next to it in the up, down, left, right direction. Diagonals does not count.
Find an “outside cube” to start with. Possible ways:
Pick any empty spot on the outside of the placement area, or
start at a spot far away from the placed cube that’s guaranteed to be empty, and march inwards until you hit the cube you just placed, then back up one to the last empty cube.
From that “outside cube”, run a flood fill to mark all the “not inside a shape” cubes with some marker (not a cube, mind you, just a little tag that tells you they’re outside)
Any cubes that you didn’t just tag, and don’t already have a cube in them, put a cube in.
The biggest issue is maybe step 3. If your placement area is super large, that might take a while. It would have to be pretty massive though, and you can always stop iterations at a certain point to get “good enough”.
I can come up with an example implementation if you like.
Edit: Thinking about this more… Step (2) might need to be modified because your first “outer cubes” flood fill might miss cubes on the outside that are partially walled off by interior cubes. So your flood fill algo might have to start with all the open border cubes in the queue.
In step 2, I changed the way to find the “outside cube”. Once a new cube is placed, instead of picking a random spot far away and marching in, I first find all the connecting cubes. Then find its bounding box by comparing each cube’s position to see which one is the highest/lowest on each axis. After that, I created a “barrier” that is one stud bigger than the shape bounding box. The “outside cube” will be located in the bottom left corner of the barrier.
This will ensure that the “outside cube” will always be empty and all missing spaces outside of the frame is covered.
This also helps with lag in step 3, as it does not have to iterate a huge area.
During the flood fill in step 3, all cubes that are not part of the main structure is ignored and tagged to prevent interference.
Lastly, I checked for empty spaces that didn’t get tagged and filled it in with cubes like you said.