0

I am trying to generate a maze with rooms and I am using this guide

This is what I am trying to achieve This is what I am trying to achieve. Instead I am getting this:

What I am achieving

This is what I have done so far (with unnecessary parts taken out)

    using UnityEngine;
using Random = UnityEngine.Random;
using UnityEngine.Tilemaps;

using System.Collections.Generic;
using System.Linq;

public class MazeAndRoomGenerator : MonoBehaviour
{
    TileType[,] levelWall;
    int[,] regions;
    int currentRegion = -1;
    enum TileType { Floor, Wall }


    // CONSTANTS
    readonly static int[] north = { 0, 1 };
    readonly static int[] south = { 0, -1 };
    readonly static int[] east = { 1, 0 };
    readonly static int[] west = { -1, 0 };
    readonly static int[] northEast = { 1, 1 };
    readonly static int[] northWest = { -1, 1 };
    readonly static int[] southEast = { 1, -1 };
    readonly static int[] southWest = { -1, -1 };
    readonly static int[][] northCells = { north, northEast, northWest };
    readonly static int[][] southCells = { south, southEast, southWest };
    readonly static int[][] eastCells = { east, northEast, southEast };
    readonly static int[][] westCells = { west, northWest, southWest };



    public void GenerateMaze()
    {
        // Loop through all cells in the level and grow the maze in all parts that aren't assigned yet
        for (int y = 0; y < mapSize; y += 2)
        {
            for (int x = 0; x < mapSize; x += 2)
            {
                if(levelWall[x,y] == TileType.Wall)
                {
                    GrowMaze(x, y);
                }
            }
        }
    }





    public void Carve(int x, int y)
    {
        levelWall[x,y] = TileType.Floor;
        regions[x, y] = currentRegion;
    }
    public bool CanCarve(int[] pos, int[] dir)
    {
        // Returns false if the cell is already taken or out of map bounds
        int x = pos[0] + dir[0] * 2;
        int y = pos[1] + dir[1] * 2;
        if (!InBounds(x, y)) { return false; }


        int[][] checkCells = null;
        if      (dir == north) { checkCells = northCells; }
        else if (dir == south) { checkCells = southCells; }
        else if (dir == east)  { checkCells = eastCells; }
        else if (dir == west)  { checkCells = westCells; }
        else { Debug.LogError("Incorrect direction inputted"); }

        foreach (int[] checkCell in checkCells)
        {
            int[] cell = { pos[0] + checkCell[0], pos[1] + checkCell[1] };
            if (CanCarve(cell))
            {
                return false;
            }
        }

        // All of the surrounding walls are available so return true
        return true;
    }
    public bool CanCarve(int[] pos)
    {
        // Returns false if the cell is already taken or out of map bounds

        int x = pos[0];
        int y = pos[1];

        // Checking if map is out of bounds
        if (!InBounds(x, y)) 
        { 
            return false; 
        }


        // return True if the cell is a wall (1)
        // false if the cell is a floor (0)
        return (levelWall[x, y] == TileType.Wall);
    }
    public bool InBounds(int x, int y)
    {
        // Checking if map is out of bounds
        if (!(0 < x) || !(x < mapSize) ||
            !(0 < y) || !(y < mapSize))
        {
            return false;
        }
        else return true;
    }
    public void GrowMaze(int startX, int startY)
    {
        /*
         * RULES: 
         *  If any of the neighbour cells to start point (CanCarve == false) are floor then stop.
         *  Take a random available direction and start carving.
         *  For each cell that is carved first check if the cell in front of it (travelling in the same direction)
         *  and the cells to the left and right of the cell is carvable. 
         *  If isn't then remove that direction from available directions and pick new direction from original cell.
         *  Repeat until no available directions left
         */


        int[][] directions = { north, south, east, west };
        int[][] neighbourCells = { north, south, east, west, northEast, northWest, southEast, southWest };

        int[] start = { startX, startY };
        List<int[]> cells = new List<int[]>();
        int[] lastDirection = null;

        // Check if starting point is valid
        foreach (int[] direction in neighbourCells)
        {
            int[] checkCell = { start[0] + direction[0], start[1] + direction[1] };
            if (!CanCarve(checkCell))
            {
                // Throw out start cell and don't start maze from there
                return;
            }
        }

        // Start a new region for the new maze region
        StartRegion();
        Carve(start[0], start[1]);
        cells.Add(start);


        // While there are available cells to travel to run script
        while (cells.Count > 0 && cells.Count < 10000)
        {
            int[] cell = cells[cells.Count - 1];

            List<int[]> unmadeCells = new List<int[]>();

            foreach (int[] direction in directions)
            {
                int[] checkCell = { cell[0] + direction[0], cell[1] + direction[1] };
                if (CanCarve(checkCell, direction))
                {
                    unmadeCells.Add(direction);
                }
            }

            // If there are available cells to travel to run script
            if (unmadeCells.Count > 0)
            {
                // Prefer to continue in the last direction travelling if available
                // Random chance for it to choose a different direction
                int[] direction;

                if (unmadeCells.Contains(lastDirection) 
                    && (Random.value > (windingChance/100)) )
                {
                    direction = lastDirection;
                }
                else
                {
                    direction = unmadeCells[Random.Range(0, unmadeCells.Count)];
                }

                int[] newCell;
                newCell = new int[] { cell[0] + direction[0], cell[1] + direction[1] };
                Carve(newCell[0], newCell[1]);
                // Adds new cell onto stack and script will repeat with this cell until it has no possible directions to travel
                cells.Add(newCell);

                lastDirection = direction;
            }
            else
            {
                cells.RemoveAt(cells.Count - 1);
                lastDirection = null;
            }
        }
    }


}

I have an idea that it is something to do with that the cells array keeps increasing and is therefore stuck in a loop which is why I have added a restriction to the amount of cells in the while loop for debugging.

My rules for the maze are:

  • If any of the neighbour cells to start point (CanCarve == false) are floor then stop.
  • Take a random available direction and start carving.
  • For each cell that is carved first check if the cell in front of it (travelling in the same direction) and the cells to the left and right of the cell is carvable. If isn't then remove that direction from available directions and pick new direction from original cell.
  • Repeat until no available directions left

I would really appreciate any help. I've been ripping my hair out over this :)

JudasMoses
  • 378
  • 4
  • 22
  • Great question. uhh... I'm pretty sure it was to do with how the original algorithm was working before I changed it. Just fixed it doesn't solve my problem unfortunately :( – JudasMoses Apr 10 '20 at 18:31
  • I looked at the source code and I can't see the "_connectRegions();" and "_removeDeadEnds();" methods in your code, did you miss them? – Gabriel Alexandre Apr 10 '20 at 18:37
  • Not up to that part of the source code yet. Those functions don't influence what I am doing yet. – JudasMoses Apr 10 '20 at 18:38
  • fill(Tiles.wall) ? is there some code missing, cant see initializations and are all those floor squares de problem here? – Gabriel Alexandre Apr 10 '20 at 18:41
  • That is literally not in my code. I realise you are talking the source code I linked. It is initializing each element in the level array to be a Tiles.wall I assume. That's not my code. – JudasMoses Apr 10 '20 at 18:45

2 Answers2

0

You are incrasing the x and y by 2 each itteration, so of course you get a checkered pattern.

Generally you should limit for loops to ArrayYouAreWorkingWith.Lenght. Use MapSize when you initialize the array - that way the loops are future proof. Persdonally I realy dislike using 2-dimensional arrays, as it is hard to figure out the bounds of one dimension. I use jagged arrays instead, but the basic still works. But that is more of a preference then a real rule.

Christopher
  • 9,634
  • 2
  • 17
  • 31
  • Thanks for the tip :) I will do that. Unfortunately increasing x and y by 2 each iteration is not the problem. The starting cell is still being carved out (as seen by the checkered pattern) but I am trying to solve why the maze is not continuing from that starting cell. – JudasMoses Apr 10 '20 at 18:35
  • @LiamBoreback Nothing in the original exampel requires you to bulk set array values like that. Aside from initializsation of all cells, I have no idea why you even itterate over the whole array like that. – Christopher Apr 10 '20 at 18:39
  • Are you still talking about the `GenerateMaze` function? If so can you elaborate on what you mean by bulk set of array values? I am calling my maze algorithm on every available cell. – JudasMoses Apr 10 '20 at 18:43
0

The reason why there is a checkered pattern generated is due to the pos cell not being checked in the CanCarve function.

JudasMoses
  • 378
  • 4
  • 22