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…
using UnityEngine;
using System;
using System.Collections;
using System.Collections.Generic;
public class MazeGenerator : MonoBehaviour {
public int width, height;
public Material brick;
private int[,] Maze;
private List<Vector3> pathMazes = new List<Vector3>();
private Stack<Vector2> _tiletoTry = new Stack<Vector2>();
private List<Vector2> offsets = new List<Vector2> { new Vector2(0, 1), new Vector2(0, -1), new Vector2(1, 0), new Vector2(-1, 0) };
private System.Random rnd = new System.Random();
private int _width, _height;
private Vector2 _currentTile;
public Vector2 CurrentTile
{
get { return _currentTile; }
private set
{
if (value.x < 1 || value.x >= this.width - 1 || value.y < 1 || value.y >= this.height - 1)
{
throw new ArgumentException("CurrentTile must be within the one tile border all around the maze");
}
if (value.x % 2 == 1 || value.y % 2 == 1)
{ _currentTile = value; }
else
{
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");
}
}
}
private static MazeGenerator instance;
public static MazeGenerator Instance
{
get
{
return instance;
}
}
void Awake()
{
instance = this;
}
void Start()
{
Camera.main.orthographic = true;
Camera.main.orthographicSize = 30;
GenerateMaze();
}
void GenerateMaze()
{
Maze = new int[width, height];
for (int x = 0; x < width; x++)
{
for (int y = 0; y < height; y++)
{
Maze[x, y] = 1;
}
}
CurrentTile = Vector2.one;
_tiletoTry.Push(CurrentTile);
Maze = CreateMaze();
GameObject ptype = null;
for (int i = 0; i <= Maze.GetUpperBound(0); i++)
{
for (int j = 0; j <= Maze.GetUpperBound(1); j++)
{
if (Maze[i, j] == 1)
{
ptype = GameObject.CreatePrimitive(PrimitiveType.Cube);
ptype.transform.position = new Vector3(i * ptype.transform.localScale.x, j * ptype.transform.localScale.y, 0);
if (brick != null)
{
ptype.renderer.material = brick;
}
ptype.transform.parent = transform;
}
else if (Maze[i, j] == 0)
{
pathMazes.Add(new Vector3(i, j, 0));
}
}
}
}
public int[,] CreateMaze()
{
//local variable to store neighbors to the current square
//as we work our way through the maze
List<Vector2> neighbors;
//as long as there are still tiles to try
while (_tiletoTry.Count > 0)
{
//excavate the square we are on
Maze[(int)CurrentTile.x, (int)CurrentTile.y] = 0;
//get all valid neighbors for the new tile
neighbors = GetValidNeighbors(CurrentTile);
//if there are any interesting looking neighbors
if (neighbors.Count > 0)
{
//remember this tile, by putting it on the stack
_tiletoTry.Push(CurrentTile);
//move on to a random of the neighboring tiles
CurrentTile = neighbors[rnd.Next(neighbors.Count)];
}
else
{
//if there were no neighbors to try, we are at a dead-end
//toss this tile out
//(thereby returning to a previous tile in the list to check).
CurrentTile = _tiletoTry.Pop();
}
}
return Maze;
}
/// <summary>
/// Get all the prospective neighboring tiles
/// </summary>
/// <param name="centerTile">The tile to test</param>
/// <returns>All and any valid neighbors</returns>
private List<Vector2> GetValidNeighbors(Vector2 centerTile)
{
List<Vector2> validNeighbors = new List<Vector2>();
//Check all four directions around the tile
foreach (var offset in offsets)
{
//find the neighbor's position
Vector2 toCheck = new Vector2(centerTile.x + offset.x, centerTile.y + offset.y);
//make sure the tile is not on both an even X-axis and an even Y-axis
//to ensure we can get walls around all tunnels
if (toCheck.x % 2 == 1 || toCheck.y % 2 == 1)
{
//if the potential neighbor is unexcavated (==1)
//and still has three walls intact (new territory)
if (Maze[(int)toCheck.x, (int)toCheck.y] == 1 HasThreeWallsIntact(toCheck))
{
//add the neighbor
validNeighbors.Add(toCheck);
}
}
}
return validNeighbors;
}
/// <summary>
/// Counts the number of intact walls around a tile
/// </summary>
/// <param name="Vector2ToCheck">The coordinates of the tile to check</param>
/// <returns>Whether there are three intact walls (the tile has not been dug into earlier.</returns>
private bool HasThreeWallsIntact(Vector2 Vector2ToCheck)
{
int intactWallCounter = 0;
//Check all four directions around the tile
foreach (var offset in offsets)
{
//find the neighbor's position
Vector2 neighborToCheck = new Vector2(Vector2ToCheck.x + offset.x, Vector2ToCheck.y + offset.y);
//make sure it is inside the maze, and it hasn't been dug out yet
if (IsInside(neighborToCheck) Maze[(int)neighborToCheck.x, (int)neighborToCheck.y] == 1)
{
intactWallCounter++;
}
}
//tell whether three walls are intact
return intactWallCounter == 3;
}
private bool IsInside(Vector2 p)
{
return p.x >= 0 p.y >= 0 p.x < width p.y < height;
}
}
Indeed it is, just put your prefab in the ‘wall’ slot in the inspector:
using UnityEngine;
using System;
using System.Collections;
using System.Collections.Generic;
public class MazeGenerator : MonoBehaviour {
public GameObject wall;
public int width, height;
private int[,] Maze;
private List<Vector3> pathMazes = new List<Vector3>();
private Stack<Vector2> _tiletoTry = new Stack<Vector2>();
private List<Vector2> offsets = new List<Vector2> { new Vector2(0, 1), new Vector2(0, -1), new Vector2(1, 0), new Vector2(-1, 0) };
private System.Random rnd = new System.Random();
private int _width, _height;
private Vector2 _currentTile;
public Vector2 CurrentTile
{
get { return _currentTile; }
private set
{
if (value.x < 1 || value.x >= this.width - 1 || value.y < 1 || value.y >= this.height - 1)
{
throw new ArgumentException("CurrentTile must be within the one tile border all around the maze");
}
if (value.x % 2 == 1 || value.y % 2 == 1)
{ _currentTile = value; }
else
{
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");
}
}
}
private static MazeGenerator instance;
public static MazeGenerator Instance
{
get
{
return instance;
}
}
void Awake()
{
instance = this;
}
void Start()
{
Camera.main.orthographic = true;
Camera.main.orthographicSize = 30;
GenerateMaze();
}
void GenerateMaze()
{
Maze = new int[width, height];
for (int x = 0; x < width; x++)
{
for (int y = 0; y < height; y++)
{
Maze[x, y] = 1;
}
}
CurrentTile = Vector2.one;
_tiletoTry.Push(CurrentTile);
Maze = CreateMaze();
GameObject ptype = null;
for (int i = 0; i <= Maze.GetUpperBound(0); i++)
{
for (int j = 0; j <= Maze.GetUpperBound(1); j++)
{
if (Maze[i, j] == 1)
{
ptype = wall;
Instantiate(wall, new Vector3(i * ptype.transform.localScale.x, j * ptype.transform.localScale.y, 0), Quaternion.identity);
}
else if (Maze[i, j] == 0)
{
pathMazes.Add(new Vector3(i, j, 0));
}
}
}
}
public int[,] CreateMaze()
{
//local variable to store neighbors to the current square
//as we work our way through the maze
List<Vector2> neighbors;
//as long as there are still tiles to try
while (_tiletoTry.Count > 0)
{
//excavate the square we are on
Maze[(int)CurrentTile.x, (int)CurrentTile.y] = 0;
//get all valid neighbors for the new tile
neighbors = GetValidNeighbors(CurrentTile);
//if there are any interesting looking neighbors
if (neighbors.Count > 0)
{
//remember this tile, by putting it on the stack
_tiletoTry.Push(CurrentTile);
//move on to a random of the neighboring tiles
CurrentTile = neighbors[rnd.Next(neighbors.Count)];
}
else
{
//if there were no neighbors to try, we are at a dead-end
//toss this tile out
//(thereby returning to a previous tile in the list to check).
CurrentTile = _tiletoTry.Pop();
}
}
return Maze;
}
/// <summary>
/// Get all the prospective neighboring tiles
/// </summary>
/// <param name="centerTile">The tile to test</param>
/// <returns>All and any valid neighbors</returns>
private List<Vector2> GetValidNeighbors(Vector2 centerTile)
{
List<Vector2> validNeighbors = new List<Vector2>();
//Check all four directions around the tile
foreach (var offset in offsets)
{
//find the neighbor's position
Vector2 toCheck = new Vector2(centerTile.x + offset.x, centerTile.y + offset.y);
//make sure the tile is not on both an even X-axis and an even Y-axis
//to ensure we can get walls around all tunnels
if (toCheck.x % 2 == 1 || toCheck.y % 2 == 1)
{
//if the potential neighbor is unexcavated (==1)
//and still has three walls intact (new territory)
if (Maze[(int)toCheck.x, (int)toCheck.y] == 1 HasThreeWallsIntact(toCheck))
{
//add the neighbor
validNeighbors.Add(toCheck);
}
}
}
return validNeighbors;
}
/// <summary>
/// Counts the number of intact walls around a tile
/// </summary>
/// <param name="Vector2ToCheck">The coordinates of the tile to check</param>
/// <returns>Whether there are three intact walls (the tile has not been dug into earlier.</returns>
private bool HasThreeWallsIntact(Vector2 Vector2ToCheck)
{
int intactWallCounter = 0;
//Check all four directions around the tile
foreach (var offset in offsets)
{
//find the neighbor's position
Vector2 neighborToCheck = new Vector2(Vector2ToCheck.x + offset.x, Vector2ToCheck.y + offset.y);
//make sure it is inside the maze, and it hasn't been dug out yet
if (IsInside(neighborToCheck) Maze[(int)neighborToCheck.x, (int)neighborToCheck.y] == 1)
{
intactWallCounter++;
}
}
//tell whether three walls are intact
return intactWallCounter == 3;
}
private bool IsInside(Vector2 p)
{
return p.x >= 0 p.y >= 0 p.x < width p.y < height;
}
}
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.
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.
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
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.
==============
// remember you can NOT have even numbers of height or width in this style of block maze
// to ensure we can get walls around all tunnels... so use 21 x 13 , or 7 x 7 for examples.
using UnityEngine;
using System;
using System.Collections;
using System.Collections.Generic;
public class MazeGenerator : MonoBehaviour {
public int width, height;
public Material brick;
private int[,] Maze;
private List<Vector3> pathMazes = new List<Vector3>();
private Stack<Vector2> _tiletoTry = new Stack<Vector2>();
private List<Vector2> offsets = new List<Vector2> { new Vector2(0, 1), new Vector2(0, -1), new Vector2(1, 0), new Vector2(-1, 0) };
private System.Random rnd = new System.Random();
private int _width, _height;
private Vector2 _currentTile;
public String MazeString;
public Vector2 CurrentTile {
get { return _currentTile; }
private set {
if (value.x < 1 || value.x >= this.width - 1 || value.y < 1 || value.y >= this.height - 1){
throw new ArgumentException("Width and Height must be greater than 2 to make a maze");
}
_currentTile = value;
}
}
private static MazeGenerator instance;
public static MazeGenerator Instance {
get {return instance;}
}
void Awake() { instance = this;}
void Start() { MakeBlocks(); }
// end of main program
// ============= subroutines ============
void MakeBlocks() {
Maze = new int[width, height];
for (int x = 0; x < width; x++) {
for (int y = 0; y < height; y++) {
Maze[x, y] = 1;
}
}
CurrentTile = Vector2.one;
_tiletoTry.Push(CurrentTile);
Maze = CreateMaze(); // generate the maze in Maze Array.
GameObject ptype = null;
for (int i = 0; i <= Maze.GetUpperBound(0); i++) {
for (int j = 0; j <= Maze.GetUpperBound(1); j++) {
if (Maze[i, j] == 1) {
MazeString=MazeString+"X"; // added to create String
ptype = GameObject.CreatePrimitive(PrimitiveType.Cube);
ptype.transform.position = new Vector3(i * ptype.transform.localScale.x, 0, j * ptype.transform.localScale.z);
if (brick != null) { ptype.renderer.material = brick; }
ptype.transform.parent = transform;
}
else if (Maze[i, j] == 0) {
MazeString=MazeString+"0"; // added to create String
pathMazes.Add(new Vector3(i, 0, j));
}
}
MazeString=MazeString+"\n"; // added to create String
}
print (MazeString); // added to create String
}
// =======================================
public int[,] CreateMaze() {
//local variable to store neighbors to the current square as we work our way through the maze
List<Vector2> neighbors;
//as long as there are still tiles to try
while (_tiletoTry.Count > 0)
{
//excavate the square we are on
Maze[(int)CurrentTile.x, (int)CurrentTile.y] = 0;
//get all valid neighbors for the new tile
neighbors = GetValidNeighbors(CurrentTile);
//if there are any interesting looking neighbors
if (neighbors.Count > 0)
{
//remember this tile, by putting it on the stack
_tiletoTry.Push(CurrentTile);
//move on to a random of the neighboring tiles
CurrentTile = neighbors[rnd.Next(neighbors.Count)];
}
else
{
//if there were no neighbors to try, we are at a dead-end toss this tile out
//(thereby returning to a previous tile in the list to check).
CurrentTile = _tiletoTry.Pop();
}
}
print("Maze Generated ...");
return Maze;
}
// ================================================
// Get all the prospective neighboring tiles "centerTile" The tile to test
// All and any valid neighbors</returns>
private List<Vector2> GetValidNeighbors(Vector2 centerTile) {
List<Vector2> validNeighbors = new List<Vector2>();
//Check all four directions around the tile
foreach (var offset in offsets) {
//find the neighbor's position
Vector2 toCheck = new Vector2(centerTile.x + offset.x, centerTile.y + offset.y);
//make sure the tile is not on both an even X-axis and an even Y-axis
//to ensure we can get walls around all tunnels
if (toCheck.x % 2 == 1 || toCheck.y % 2 == 1) {
//if the potential neighbor is unexcavated (==1)
//and still has three walls intact (new territory)
if (Maze[(int)toCheck.x, (int)toCheck.y] == 1 && HasThreeWallsIntact(toCheck)) {
//add the neighbor
validNeighbors.Add(toCheck);
}
}
}
return validNeighbors;
}
// ================================================
// Counts the number of intact walls around a tile
//"Vector2ToCheck">The coordinates of the tile to check
//Whether there are three intact walls (the tile has not been dug into earlier.
private bool HasThreeWallsIntact(Vector2 Vector2ToCheck) {
int intactWallCounter = 0;
//Check all four directions around the tile
foreach (var offset in offsets) {
//find the neighbor's position
Vector2 neighborToCheck = new Vector2(Vector2ToCheck.x + offset.x, Vector2ToCheck.y + offset.y);
//make sure it is inside the maze, and it hasn't been dug out yet
if (IsInside(neighborToCheck) && Maze[(int)neighborToCheck.x, (int)neighborToCheck.y] == 1) {
intactWallCounter++;
}
}
//tell whether three walls are intact
return intactWallCounter == 3;
}
// ================================================
private bool IsInside(Vector2 p) {
//return p.x >= 0 p.y >= 0 p.x < width p.y < height;
return p.x >= 0 && p.y >= 0 && p.x < width && p.y < height;
}
}