Search Unity

  1. Megacity Metro Demo now available. Download now.
    Dismiss Notice
  2. Unity support for visionOS is now available. Learn more in our blog post.
    Dismiss Notice

Quick maze generator

Discussion in 'Scripting' started by tombali, Mar 9, 2013.

  1. tombali

    tombali

    Joined:
    Jun 7, 2012
    Posts:
    132
    Hello,

    here's code for quick maze generator. It simply modification of this code. With it, you can create mazes that look like this...





    Just attach the script to some object...

    Code (csharp):
    1. using UnityEngine;
    2. using System;
    3. using System.Collections;
    4. using System.Collections.Generic;
    5.  
    6. public class MazeGenerator : MonoBehaviour {
    7.     public int width, height;
    8.     public Material brick;
    9.     private int[,] Maze;
    10.     private List<Vector3> pathMazes = new List<Vector3>();
    11.     private Stack<Vector2> _tiletoTry = new Stack<Vector2>();
    12.     private List<Vector2> offsets = new List<Vector2> { new Vector2(0, 1), new Vector2(0, -1), new Vector2(1, 0), new Vector2(-1, 0) };
    13.     private System.Random rnd = new System.Random();
    14.     private int _width, _height;
    15.     private Vector2 _currentTile;
    16.     public Vector2 CurrentTile
    17.     {
    18.         get { return _currentTile; }
    19.         private set
    20.         {
    21.             if (value.x < 1 || value.x >= this.width - 1 || value.y < 1 || value.y >= this.height - 1)
    22.             {
    23.                 throw new ArgumentException("CurrentTile must be within the one tile border all around the maze");
    24.             }
    25.             if (value.x % 2 == 1 || value.y % 2 == 1)
    26.             { _currentTile = value; }
    27.             else
    28.             {
    29.                 throw new ArgumentException("The current square must not be both on an even X-axis and an even Y-axis, to ensure we can get walls around all tunnels");
    30.             }
    31.         }
    32.     }
    33.  
    34.     private static MazeGenerator instance;
    35.     public static MazeGenerator Instance
    36.     {
    37.         get
    38.         {
    39.             return instance;
    40.         }
    41.     }
    42.  
    43.     void Awake()
    44.     {
    45.         instance = this;
    46.     }
    47.  
    48.     void Start()
    49.     {
    50.         Camera.main.orthographic = true;
    51.         Camera.main.orthographicSize = 30;
    52.         GenerateMaze();
    53.     }
    54.  
    55.     void GenerateMaze()
    56.     {
    57.         Maze = new int[width, height];
    58.         for (int x = 0; x < width; x++)
    59.         {
    60.             for (int y = 0; y < height; y++)
    61.             {
    62.                 Maze[x, y] = 1;
    63.             }
    64.         }
    65.         CurrentTile = Vector2.one;
    66.         _tiletoTry.Push(CurrentTile);
    67.         Maze = CreateMaze();
    68.         GameObject ptype = null;
    69.  
    70.         for (int i = 0; i <= Maze.GetUpperBound(0); i++)
    71.         {
    72.             for (int j = 0; j <= Maze.GetUpperBound(1); j++)
    73.             {
    74.                 if (Maze[i, j] == 1)
    75.                 {
    76.                     ptype = GameObject.CreatePrimitive(PrimitiveType.Cube);
    77.                     ptype.transform.position = new Vector3(i * ptype.transform.localScale.x, j * ptype.transform.localScale.y, 0);
    78.                     if (brick != null)
    79.                     {
    80.                         ptype.renderer.material = brick;
    81.                     }
    82.                     ptype.transform.parent = transform;
    83.                 }
    84.                 else if (Maze[i, j] == 0)
    85.                 {
    86.                     pathMazes.Add(new Vector3(i, j, 0));
    87.                 }
    88.  
    89.             }
    90.         }
    91.     }
    92.  
    93.     public int[,] CreateMaze()
    94.         {
    95.             //local variable to store neighbors to the current square
    96.             //as we work our way through the maze
    97.             List<Vector2> neighbors;
    98.             //as long as there are still tiles to try
    99.             while (_tiletoTry.Count > 0)
    100.             {
    101.                 //excavate the square we are on
    102.                 Maze[(int)CurrentTile.x, (int)CurrentTile.y] = 0;
    103.  
    104.                 //get all valid neighbors for the new tile
    105.                 neighbors = GetValidNeighbors(CurrentTile);
    106.  
    107.                 //if there are any interesting looking neighbors
    108.                 if (neighbors.Count > 0)
    109.                 {
    110.                     //remember this tile, by putting it on the stack
    111.                     _tiletoTry.Push(CurrentTile);
    112.                     //move on to a random of the neighboring tiles
    113.                     CurrentTile = neighbors[rnd.Next(neighbors.Count)];
    114.                 }
    115.                 else
    116.                 {
    117.                     //if there were no neighbors to try, we are at a dead-end
    118.                     //toss this tile out
    119.                     //(thereby returning to a previous tile in the list to check).
    120.                     CurrentTile = _tiletoTry.Pop();
    121.                 }
    122.             }
    123.  
    124.             return Maze;
    125.         }
    126.         /// <summary>
    127.         /// Get all the prospective neighboring tiles
    128.         /// </summary>
    129.         /// <param name="centerTile">The tile to test</param>
    130.         /// <returns>All and any valid neighbors</returns>
    131.         private List<Vector2> GetValidNeighbors(Vector2 centerTile)
    132.         {
    133.  
    134.             List<Vector2> validNeighbors = new List<Vector2>();
    135.  
    136.             //Check all four directions around the tile
    137.             foreach (var offset in offsets)
    138.             {
    139.                 //find the neighbor's position
    140.                 Vector2 toCheck = new Vector2(centerTile.x + offset.x, centerTile.y + offset.y);
    141.  
    142.                 //make sure the tile is not on both an even X-axis and an even Y-axis
    143.                 //to ensure we can get walls around all tunnels
    144.                 if (toCheck.x % 2 == 1 || toCheck.y % 2 == 1)
    145.                 {
    146.                     //if the potential neighbor is unexcavated (==1)
    147.                     //and still has three walls intact (new territory)
    148.                     if (Maze[(int)toCheck.x, (int)toCheck.y] == 1  HasThreeWallsIntact(toCheck))
    149.                     {
    150.                         //add the neighbor
    151.                         validNeighbors.Add(toCheck);
    152.                     }
    153.                 }
    154.             }
    155.  
    156.             return validNeighbors;
    157.         }
    158.  
    159.  
    160.         /// <summary>
    161.         /// Counts the number of intact walls around a tile
    162.         /// </summary>
    163.         /// <param name="Vector2ToCheck">The coordinates of the tile to check</param>
    164.         /// <returns>Whether there are three intact walls (the tile has not been dug into earlier.</returns>
    165.         private bool HasThreeWallsIntact(Vector2 Vector2ToCheck)
    166.         {
    167.             int intactWallCounter = 0;
    168.  
    169.             //Check all four directions around the tile
    170.             foreach (var offset in offsets)
    171.             {
    172.                 //find the neighbor's position
    173.                 Vector2 neighborToCheck = new Vector2(Vector2ToCheck.x + offset.x, Vector2ToCheck.y + offset.y);
    174.  
    175.                 //make sure it is inside the maze, and it hasn't been dug out yet
    176.                 if (IsInside(neighborToCheck)  Maze[(int)neighborToCheck.x, (int)neighborToCheck.y] == 1)
    177.                 {
    178.                     intactWallCounter++;
    179.                 }
    180.             }
    181.  
    182.             //tell whether three walls are intact
    183.             return intactWallCounter == 3;
    184.  
    185.         }
    186.  
    187.         private bool IsInside(Vector2 p)
    188.         {
    189.             return p.x >= 0  p.y >= 0  p.x < width  p.y < height;
    190.         }
    191. }
    192.  
     
    Last edited: Mar 9, 2013
  2. Amon

    Amon

    Joined:
    Oct 18, 2009
    Posts:
    1,384
    Cool! :)
     
  3. ptdnet

    ptdnet

    Joined:
    Apr 20, 2011
    Posts:
    100
    I'd modify it to remove the top row and right column. Those saw-edge lines look silly.
     
  4. tombali

    tombali

    Joined:
    Jun 7, 2012
    Posts:
    132
    Well, can we see the changes?
     
  5. ibyte

    ibyte

    Joined:
    Aug 14, 2009
    Posts:
    1,047
    Hi Tombali,

    I just purchased your crossword game from the Asset store. Thanks it will go together well with the other kids games I am putting together.

    Thank as well for sharing your maze code. I am looking to make some changes to the maze generation code so that it will

    a) have multiple entry/exits and b) connect a few paths so there are not to many long routes.

    I would also like to remove the ridges if possible.

    Are you able to help with this?

    iByte
     
  6. joaobsneto

    joaobsneto

    Joined:
    Dec 10, 2009
    Posts:
    152
    That will save me a lot of time. Thank you! Why did you use System.Random and not Unity Random class?
     
  7. Kount_Kancer

    Kount_Kancer

    Joined:
    Oct 7, 2013
    Posts:
    1
    Great script, I was wondering is it possible to change the game object to that it uses to a predefined prefab object.
     
  8. softwizz

    softwizz

    Joined:
    Mar 12, 2011
    Posts:
    793
    Indeed it is, just put your prefab in the 'wall' slot in the inspector:

    Code (csharp):
    1.     using UnityEngine;
    2.     using System;
    3.     using System.Collections;
    4.     using System.Collections.Generic;
    5.      
    6.     public class MazeGenerator : MonoBehaviour {
    7.                 public GameObject wall;
    8.         public int width, height;
    9.         private int[,] Maze;
    10.         private List<Vector3> pathMazes = new List<Vector3>();
    11.         private Stack<Vector2> _tiletoTry = new Stack<Vector2>();
    12.         private List<Vector2> offsets = new List<Vector2> { new Vector2(0, 1), new Vector2(0, -1), new Vector2(1, 0), new Vector2(-1, 0) };
    13.         private System.Random rnd = new System.Random();
    14.         private int _width, _height;
    15.         private Vector2 _currentTile;
    16.         public Vector2 CurrentTile
    17.         {
    18.             get { return _currentTile; }
    19.             private set
    20.             {
    21.                 if (value.x < 1 || value.x >= this.width - 1 || value.y < 1 || value.y >= this.height - 1)
    22.                 {
    23.                     throw new ArgumentException("CurrentTile must be within the one tile border all around the maze");
    24.                 }
    25.                 if (value.x % 2 == 1 || value.y % 2 == 1)
    26.                 { _currentTile = value; }
    27.                 else
    28.                 {
    29.                     throw new ArgumentException("The current square must not be both on an even X-axis and an even Y-axis, to ensure we can get walls around all tunnels");
    30.                 }
    31.             }
    32.         }
    33.      
    34.         private static MazeGenerator instance;
    35.         public static MazeGenerator Instance
    36.         {
    37.             get
    38.             {
    39.                 return instance;
    40.             }
    41.         }
    42.      
    43.         void Awake()
    44.         {
    45.             instance = this;
    46.         }
    47.      
    48.         void Start()
    49.         {
    50.             Camera.main.orthographic = true;
    51.             Camera.main.orthographicSize = 30;
    52.             GenerateMaze();
    53.         }
    54.      
    55.         void GenerateMaze()
    56.         {
    57.             Maze = new int[width, height];
    58.             for (int x = 0; x < width; x++)
    59.             {
    60.                 for (int y = 0; y < height; y++)
    61.                 {
    62.                     Maze[x, y] = 1;
    63.                 }
    64.             }
    65.             CurrentTile = Vector2.one;
    66.             _tiletoTry.Push(CurrentTile);
    67.             Maze = CreateMaze();
    68.             GameObject ptype = null;
    69.      
    70.             for (int i = 0; i <= Maze.GetUpperBound(0); i++)
    71.             {
    72.                 for (int j = 0; j <= Maze.GetUpperBound(1); j++)
    73.                 {
    74.                     if (Maze[i, j] == 1)
    75.                     {
    76.                         ptype = wall;
    77.                                                 Instantiate(wall, new Vector3(i * ptype.transform.localScale.x, j * ptype.transform.localScale.y, 0), Quaternion.identity);
    78.                                         }
    79.                     else if (Maze[i, j] == 0)
    80.                     {
    81.                         pathMazes.Add(new Vector3(i, j, 0));
    82.                     }
    83.      
    84.                 }
    85.             }
    86.         }
    87.      
    88.         public int[,] CreateMaze()
    89.             {
    90.                 //local variable to store neighbors to the current square
    91.                 //as we work our way through the maze
    92.                 List<Vector2> neighbors;
    93.                 //as long as there are still tiles to try
    94.                 while (_tiletoTry.Count > 0)
    95.                 {
    96.                     //excavate the square we are on
    97.                     Maze[(int)CurrentTile.x, (int)CurrentTile.y] = 0;
    98.      
    99.                     //get all valid neighbors for the new tile
    100.                     neighbors = GetValidNeighbors(CurrentTile);
    101.      
    102.                     //if there are any interesting looking neighbors
    103.                     if (neighbors.Count > 0)
    104.                     {
    105.                         //remember this tile, by putting it on the stack
    106.                         _tiletoTry.Push(CurrentTile);
    107.                         //move on to a random of the neighboring tiles
    108.                         CurrentTile = neighbors[rnd.Next(neighbors.Count)];
    109.                     }
    110.                     else
    111.                     {
    112.                         //if there were no neighbors to try, we are at a dead-end
    113.                         //toss this tile out
    114.                         //(thereby returning to a previous tile in the list to check).
    115.                         CurrentTile = _tiletoTry.Pop();
    116.                     }
    117.                 }
    118.      
    119.                 return Maze;
    120.             }
    121.             /// <summary>
    122.             /// Get all the prospective neighboring tiles
    123.             /// </summary>
    124.             /// <param name="centerTile">The tile to test</param>
    125.             /// <returns>All and any valid neighbors</returns>
    126.             private List<Vector2> GetValidNeighbors(Vector2 centerTile)
    127.             {
    128.      
    129.                 List<Vector2> validNeighbors = new List<Vector2>();
    130.      
    131.                 //Check all four directions around the tile
    132.                 foreach (var offset in offsets)
    133.                 {
    134.                     //find the neighbor's position
    135.                     Vector2 toCheck = new Vector2(centerTile.x + offset.x, centerTile.y + offset.y);
    136.      
    137.                     //make sure the tile is not on both an even X-axis and an even Y-axis
    138.                     //to ensure we can get walls around all tunnels
    139.                     if (toCheck.x % 2 == 1 || toCheck.y % 2 == 1)
    140.                     {
    141.                         //if the potential neighbor is unexcavated (==1)
    142.                         //and still has three walls intact (new territory)
    143.                         if (Maze[(int)toCheck.x, (int)toCheck.y] == 1  HasThreeWallsIntact(toCheck))
    144.                         {
    145.                             //add the neighbor
    146.                             validNeighbors.Add(toCheck);
    147.                         }
    148.                     }
    149.                 }
    150.      
    151.                 return validNeighbors;
    152.             }
    153.      
    154.      
    155.             /// <summary>
    156.             /// Counts the number of intact walls around a tile
    157.             /// </summary>
    158.             /// <param name="Vector2ToCheck">The coordinates of the tile to check</param>
    159.             /// <returns>Whether there are three intact walls (the tile has not been dug into earlier.</returns>
    160.             private bool HasThreeWallsIntact(Vector2 Vector2ToCheck)
    161.             {
    162.                 int intactWallCounter = 0;
    163.      
    164.                 //Check all four directions around the tile
    165.                 foreach (var offset in offsets)
    166.                 {
    167.                     //find the neighbor's position
    168.                     Vector2 neighborToCheck = new Vector2(Vector2ToCheck.x + offset.x, Vector2ToCheck.y + offset.y);
    169.      
    170.                     //make sure it is inside the maze, and it hasn't been dug out yet
    171.                     if (IsInside(neighborToCheck)  Maze[(int)neighborToCheck.x, (int)neighborToCheck.y] == 1)
    172.                     {
    173.                         intactWallCounter++;
    174.                     }
    175.                 }
    176.      
    177.                 //tell whether three walls are intact
    178.                 return intactWallCounter == 3;
    179.      
    180.             }
    181.      
    182.             private bool IsInside(Vector2 p)
    183.             {
    184.                 return p.x >= 0  p.y >= 0  p.x < width  p.y < height;
    185.             }
    186.     }
    187.  
     
    setKAIN and Gunging like this.
  9. booyu

    booyu

    Joined:
    Jan 22, 2014
    Posts:
    1
    Hi, I attached this script to a cylinder and it doesn't work.

    ArgumentException: CurrentTile must be within the one tile border all around the maze
    MazeGenerator.set_CurrentTile (Vector2 value) (at Assets/MazeGenerator.cs:45)
    MazeGenerator.GenerateMaze () (at Assets/MazeGenerator.cs:129)
    MazeGenerator.Start () (at Assets/MazeGenerator.cs:103)


    What kind of object do I need to attach to? Thanks.
     
  10. raiden

    raiden

    Joined:
    Feb 8, 2009
    Posts:
    333
    booyu, I had no issues, I attached the script to an empty game object (zeroed out the position) and set the width/height in the inspector, put the material in the slot, pressed play, and like magic, a maze appeared.

    Thanks for the code tombali.

    -Raiden
     
  11. Raven_Gothchild

    Raven_Gothchild

    Joined:
    Mar 20, 2014
    Posts:
    10
    I can't get this to work at all, I created a new project, placed a cube and added the script to it, these compiler errors appear:

    Assets/MazeGenerator.cs(143,97): error CS1525: Unexpected symbol `HasThreeWallsIntact'
    Assets/MazeGenerator.cs(151,22): error CS1519: Unexpected symbol `return' in class, struct, or interface member declaration
    Assets/MazeGenerator.cs(151,38): error CS1519: Unexpected symbol `;' in class, struct, or interface member declaration
    Assets/MazeGenerator.cs(171,59): error CS1525: Unexpected symbol `Maze'
    Assets/MazeGenerator.cs(160,22): error CS0116: A namespace can only contain types and namespace declarations
    Assets/MazeGenerator.cs(178,22): error CS8025: Parsing error

    What am I doing wrong here?

    Thanks for you help.
     
  12. hpjohn

    hpjohn

    Joined:
    Aug 14, 2012
    Posts:
    2,190
    Brackets missing?
     
  13. Raven_Gothchild

    Raven_Gothchild

    Joined:
    Mar 20, 2014
    Posts:
    10
    I copied and pasted the entire script from this thread, nothing was left out.
     
  14. hpjohn

    hpjohn

    Joined:
    Aug 14, 2012
    Posts:
    2,190
    Lines 143 and 171 in the code above are missing a boolean operator, either && or ||, i dunno
     
  15. Raven_Gothchild

    Raven_Gothchild

    Joined:
    Mar 20, 2014
    Posts:
    10
    Thanks hpjohn, you were right, they should have read:

    Code (CSharp):
    1. if (Maze[(int)toCheck.x, (int)toCheck.y] == 1 && HasThreeWallsIntact(toCheck))
    and

    Code (CSharp):
    1. if (IsInside(neighborToCheck) &&  Maze[(int)neighborToCheck.x, (int)neighborToCheck.y] == 1)
     
  16. kiempoturner

    kiempoturner

    Joined:
    Aug 6, 2014
    Posts:
    2
    What is this error!

    NullReferenceException: Object reference not set to an instance of an object
    MazeGenerator.Start () (at Assets/asset/Scripts/MazeGenerator.cs:50)
     
  17. smitchell

    smitchell

    Joined:
    Mar 12, 2012
    Posts:
    702
    It means exactly what it says it means. Something is not set to a instance. EG something is null

    Since it says it's coming from start the only thing that seems logical to be null is the camera. Make sure your camera has the 'Main Camera' tag
     
  18. Divine-Games

    Divine-Games

    Joined:
    Jan 26, 2014
    Posts:
    1
    How do I get unity to shut up about the p?
    Assets/Maze.cs(189,34): error CS1525: Unexpected symbol `p'
     
  19. hpjohn

    hpjohn

    Joined:
    Aug 14, 2012
    Posts:
    2,190
    Something went wrong with the code posting/migration
    There are missing operators, read the thread (but no one noticed them missing on that line yet)
     
  20. Michio-Magic

    Michio-Magic

    Joined:
    Dec 25, 2014
    Posts:
    18
    OK. I found this script had problems too... so I fixed them.
    Make a cube 1x1x1 , zero it out in space, add this script, set the width/height in the inspector, put the material in the slot, and press play. Hope this helps.
    I added a MazeString = MazeString+"X"; where all X = blocks and 0 = spaces. I think I can use the public String to create a maze in js, that I can make rooms, add doors, etc, BEFORE making the walls.

    ==============

    Code (CSharp):
    1. // remember you can NOT have even numbers of height or width in this style of block maze
    2. // to ensure we can get walls around all tunnels...  so use 21 x 13 , or 7 x 7 for examples.
    3.  
    4. using UnityEngine;
    5. using System;
    6. using System.Collections;
    7. using System.Collections.Generic;
    8. public class MazeGenerator : MonoBehaviour {
    9.     public int width, height;
    10.     public Material brick;
    11.     private int[,] Maze;
    12.     private List<Vector3> pathMazes = new List<Vector3>();
    13.     private Stack<Vector2> _tiletoTry = new Stack<Vector2>();
    14.     private List<Vector2> offsets = new List<Vector2> { new Vector2(0, 1), new Vector2(0, -1), new Vector2(1, 0), new Vector2(-1, 0) };
    15.     private System.Random rnd = new System.Random();
    16.     private int _width, _height;
    17.     private Vector2 _currentTile;
    18.     public String MazeString;
    19.  
    20.     public Vector2 CurrentTile {
    21.         get { return _currentTile; }
    22.         private set {
    23.             if (value.x < 1 || value.x >= this.width - 1 || value.y < 1 || value.y >= this.height - 1){
    24.                 throw new ArgumentException("Width and Height must be greater than 2 to make a maze");
    25.             }
    26.             _currentTile = value;
    27.         }
    28.     }
    29.     private static MazeGenerator instance;
    30.     public static MazeGenerator Instance {
    31.         get {return instance;}
    32.     }
    33.     void Awake()  { instance = this;}
    34.     void Start() { MakeBlocks(); }
    35.  
    36. // end of main program
    37.  
    38. // ============= subroutines ============
    39.  
    40.     void MakeBlocks() {
    41.    
    42.         Maze = new int[width, height];
    43.         for (int x = 0; x < width; x++) {
    44.             for (int y = 0; y < height; y++)  {
    45.                 Maze[x, y] = 1;
    46.             }
    47.         }
    48.         CurrentTile = Vector2.one;
    49.         _tiletoTry.Push(CurrentTile);
    50.         Maze = CreateMaze();  // generate the maze in Maze Array.
    51.         GameObject ptype = null;
    52.         for (int i = 0; i <= Maze.GetUpperBound(0); i++)  {
    53.             for (int j = 0; j <= Maze.GetUpperBound(1); j++) {
    54.                 if (Maze[i, j] == 1)  {
    55.                     MazeString=MazeString+"X";  // added to create String
    56.                     ptype = GameObject.CreatePrimitive(PrimitiveType.Cube);
    57.                     ptype.transform.position = new Vector3(i * ptype.transform.localScale.x, 0, j * ptype.transform.localScale.z);
    58.                
    59.                     if (brick != null)  { ptype.renderer.material = brick; }
    60.                     ptype.transform.parent = transform;
    61.                 }
    62.                 else if (Maze[i, j] == 0) {
    63.                     MazeString=MazeString+"0"; // added to create String
    64.                     pathMazes.Add(new Vector3(i, 0, j));
    65.                 }
    66.             }
    67.             MazeString=MazeString+"\n";  // added to create String
    68.         }
    69.         print (MazeString);  // added to create String
    70.     }
    71.  
    72.     // =======================================
    73.     public int[,] CreateMaze() {
    74.    
    75.         //local variable to store neighbors to the current square as we work our way through the maze
    76.             List<Vector2> neighbors;
    77.             //as long as there are still tiles to try
    78.             while (_tiletoTry.Count > 0)
    79.             {
    80.                 //excavate the square we are on
    81.                 Maze[(int)CurrentTile.x, (int)CurrentTile.y] = 0;
    82.                 //get all valid neighbors for the new tile
    83.                 neighbors = GetValidNeighbors(CurrentTile);
    84.                 //if there are any interesting looking neighbors
    85.                 if (neighbors.Count > 0)
    86.                 {
    87.                     //remember this tile, by putting it on the stack
    88.                     _tiletoTry.Push(CurrentTile);
    89.                     //move on to a random of the neighboring tiles
    90.                     CurrentTile = neighbors[rnd.Next(neighbors.Count)];
    91.                 }
    92.                 else
    93.                 {
    94.                     //if there were no neighbors to try, we are at a dead-end toss this tile out
    95.                     //(thereby returning to a previous tile in the list to check).
    96.                     CurrentTile = _tiletoTry.Pop();
    97.                 }
    98.             }
    99.             print("Maze Generated ...");
    100.             return Maze;
    101.         }
    102.    
    103.     // ================================================
    104.         // Get all the prospective neighboring tiles "centerTile" The tile to test
    105.         // All and any valid neighbors</returns>
    106.         private List<Vector2> GetValidNeighbors(Vector2 centerTile) {
    107.             List<Vector2> validNeighbors = new List<Vector2>();
    108.             //Check all four directions around the tile
    109.             foreach (var offset in offsets) {
    110.                 //find the neighbor's position
    111.                 Vector2 toCheck = new Vector2(centerTile.x + offset.x, centerTile.y + offset.y);
    112.                 //make sure the tile is not on both an even X-axis and an even Y-axis
    113.                 //to ensure we can get walls around all tunnels
    114.                 if (toCheck.x % 2 == 1 || toCheck.y % 2 == 1) {
    115.                
    116.                     //if the potential neighbor is unexcavated (==1)
    117.                     //and still has three walls intact (new territory)
    118.                     if (Maze[(int)toCheck.x, (int)toCheck.y]  == 1 && HasThreeWallsIntact(toCheck)) {
    119.                    
    120.                         //add the neighbor
    121.                         validNeighbors.Add(toCheck);
    122.                     }
    123.                 }
    124.             }
    125.             return validNeighbors;
    126.         }
    127.     // ================================================
    128.         // Counts the number of intact walls around a tile
    129.         //"Vector2ToCheck">The coordinates of the tile to check
    130.         //Whether there are three intact walls (the tile has not been dug into earlier.
    131.         private bool HasThreeWallsIntact(Vector2 Vector2ToCheck) {
    132.        
    133.             int intactWallCounter = 0;
    134.             //Check all four directions around the tile
    135.             foreach (var offset in offsets) {
    136.            
    137.                 //find the neighbor's position
    138.                 Vector2 neighborToCheck = new Vector2(Vector2ToCheck.x + offset.x, Vector2ToCheck.y + offset.y);
    139.                 //make sure it is inside the maze, and it hasn't been dug out yet
    140.                 if (IsInside(neighborToCheck) && Maze[(int)neighborToCheck.x, (int)neighborToCheck.y] == 1) {
    141.                     intactWallCounter++;
    142.                 }
    143.             }
    144.             //tell whether three walls are intact
    145.             return intactWallCounter == 3;
    146.         }
    147.    
    148.     // ================================================
    149.         private bool IsInside(Vector2 p) {
    150.             //return p.x >= 0  p.y >= 0  p.x < width  p.y < height;
    151.            return p.x >= 0 && p.y >= 0 && p.x < width && p.y < height;
    152.         }
    153. }

     
    Last edited: Feb 20, 2015
  21. Suraj.

    Suraj.

    Joined:
    Jan 27, 2015
    Posts:
    4


    thanks for script softwizz...after little erros fix script is working././But however its not creating maze..I have created a gameobject,added sprite rendere to it.n 2d collider (created prefab of this) and attached script to it and dragged prefab..its creating boxes around itself...so in short its scaling itself..not creating maze..would appreciate if u could throw some light...thanks
     
  22. Suraj.

    Suraj.

    Joined:
    Jan 27, 2015
    Posts:
    4
    [/QUOTE]


    What r u trying to say here? Do u need help with those errors? Just add '&&' and it will work...
     
  23. malosal

    malosal

    Joined:
    Jun 22, 2013
    Posts:
    151
    Is there any way to modify the walls themselves, like how high/low they are, or how much space to have for walking room in the maze?
     
  24. hike1

    hike1

    Joined:
    Sep 6, 2009
    Posts:
    401
    Assets/Scripts/MazeGenerator.cs(186,34): error CS1525: Unexpected symbol `p'

    private bool IsInside(Vector2 p)
    {
    return p.x >= 0 p.y >= 0 p.x < width p.y < height;
     
  25. hike1

    hike1

    Joined:
    Sep 6, 2009
    Posts:
    401
    Only the 1st version worked, the others freeze unity, (4.6 and 5) no errors, did I copy/paste right?
    You can save the mazes by dragging them into/project/ prefab from hierarchy
     

    Attached Files:

  26. hike1

    hike1

    Joined:
    Sep 6, 2009
    Posts:
    401
    Working now, only does a cube, even if I make the cube a wall shape
     

    Attached Files:

    haroonkhan38 likes this.
  27. malosal

    malosal

    Joined:
    Jun 22, 2013
    Posts:
    151
    I never understood why people don't care about issues in things they released.
     
  28. RicardoAntezana

    RicardoAntezana

    Joined:
    Nov 20, 2014
    Posts:
    1
    Hey how would I make an exit?
     
  29. Logic59

    Logic59

    Joined:
    May 14, 2015
    Posts:
    1
    Hi all , Can someone tell me how to modify the walls in the script please, like the scale because I find it a bit small.
    Thank you
     
  30. Michio-Magic

    Michio-Magic

    Joined:
    Dec 25, 2014
    Posts:
    18
    I've been asked a few times about my modifications to this script.
    Note: I did not write the original quick maze generator script, but i did modify it.

    Upon opening this script in Unity 5, I got this message:
    "UnityEngine.GameObject.renderer' is obsolete: `Property renderer has been deprecated. Use GetComponent<Renderer>() instead." , then got "Can't add script component... make sure the file name and class name match". So I renamed the script and class (within the script) mazegen, and fixed the obsolete renderer. Now it is up-to-date (May 2015).

    ==================
    First: Make sure the maze script works on your computer!

    Drag this 'mazegen.sc' script into the Project window.
    I then Created a CUBE, (default size 1x1x1), and in the inspector changed position X,Y,Z all to 0 (zeroed it out), pressed 'f' to center the cube in the Scene window, and dragged the script 'mazegen.cs' onto the cube in the Heirarchy window (or onto the cube in the Scene window.) Select 'Create' material, rename it 'grey' and drag the new material onto the cube itself in the Scene window. It helps to view it with shading so I added a Directional light. (pic1)


    Finally select the Cube, and note the properties in the Inspector window, scroll down to see the Mazegen (Script). Set Width and Height (both need to be odd numbers), and set the Brick material as the 'grey' material. Remember you can make this material anything you want. Just press PLAY, and you'll see the maze. (pic2)


    Note: If you go with any EVEN numbers you'll get those 'Saw-Edge' lines (pic3), so use only ODD numbers.


    I know the above instructions are too basic, but we just want to be clear about what we're doing. If you get this far, and see a maze (pic2) then the simple maze generator script has done it's job. This is not a complete game, or even a playable demo, but only a MAZE GENERATOR. What you do with this maze is how you can make a game to play.


    ================
    Modifying the generated MAZE - Method 1.

    In order to scale it larger, add a variable 'scale'.
    line 10 : public int scale;

    and use it to modify localScale.
    line 58 : ptype.transform.localScale += new Vector3(scale,scale,scale);

    I set the scale at '2' in the inspector and pressed play : see (pic4)


    You can change the height of 'Y' = Vector3 ( X , Y , Z )
    line 58 : ptype.transform.localScale += new Vector3(scale,scale*3,scale);

    Press play and the Y is now 6 units (scale variable = 2, x 3) : see (pic5)


    =================
    Modifying the generated MAZE - Method 2.

    An advanced method is to have the maze generator algorithyms create an ARRAY that you can see, and modify, before creating the maze.This script makes use of a C# ARRAY function line 42 : Maze = new int[width, height];

    My preference is that I like to use javascript (UnityScript), and I could not use a multi-dimensional array in js (an array with width AND height), so a string works well. After running my script once, open the Console window, and see my array of data in the form of a String >> MazeString=MazeString+"."; // added to create String . See (pic6). On the right I have grabbed the string, and viewed it in a fixed width font, so we can see that the walls are the 'X' s and the spaces are the periods.


    Disable the 'mazegen.cs' script from making blocks in your scene. use // in front of lines 10, 12, 51, 56, 57, 59, 60, 64. When this script runs it will now only generate the 'MazeString'. Because the Awake function calls MakeBlocks(), it is run before Start functions. Check in the Console window that it is printing the string out.

    Using the same iteration format to get the data out of the String, I wrote another script in JavaScript that will actually create the maze blocks in the Scene window. Now you should have a cube with 'Mazegen (Script)' , and 'Makemaze (Script)' attached. Set Mazecube : GameObject as itself, set the Cubesize (I use 3), and Brick material.

    When you play the game, you'll see red and white cubes (pic7).

    I made a couple of lines of codes to show where the red blocks are. An interesting thing about this maze generator, is that is in this style of BLOCKS for a mazes, rather than long thin WALLS, is that every red block will ALWAYS be there; they are like the frame of the maze. You can remove any single grey block and you'll never be stuck; if you remove a red block and put a treasure, trap, door, or exit there it COULD be blocked by a random block placed by the maze generator.

    To make an entrance or exit, pre-choose a GREY block along the perimeter, and then using the function RemoveBlocks(mazearray), remove the block BEFORE the maze is placed with function makeMaze(). You can also use the RemoveBlocks sub-routine to clear a larger area inside the maze.

    For the floor just make a large plane or flat cube as the floor.

    I hope this gives many of you designers good insight into my method of making random mazes.

    - Michio

    =================
    Code (CSharp):
    1. // mazegen.cs
    2. // remember you can NOT have even numbers of height or width in this style of block maze
    3. // to ensure we can get walls around all tunnels...  so use 21 x 13 , or 7 x 7 for examples.
    4. using UnityEngine;
    5. using System;
    6. using System.Collections;
    7. using System.Collections.Generic;
    8. public class mazegen : MonoBehaviour {
    9.     public int width, height;
    10.     public Material brick;
    11.     private int[,] Maze;
    12.     private Stack<Vector2> _tiletoTry = new Stack<Vector2>();
    13.     private List<Vector2> offsets = new List<Vector2> { new Vector2(0, 1), new Vector2(0, -1), new Vector2(1, 0), new Vector2(-1, 0) };
    14.     private System.Random rnd = new System.Random();
    15.     private int _width, _height;
    16.     private Vector2 _currentTile;
    17.     public static String MazeString;
    18.     public Vector2 CurrentTile {
    19.         get { return _currentTile; }
    20.         private set {
    21.             if (value.x < 1 || value.x >= this.width - 1 || value.y < 1 || value.y >= this.height - 1){
    22.                 throw new ArgumentException("Width and Height must be greater than 2 to make a maze");
    23.             }
    24.             _currentTile = value;
    25.         }
    26.     }
    27.     private static mazegen instance;
    28.     public static mazegen Instance {
    29.         get {return instance;}
    30.     }
    31.     void Awake()  { instance = this; MakeBlocks(); }
    32. // end of main program
    33. // ============= subroutines ============
    34.     void MakeBlocks() {
    35.  
    36.         Maze = new int[width, height];
    37.         for (int x = 0; x < width; x++) {
    38.             for (int y = 0; y < height; y++)  {
    39.                 Maze[x, y] = 1;
    40.             }
    41.         }
    42.         CurrentTile = Vector2.one;
    43.         _tiletoTry.Push(CurrentTile);
    44.         Maze = CreateMaze();  // generate the maze in Maze Array.
    45.         GameObject ptype = null;
    46.         for (int i = 0; i <= Maze.GetUpperBound(0); i++)  {
    47.             for (int j = 0; j <= Maze.GetUpperBound(1); j++) {
    48.                 if (Maze[i, j] == 1)  {
    49.                     MazeString=MazeString+"X";  // added to create String
    50.                     ptype = GameObject.CreatePrimitive(PrimitiveType.Cube);
    51.                    ptype.transform.position = new Vector3(i * ptype.transform.localScale.x, 0, j * ptype.transform.localScale.z);
    52.  
    53.                     if (brick != null)  { ptype.GetComponent<Renderer>().material = brick; }
    54.                     ptype.transform.parent = transform;
    55.                 }
    56.                 else if (Maze[i, j] == 0) {
    57.                     MazeString=MazeString+"."; // added to create String
    58.                 }
    59.             }
    60.             MazeString=MazeString+"\n";  // added to create String
    61.         }
    62.         print (MazeString);  // added to create String
    63.     }
    64.     // =======================================
    65.     public int[,] CreateMaze() {
    66.  
    67.         //local variable to store neighbors to the current square as we work our way through the maze
    68.             List<Vector2> neighbors;
    69.             //as long as there are still tiles to try
    70.             while (_tiletoTry.Count > 0)
    71.             {
    72.                 //excavate the square we are on
    73.                 Maze[(int)CurrentTile.x, (int)CurrentTile.y] = 0;
    74.                 //get all valid neighbors for the new tile
    75.                 neighbors = GetValidNeighbors(CurrentTile);
    76.                 //if there are any interesting looking neighbors
    77.                 if (neighbors.Count > 0)
    78.                 {
    79.                     //remember this tile, by putting it on the stack
    80.                     _tiletoTry.Push(CurrentTile);
    81.                     //move on to a random of the neighboring tiles
    82.                     CurrentTile = neighbors[rnd.Next(neighbors.Count)];
    83.                 }
    84.                 else
    85.                 {
    86.                     //if there were no neighbors to try, we are at a dead-end toss this tile out
    87.                     //(thereby returning to a previous tile in the list to check).
    88.                     CurrentTile = _tiletoTry.Pop();
    89.                 }
    90.             }
    91.             print("Maze Generated ...");
    92.             return Maze;
    93.         }
    94.  
    95.     // ================================================
    96.         // Get all the prospective neighboring tiles "centerTile" The tile to test
    97.         // All and any valid neighbors</returns>
    98.         private List<Vector2> GetValidNeighbors(Vector2 centerTile) {
    99.             List<Vector2> validNeighbors = new List<Vector2>();
    100.             //Check all four directions around the tile
    101.             foreach (var offset in offsets) {
    102.                 //find the neighbor's position
    103.                 Vector2 toCheck = new Vector2(centerTile.x + offset.x, centerTile.y + offset.y);
    104.                 //make sure the tile is not on both an even X-axis and an even Y-axis
    105.                 //to ensure we can get walls around all tunnels
    106.                 if (toCheck.x % 2 == 1 || toCheck.y % 2 == 1) {
    107.              
    108.                     //if the potential neighbor is unexcavated (==1)
    109.                     //and still has three walls intact (new territory)
    110.                     if (Maze[(int)toCheck.x, (int)toCheck.y]  == 1 && HasThreeWallsIntact(toCheck)) {
    111.                  
    112.                         //add the neighbor
    113.                         validNeighbors.Add(toCheck);
    114.                     }
    115.                 }
    116.             }
    117.             return validNeighbors;
    118.         }
    119.     // ================================================
    120.         // Counts the number of intact walls around a tile
    121.         //"Vector2ToCheck">The coordinates of the tile to check
    122.         //Whether there are three intact walls (the tile has not been dug into earlier.
    123.         private bool HasThreeWallsIntact(Vector2 Vector2ToCheck) {
    124.      
    125.             int intactWallCounter = 0;
    126.             //Check all four directions around the tile
    127.             foreach (var offset in offsets) {
    128.          
    129.                 //find the neighbor's position
    130.                 Vector2 neighborToCheck = new Vector2(Vector2ToCheck.x + offset.x, Vector2ToCheck.y + offset.y);
    131.                 //make sure it is inside the maze, and it hasn't been dug out yet
    132.                 if (IsInside(neighborToCheck) && Maze[(int)neighborToCheck.x, (int)neighborToCheck.y] == 1) {
    133.                     intactWallCounter++;
    134.                 }
    135.             }
    136.             //tell whether three walls are intact
    137.             return intactWallCounter == 3;
    138.         }
    139.  
    140.     // ================================================
    141.         private bool IsInside(Vector2 p) {
    142.             //return p.x >= 0  p.y >= 0  p.x < width  p.y < height;
    143.            return p.x >= 0 && p.y >= 0 && p.x < width && p.y < height;
    144.         }
    145. }

    ===================

    Code (JavaScript):
    1. // makemaze.js  - MichShire Feb2015
    2.  
    3. var mazecube : GameObject;
    4. var cubesize=3;
    5. var brick : Material;
    6. private var MazeString : String = "";
    7. private var width : int;
    8. private var height : int;
    9. private static var mazearray : Array;
    10. private var t : boolean = false ;
    11.  
    12. function Start() {
    13.     MazeString = mazecube.GetComponent("mazegen").MazeString;
    14.     width = mazecube.GetComponent("mazegen").width;
    15.     height = mazecube.GetComponent("mazegen").height;
    16.     print ("mazegen width = " + width + " height = " + height +" array = \n" +MazeString);
    17.     makeMaze();
    18. }
    19.  
    20. function makeMaze() {
    21.  
    22.         var mazearray = MazeString.Split("\n"[0]);
    23.         //print (mazearray[0]);
    24.         //mod = mazearray[0];  index = 9;
    25.     //mazearray[0] = mod.Substring(0, index) + '0' + mod.Substring(index + 1);
    26.  
    27.     RemoveBlocks(mazearray);
    28.  
    29.  
    30.         for (var i : int = 0; i <width; i++)  {
    31.             //print ("array " + i + " = " + mazearray[i]);
    32.             for (var j : int = 0; j <height; j++)  {
    33.                 var st=mazearray[i];
    34.                 //print ("mazei= " + i + mazearray[i] );
    35.                 //print ("substring= " + st.Substring(0,1) );
    36.                 if (st.Substring(j,1)=="X")  {      // make a block if 'X' ...
    37.                 ptype = GameObject.CreatePrimitive(PrimitiveType.Cube);
    38.                 ptype.transform.position = new Vector3(j * cubesize, .5+(cubesize/2), i *cubesize);
    39.                 ptype.transform.localScale = new Vector3(cubesize, cubesize, cubesize);
    40.  
    41.                 if (brick != null)  { ptype.GetComponent.<Renderer>().material = brick; }
    42.                     ptype.transform.parent = transform;
    43.                 }
    44.                 // just to show colored blocks every second block
    45.                 t=!t;
    46.                 if (t==true && (i==0 || i==2 || i==4 ||i==6 || i==8 || i==10 || i==12)){
    47.                 //    ptype.GetComponent.<Renderer>().material.color = Color.red;
    48.                 }
    49.             }
    50.         }
    51.         //t=1;
    52.             return;
    53.     }
    54.  
    55.     // ====================
    56. function RemoveBlocks(mazearray) {
    57.     var mod; var index;
    58.  
    59.     //print (mazearray[0]);
    60.  
    61.     // entrance
    62.     mod = mazearray[0];  index = 1;
    63.     mazearray[0] = mod.Substring(0, index) + '0' + mod.Substring(index + 1);
    64.  
    65.     // exit
    66.     mod = mazearray[12];  index = 7;
    67.     mazearray[12] = mod.Substring(0, index) + '0' + mod.Substring(index + 1);
    68. }
    69.    
     
  31. _Newbz

    _Newbz

    Joined:
    Dec 27, 2014
    Posts:
    1
    I have a question, how would i make the corridors longer so its less turns.
     
  32. nguyenthanhliemfc

    nguyenthanhliemfc

    Joined:
    Dec 28, 2016
    Posts:
    21
    How to create COMPASS?
     
  33. Bolt

    Bolt

    Joined:
    Dec 15, 2012
    Posts:
    296
    This is my code for generate a random maze :)
    Code (csharp):
    1.  
    2.  
    3.  
    4. using UnityEngine;
    5. using System.Collections;
    6. using System.Collections.Generic;
    7. using System.Linq;
    8.  
    9.  
    10.  
    11. public class Cell
    12. {
    13.    public bool isVisited;
    14.    public GameObject north;
    15.    public GameObject south;
    16.    public GameObject east;
    17.    public GameObject west;
    18.    public GameObject floor;
    19.    public int current; //Debug
    20.    public Vector3 cellPosition; //Debug
    21.  
    22.  
    23. }
    24.  
    25.  
    26.  
    27.  
    28.  
    29.  
    30.  
    31.  
    32. public class GeneratorMaze : MonoBehaviour
    33. {
    34.  
    35.  
    36.     [Range(3, 200)]
    37.     public int xSize;//size axis X
    38.     [Range(3, 200)]
    39.     public int ySize;//size axis Y
    40.     [Range(1, 50)]
    41.     public int sizeWall; //Heigth and Width Wall
    42.     public GameObject wall; //GameObject Wall
    43.     private GameObject Floor; //Create GameObject Floor
    44.     public GameObject floor;//GameObject Floor
    45.     private Vector3 initialPos; //start position Maze
    46.     private GameObject[] walls;
    47.     private GameObject[] floors;
    48.     private float middle;
    49.     private GameObject Maze;
    50.     public List<Cell> cells;
    51.     private int eastLimited;
    52.     private int southLimited;
    53.     private int northLimited;
    54.     private int westLimited;
    55.     private int currentRow;
    56.     public List<Cell> lastCellVisited;
    57.     private int indexCell;
    58.     private int currentI;
    59.     private Cell currentCell;
    60.     private List<Cell> neighboringCell;
    61.     private GameObject parent;
    62.     private int range;
    63.  
    64.  
    65.  
    66.  
    67.     void Start()
    68.     {
    69.      
    70.         Create();
    71.      }
    72.  
    73.     void Update()
    74.     {
    75.      
    76.     }
    77.  
    78.  
    79.  
    80.  
    81.  
    82.     void Create()
    83.     {
    84.  
    85.         //CELL INIT
    86.         middle = (float)sizeWall / 2;
    87.         initialPos = Vector3.zero;
    88.         this.transform.position = initialPos;
    89.         lastCellVisited = new List<Cell>();
    90.         neighboringCell = new List<Cell>();
    91.         cells = new List<Cell>();
    92.    
    93.  
    94.         currentI = 0;
    95.         range = sizeWall;
    96.  
    97.         wall.transform.localScale = new Vector3(0.1f, sizeWall, sizeWall);
    98.         floor.transform.localScale = new Vector3(sizeWall, sizeWall, sizeWall);
    99.  
    100.         createWalls();
    101.         createFloor();
    102.         createCell();
    103.        // this.transform.position = new Vector3(cells[(xSize / 2)].floor.transform.position.x, 0, cells[(ySize / 2)].floor.transform.position.x);
    104.      
    105.     }
    106.  
    107.    
    108.  
    109.     //I create labyrinth floor
    110.     void createFloor()
    111.     {
    112.         Floor = new GameObject("Floor"); //group all the Quad to keep everything in order
    113.         for (int i = 0; i < ySize; i++)
    114.         {
    115.             for (int j = 0; j < xSize; j++)
    116.             {
    117.                 var insFloor = Instantiate(floor, new Vector3((j * sizeWall) + middle, -middle, i * sizeWall), Quaternion.Euler(90, 0, 0)) as GameObject;
    118.  
    119.                 insFloor.transform.parent = Floor.transform; //group all the Quad to keep everything in order
    120.                 indexCell = (i * xSize) + j; //I transform the array into an array
    121.                 insFloor.transform.name = indexCell.ToString();
    122.             }
    123.         }
    124.     }
    125.  
    126.     //I create labyrinth walls
    127.     void createWalls()
    128.     {
    129.         Maze = new GameObject("Maze"); //group all the Quad to keep everything in order
    130.  
    131.         //X
    132.         for (int i = 0; i < ySize; i++)
    133.         {
    134.             for (int j = 0; j <= xSize; j++)
    135.             {
    136.                 GameObject wallX = Instantiate(wall, new Vector3(initialPos.x + (j * sizeWall), 0, initialPos.z + (i * sizeWall)), Quaternion.identity) as GameObject;
    137.                 wallX.transform.parent = Maze.transform;
    138.  
    139.             }
    140.         }
    141.  
    142.  
    143.         //Y
    144.         for (int i = 0; i <= ySize; i++)
    145.         {
    146.             for (int j = 0; j < xSize; j++)
    147.             {
    148.                 GameObject wallY = Instantiate(wall, new Vector3(initialPos.x + (j * sizeWall) + middle, 0, initialPos.z + (i * sizeWall) - middle), Quaternion.Euler(0, 90, 0)) as GameObject;
    149.                 wallY.transform.parent = Maze.transform;
    150.             }
    151.         }
    152.  
    153.     }
    154.  
    155.  
    156.     //I create the cell, check the fees for each cell walls and floors
    157.     void createCell()
    158.     {
    159.         walls = new GameObject[Maze.transform.childCount];
    160.         floors = new GameObject[xSize * ySize];
    161.  
    162.  
    163.         int south = (xSize + 1) * ySize;
    164.         int north = ((xSize + 1) * ySize) + xSize;
    165.  
    166.         for (int i = 0; i < Maze.transform.childCount; i++)
    167.         {
    168.             walls[i] = Maze.transform.GetChild(i).gameObject;
    169.         }
    170.  
    171.  
    172.         for (int i = 0; i < xSize * ySize; i++)
    173.         {
    174.             floors[i] = Floor.transform.GetChild(i).gameObject;
    175.         }
    176.  
    177.         for (int i = 0; i < xSize * ySize; i++)
    178.         {
    179.  
    180.             cells.Add(new Cell());
    181.             currentRow = (i / xSize); //find current row for every cell
    182.  
    183.  
    184.             cells[i].south = walls[i + south];
    185.             cells[i].north = walls[i + north];
    186.             cells[i].east = walls[(currentRow + i) + 1];
    187.             cells[i].west = walls[currentRow + i];
    188.             cells[i].floor = floors[i];
    189.             cells[i].current = i;
    190.  
    191.        
    192.         }
    193.  
    194.  
    195.         mazeCreation(cells);
    196.  
    197.      
    198.  
    199.  
    200.     }
    201.  
    202.  
    203.  
    204.     //  Add all of the cells of the current cell that I can visit
    205.     void AddNears(Cell currentCell)
    206.     {
    207.  
    208.  
    209.         int currentRow = (currentCell.current / xSize) + 1;
    210.         int eastLimited = currentCell.current % xSize;
    211.         int westLimited = eastLimited;
    212.         int southLimited = xSize - 1;
    213.         int northLimited = currentCell.current / xSize;
    214.  
    215.  
    216.  
    217.         //East
    218.         if (eastLimited != xSize - 1)
    219.         {
    220.             if (cells[currentCell.current + 1].isVisited == false)
    221.                 neighboringCell.Add(cells[currentCell.current + 1]);
    222.  
    223.         }
    224.         //West
    225.         if (westLimited != 0)
    226.         {
    227.             if (cells[currentCell.current - 1].isVisited == false)
    228.                 neighboringCell.Add(cells[currentCell.current - 1]);
    229.  
    230.         }
    231.         //South
    232.         if (currentCell.current > southLimited)
    233.         {
    234.             if (cells[currentCell.current - xSize].isVisited == false)
    235.                 neighboringCell.Add(cells[currentCell.current - xSize]);
    236.  
    237.         }
    238.         //North
    239.         if (northLimited != ySize - 1)
    240.         {
    241.             if (cells[currentCell.current + xSize].isVisited == false)
    242.                 neighboringCell.Add(cells[currentCell.current + xSize]);
    243.  
    244.         }
    245.     }
    246.  
    247.     //It breaks down the wall between one cell and the other
    248.     void breakWall(Cell curr, Cell choose)
    249.     {
    250.         //East
    251.         if (choose.current == curr.current + 1)
    252.         {
    253.             Destroy(curr.east);
    254.             currentCell = cells[currentCell.current + 1];
    255.          
    256.  
    257.         }
    258.  
    259.         //West
    260.         if (choose.current == curr.current - 1)
    261.         {
    262.             Destroy(curr.west);
    263.             currentCell = cells[currentCell.current - 1];
    264.          
    265.  
    266.         }
    267.         //South
    268.  
    269.         if (choose.current == curr.current - xSize)
    270.         {
    271.             Destroy(curr.south);
    272.             currentCell = cells[currentCell.current - xSize];
    273.          
    274.  
    275.  
    276.         }
    277.         //North
    278.         if (choose.current == curr.current + xSize)
    279.         {
    280.             Destroy(curr.north);
    281.             currentCell = cells[currentCell.current + xSize];
    282.  
    283.         }
    284.  
    285.  
    286.  
    287.     }
    288.  
    289.  
    290.     //maze creation algorithm
    291.     void mazeCreation(List<Cell> allCells)
    292.     {
    293.  
    294.         int visitedCell = 0;
    295.  
    296.  
    297.  
    298.         int random = Random.Range(0, allCells.Count); //I choose a cell at random
    299.         currentCell = allCells[random]; //the choice cell becomes the current cell
    300.      
    301.  
    302.  
    303.         while (visitedCell < allCells.Count) //I run the cycle for all the cells of the matrix
    304.         {
    305.             AddNears(currentCell); //add to the selection cell neighboring cells
    306.             int randomNeighboring = Random.Range(0, neighboringCell.Count); //I choose a neighboring cell at random
    307.  
    308.  
    309.  
    310.             if (neighboringCell.Any()) //if there are cells neighboring the current cell
    311.             {
    312.                 lastCellVisited.Add(currentCell); //becomes visited
    313.  
    314.                 breakWall(currentCell, neighboringCell[randomNeighboring]); //Break the wall
    315.  
    316.                 neighboringCell.Clear(); //Reset the list of neighboring cells
    317.                 visitedCell++;
    318.                 currentCell.isVisited = true;
    319.  
    320.  
    321.             }
    322.  
    323.             else
    324.             {
    325.                 //else I choose a already been visited cell
    326.                 random = Random.Range(0, lastCellVisited.Count);
    327.                 currentCell = lastCellVisited[random];
    328.  
    329.             }
    330.  
    331.         }
    332.      
    333.  
    334.     }
    335. }
    336.  
     
    setKAIN, saneangel8 and Firlefanz73 like this.
  34. nguyenthanhliemfc

    nguyenthanhliemfc

    Joined:
    Dec 28, 2016
    Posts:
    21
    How to set center of the maze is the center of world (Pos: 0,0,0)
     
  35. saneangel8

    saneangel8

    Joined:
    May 1, 2017
    Posts:
    4
    @Bolt
    Digging this script. I spent a day with my head in mazes and procedural generation.
    I have added my own fine tuning to it, I'd love to chat about the code more.
    I have some questions about the algorithm you used and the structure, choices in your code.

    I adjusted where you were doing the localScale change. I didn't want to change the PreFabs localScale (97, 98), it changes the prefab itself, so I just made the adjustment to the instances : insFloor, wallX, wallY; this prevents any changes to the prefab itself.
     
    setKAIN and Bolt like this.
  36. malghamdi1045

    malghamdi1045

    Joined:
    Dec 8, 2019
    Posts:
    1
    Hello,

    I have a question regarding the maze creation, are you using any specific famous algorithms to create the maze? or the code is completely created by you.
     
  37. Ekkara

    Ekkara

    Joined:
    Jan 27, 2018
    Posts:
    1
    I did a few Edits myself with these instead of starting the game every time you want to generate one maze, you simply press a button and you get x amount of them in a folder as prefabs (Obs the new script needs to be in the "Editor" folder and the other script should be there when you compiling the game, not when using it in the editor though). I also removed the singelton functions which I am unsure why they are there in the first place since there was no function or variables one might need outside the script :/ I have also corrected some missing "&&" operations and another syntax that didn't work when I implemented the script to my unity scene

    Edit: wow I just noticed this is an old thread :p hope didn't bother anyone when posting this

    Code (CSharp):
    1. using System.Collections;
    2. using System.Collections;
    3. using System.Collections.Generic;
    4. using UnityEngine;
    5. using System;
    6. using UnityEditor;
    7. using System.IO;
    8.  
    9. public class MazeGenerator : MonoBehaviour
    10. {
    11.     public int width, height;
    12.     public Material brick;
    13.     private int[,] Maze;
    14.     private List<Vector3> pathMazes = new List<Vector3>();
    15.     private Stack<Vector2> _tiletoTry = new Stack<Vector2>();
    16.     private List<Vector2> offsets = new List<Vector2> { new Vector2(0, 1), new Vector2(0, -1), new Vector2(1, 0), new Vector2(-1, 0) };
    17.     private System.Random rnd = new System.Random();
    18.     private int _width, _height;
    19.     private Vector2 _currentTile;
    20.  
    21.  
    22.     [SerializeField] int amountOfMazes = 1;
    23.  
    24.     public Vector2 CurrentTile
    25.     {
    26.         get { return _currentTile; }
    27.         private set
    28.         {
    29.             if (value.x < 1 || value.x >= this.width - 1 || value.y < 1 || value.y >= this.height - 1)
    30.             {
    31.                 throw new ArgumentException("CurrentTile must be within the one tile border all around the maze");
    32.             }
    33.             if (value.x % 2 == 1 || value.y % 2 == 1)
    34.             { _currentTile = value; }
    35.             else
    36.             {
    37.                 throw new ArgumentException("The current square must not be both on an even X-axis and an even Y-axis, to ensure we can get walls around all tunnels");
    38.             }
    39.         }
    40.     }
    41.     private void Start()
    42.     {
    43.         Camera.main.orthographic = true;
    44.         Camera.main.orthographicSize = 30;
    45.     }
    46.  
    47.     GameObject parent;
    48.     public void GenerateMazes()
    49.     {
    50.         if (!Directory.Exists("Assets/Mazes"))
    51.         {
    52.             Directory.CreateDirectory("Assets/Mazes");
    53.  
    54.         }
    55.  
    56.         for (int i = 0; i < amountOfMazes; i++)
    57.         {
    58.             string name = "Maze" + (i + 1).ToString();
    59.             GameObject newMaze = new GameObject(name);
    60.             parent = newMaze;
    61.  
    62.             GenerateMaze();
    63.  
    64.             string localPath = "Assets/Mazes/" + name + ".prefab";
    65.             PrefabUtility.SaveAsPrefabAssetAndConnect(parent, localPath, InteractionMode.UserAction);
    66.  
    67.             DestroyImmediate(parent);
    68.         }
    69.     }
    70.  
    71.     void GenerateMaze()
    72.     {
    73.         Maze = new int[width, height];
    74.         for (int x = 0; x < width; x++)
    75.         {
    76.             for (int y = 0; y < height; y++)
    77.             {
    78.                 Maze[x, y] = 1;
    79.             }
    80.         }
    81.         CurrentTile = Vector2.one;
    82.         _tiletoTry.Push(CurrentTile);
    83.         Maze = CreateMaze();
    84.         GameObject ptype = null;
    85.  
    86.         for (int i = 0; i <= Maze.GetUpperBound(0); i++)
    87.         {
    88.             for (int j = 0; j <= Maze.GetUpperBound(1); j++)
    89.             {
    90.                 if (Maze[i, j] == 1)
    91.                 {
    92.                     ptype = GameObject.CreatePrimitive(PrimitiveType.Cube);
    93.                     ptype.transform.position = new Vector3(i * ptype.transform.localScale.x, j * ptype.transform.localScale.y, 0);
    94.                     if (brick != null)
    95.                     {
    96.                         ptype.GetComponent<Renderer>().material = brick;
    97.                     }
    98.                     ptype.transform.parent = parent.transform;
    99.                 }
    100.                 else if (Maze[i, j] == 0)
    101.                 {
    102.                     pathMazes.Add(new Vector3(i, j, 0));
    103.                 }
    104.  
    105.             }
    106.         }
    107.     }
    108.  
    109.     public int[,] CreateMaze()
    110.     {
    111.         //local variable to store neighbors to the current square
    112.         //as we work our way through the maze
    113.         List<Vector2> neighbors;
    114.         //as long as there are still tiles to try
    115.         while (_tiletoTry.Count > 0)
    116.         {
    117.             //excavate the square we are on
    118.             Maze[(int)CurrentTile.x, (int)CurrentTile.y] = 0;
    119.  
    120.             //get all valid neighbors for the new tile
    121.             neighbors = GetValidNeighbors(CurrentTile);
    122.  
    123.             //if there are any interesting looking neighbors
    124.             if (neighbors.Count > 0)
    125.             {
    126.                 //remember this tile, by putting it on the stack
    127.                 _tiletoTry.Push(CurrentTile);
    128.                 //move on to a random of the neighboring tiles
    129.                 CurrentTile = neighbors[rnd.Next(neighbors.Count)];
    130.             }
    131.             else
    132.             {
    133.                 //if there were no neighbors to try, we are at a dead-end
    134.                 //toss this tile out
    135.                 //(thereby returning to a previous tile in the list to check).
    136.                 CurrentTile = _tiletoTry.Pop();
    137.             }
    138.         }
    139.  
    140.         return Maze;
    141.     }
    142.     /// <summary>
    143.     /// Get all the prospective neighboring tiles
    144.     /// </summary>
    145.     /// <param name="centerTile">The tile to test</param>
    146.     /// <returns>All and any valid neighbors</returns>
    147.     private List<Vector2> GetValidNeighbors(Vector2 centerTile)
    148.     {
    149.  
    150.         List<Vector2> validNeighbors = new List<Vector2>();
    151.  
    152.         //Check all four directions around the tile
    153.         foreach (var offset in offsets)
    154.         {
    155.             //find the neighbor's position
    156.             Vector2 toCheck = new Vector2(centerTile.x + offset.x, centerTile.y + offset.y);
    157.  
    158.             //make sure the tile is not on both an even X-axis and an even Y-axis
    159.             //to ensure we can get walls around all tunnels
    160.             if (toCheck.x % 2 == 1 || toCheck.y % 2 == 1)
    161.             {
    162.                 //if the potential neighbor is unexcavated (==1)
    163.                 //and still has three walls intact (new territory)
    164.                 if (Maze[(int)toCheck.x, (int)toCheck.y] == 1  && HasThreeWallsIntact(toCheck))
    165.                     {
    166.                     //add the neighbor
    167.                     validNeighbors.Add(toCheck);
    168.                 }
    169.             }
    170.         }
    171.  
    172.         return validNeighbors;
    173.     }
    174.  
    175.  
    176.     /// <summary>
    177.     /// Counts the number of intact walls around a tile
    178.     /// </summary>
    179.     /// <param name="Vector2ToCheck">The coordinates of the tile to check</param>
    180.     /// <returns>Whether there are three intact walls (the tile has not been dug into earlier.</returns>
    181.     private bool HasThreeWallsIntact(Vector2 Vector2ToCheck)
    182.     {
    183.         int intactWallCounter = 0;
    184.  
    185.         //Check all four directions around the tile
    186.         foreach (var offset in offsets)
    187.         {
    188.             //find the neighbor's position
    189.             Vector2 neighborToCheck = new Vector2(Vector2ToCheck.x + offset.x, Vector2ToCheck.y + offset.y);
    190.  
    191.             //make sure it is inside the maze, and it hasn't been dug out yet
    192.             if (IsInside(neighborToCheck) && Maze[(int)neighborToCheck.x, (int)neighborToCheck.y] == 1)
    193.                 {
    194.                 intactWallCounter++;
    195.             }
    196.         }
    197.  
    198.         //tell whether three walls are intact
    199.         return intactWallCounter == 3;
    200.  
    201.     }
    202.  
    203.     private bool IsInside(Vector2 p)
    204.     {
    205.         return p.x >= 0 && p.y >= 0 && p.x < width && p.y < height;
    206.     }
    207. }
    208.  
    209.  
    here is the other script, once again in the "Editor" folder please, otherwise it won't work; if you are unsure what this folder is, just create a new folder in your asset folder with the name "Editor", unity will take it from there ;)
    Code (CSharp):
    1. using System.Collections;
    2. using System.Collections.Generic;
    3. using UnityEngine;
    4. using UnityEditor;
    5.  
    6.  
    7. [CustomEditor(typeof(MazeGenerator))]
    8. public class GeneratorEditor : Editor
    9. {
    10.     public override void OnInspectorGUI()
    11.     {
    12.         MazeGenerator myTarget = (MazeGenerator)target;
    13.         base.OnInspectorGUI();
    14.  
    15.         if (GUILayout.Button("Generate maze"))
    16.         {
    17.             myTarget.GenerateMazes();
    18.         }
    19.     }
    20. }
    21.