2
\$\begingroup\$

I have a C# script that creates a random 2D array that is a map for a cave system.

Sample Map:

11111111111111111111111111111111111111111111111111111111111111111111111111111111
11111111111111111111111111111111111111111111111111111111111111111111111100011111
11111111111111111111111001111111111111111110111111111111111111111111110000000111
11111111111111111111110000011111111111111100001111111111111111111111000000000111
11111110000000111111100000011111111111111000000010111111111111110101000000000111
11111100000000011111110000000111111111111000000000011111111111100000000000000111
11111000000000001111100000000101111111111000000000000111111111100000000000000111
11110000000000000111110000000000011111111100000000000111111111110000000000000111
11110000000000000111100000000000000111111111100000000111111111110000000000000111
11100000000000000111110000000000000111111111110000000111111111111000000000000111
11100000000000000111100000000000000011111111110000001111111111100000000000000011
11000000000000000111110000000000000011111111110000000111111111000000000000000011
11000000000000000111100000001000000001111110000000001111111110000000000000000011
11000000000000000001000000111100000001111100000000001111111100000000000000000011
11000000000000000001000000111100000000111000000000000111111100000000000000000011
11000000000000000000000011111110000001111000000000001111000000000000000000000011
11000010000000110000000011111111111011110000000000000110000000000000000000000011
11000011111111111000000011111111111111110000000001001100000000000000000000000011
11100001111111111000000011111111111111110000000011100100000000010100000000000111
11100000011111111100000011111111111111110000000011100000000000111110000000000111
11100000010101111000000000111111111010100000000111111000000000111111000000001111
11100000000000011100000000101111110000000000001111111100000001111111100000011111
11100000000000000111000000000111111000000000000111111110000011111111100000011111
11100000000000011111110000000011111000000000000111111111111111111111000000011111
11000000000000011111111100000111111110000000000011111111111111111110000000011111
11000000000000011111111100000111111110000000000011111111111111110000000000011111
11100000000000001111111110001111111111000000000001111111111111100000000000001111
11100000000000001111111100001111111100000000000001111111111111110000000000000111
11110000000010000011111100001111111100000000000001111111111111110000000000000111
11100000011111000000000000001111110000000000000001111111111111110000000000000111
11111111111111110000000000000111110000000000000001111111111111110000000000000111
11111111111111100000000000000000000000000000000000111111111111110000000000000111
11111111111111110000000000000000000000000000000101111111111111110000000000000111
11111111111111100000000100000000000000000000011101111111111111110000000000000111
11111111111111100000001111000000000000000000011111111111111111111000000000001111
11111111111111100000011111000000000000000000011111111111111111111100000000001111
11111111111111111000011111100000000000000000111111111111111111111110000000000111
11111111111111111110111111110001011111111111111111111111111111111111111110000111
11111111111111111111111111111111111111111111111111111111111111111111111111111111
11111111111111111111111111111111111111111111111111111111111111111111111111111111

It uses 0 for the passages and 1 for the walls.

I want to turn it into a 3D shape for the player to walk through.
I got something like it working in Unity, but I'd really like to port it to Godot.

This is what the cave looked like in Unity:

enter image description here

I had someone recommend using CSG shapes to create it, like a CSGPolygon3D, which can be used to subtract from a larger shape and carve the cave out.

I can see it having a lot of advantages that I didn't know how to solve with the unity solution, though I've also heard that CSG shapes should only be used for prototyping.

The big problem I'm running into is finding a way to turn my 2D array into a Polygon3D. I've tried a few different things but none of them seem to work out.

\$\endgroup\$
2
  • \$\begingroup\$ It is not hard to convert the CSG once it is done. Anyway, you probably want marching squares or similar algorithm. You can search marching squares on this site. \$\endgroup\$
    – Theraot
    Mar 17 at 6:16
  • \$\begingroup\$ @Theraot I've been looking into Marching Squares, and it does look really promising except that all of the examples I've found seem to use sprites for the 16 map tile shapes. I've been looking for a way to do it with Vector2s, but so far I haven't had much luck. \$\endgroup\$
    – AndyD273
    Mar 17 at 6:20

2 Answers 2

1
\$\begingroup\$

I finally got some success!

I got the clue from this reddit post:
https://www.reddit.com/r/godot/comments/lc6cxz/merge_a_grid_of_polygons_into_one_polygon/

This might not be the final form, and it can probably be more efficient, but at least it does what I want it to.

void GridRun() {
    FillPolygoneArray(); // create a list of Vector2[] to plug into the grid. Right now it's just (0,0), (0,1), (1,1), (1,0)

    List<Vector2> gridPoly;
    List<List<Vector2>> grid = new List<List<Vector2>>(); // the pieces of the map to be merged

    for(int gridX = 0; gridX < width-1; gridX++){ // run the 2D array X
        for(int gridY = 0; gridY < height-1; gridY++){ // run the 2D array Y
            // if the x,y is a passage then pull out a polygon, scale it up, put it into grid
            if(map[gridX,gridY] == 0){ 
                gridPoly = new List<Vector2>();
                foreach(Coord co in polygonTypes[0]){
                    float newX = (co.tileX + gridX) * scale;
                    float newY = (co.tileY + gridY) * scale;
                    gridPoly.Add(new Vector2(){ X = newX, Y = newY });
                }
                grid.Add(gridPoly);
            }
        }
    }

    // pull the first piece to be merged    
    Vector2[] mapgrid = grid[0].ToArray();
    Godot.Collections.Array<Vector2[]> merged;
    Vector2[] cell;
    Queue<Vector2[]> OtherGrid = new Queue<Vector2[]>();

    // first pass of the grid, trying to merge as much as possible
    for (int i = 1; i < grid.Count; i++) {
        cell = grid[i].ToArray();
        // if MergePolygons can't merge the two grid pieces because they aren't touching then it will return both. Push the second into a queue for the second pass
        merged = Geometry2D.MergePolygons(mapgrid, cell);
        mapgrid = merged[0];
        if (merged.Count > 1)
        {
            OtherGrid.Enqueue(merged[1]);
        }
    }
    
    //second pass, take all the pieces that couldn't be merged before and try again until everything fits.
    while (OtherGrid.Count > 0)
    {
        cell = OtherGrid.Dequeue();
        merged = Geometry2D.MergePolygons(mapgrid, cell);
        mapgrid = merged[0];
        if (merged.Count > 1)
        {
            OtherGrid.Enqueue(merged[1]);
        }
    }
    // put the finished polygon map into the CsgPolygon3d
    caveSys.Polygon = mapgrid;
    caveSys.Depth = depth;
}
\$\endgroup\$
0
\$\begingroup\$

The easiest way (but not very performant) is to create an Array of "x*z" cubes (or hexagons like in your picture) and disable all cubes if map value is 0.

Or you can convert your Map into .png and generate terrain based on Heightmap.

You can create a custom mesh builder. And generate its shape based on your input.

Or use any asset that allows you to generate a cave. It will not be hard to generate a cave from code based on your Map.

Maybe there is other similar options.

\$\endgroup\$

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .