Created
June 1, 2022 04:17
-
-
Save devsleeper/d43e4d83b2372d7fc5060868df9c4e3a to your computer and use it in GitHub Desktop.
A (Unty) C# Conversion of the C++ Recursive Backtrack Algorithm provided by javid9x
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| using System; | |
| using System.Collections; | |
| using System.Collections.Generic; | |
| using UnityEngine; | |
| public class SimpleMazeGenerator : MonoBehaviour | |
| { | |
| public GameObject WallPrefab; | |
| public GameObject FloorPrefab; | |
| public GameObject CenterPrefab; | |
| [SerializeField] private int m_nMazeWidth = 40; | |
| [SerializeField] private int m_nMazeHeight = 25; | |
| private BitField[] m_maze; | |
| // Some bit fields for convenience | |
| [Flags] | |
| private enum BitField | |
| { | |
| CELL_PATH_N = 0x01, | |
| CELL_PATH_E = 0x02, | |
| CELL_PATH_S = 0x04, | |
| CELL_PATH_W = 0x08, | |
| CELL_VISITED = 0x10, | |
| } | |
| // Algorithm variables | |
| [SerializeField] private int m_nVisitedCells; | |
| private Stack<Tuple<int, int>> m_stack = new Stack<Tuple<int, int>>(); // (x, z) coordinate pairs | |
| [SerializeField] private int m_nPathWidth = 3; | |
| void Start() | |
| { | |
| // Maze parameters | |
| m_maze = new BitField[m_nMazeWidth * m_nMazeHeight]; | |
| // init (unnecessary) | |
| for (int i = 0; i < m_nMazeWidth * m_nMazeHeight; i++) | |
| m_maze[i] = 0x00; | |
| m_nPathWidth = 3; | |
| // Choose a starting cell | |
| int x = UnityEngine.Random.Seed.Next() % m_nMazeWidth; | |
| int y = UnityEngine.Random.Seed.Next() % m_nMazeHeight; | |
| m_stack.Push(Tuple.Create(x, y)); | |
| m_maze[y * m_nMazeWidth + x] = BitField.CELL_VISITED; | |
| m_nVisitedCells = 1; | |
| //StartCoroutine(nameof(MazeGeneration)); | |
| MazeGeneration(); | |
| } | |
| private bool Initialized; | |
| // Called by olcConsoleGameEngine | |
| //IEnumerator MazeGeneration() | |
| void MazeGeneration() | |
| { | |
| while (m_nVisitedCells < m_nMazeWidth * m_nMazeHeight) | |
| { | |
| // Little lambda function to calculate index of a neighbor in a readable way | |
| Func<int, int, int> offset = (int x, int y) => | |
| { | |
| return (m_stack.Peek().Item2 + y) * m_nMazeWidth + (m_stack.Peek().Item1 + x); | |
| }; | |
| // Do Maze Algorithm | |
| if (m_nVisitedCells < m_nMazeWidth * m_nMazeHeight) | |
| { | |
| // Create a set of unvisted neighbours | |
| List<int> neighbours = new List<int>(); | |
| // North neighbour | |
| if (m_stack.Peek().Item2 > 0 && (m_maze[offset(0, -1)] & BitField.CELL_VISITED) == 0) | |
| { | |
| neighbours.Add(0); | |
| } | |
| // East neighbour | |
| if (m_stack.Peek().Item1 < m_nMazeWidth - 1 && (m_maze[offset(1, 0)] & BitField.CELL_VISITED) == 0) | |
| { | |
| neighbours.Add(1); | |
| } | |
| // South neighbour | |
| if (m_stack.Peek().Item2 < m_nMazeHeight - 1 && (m_maze[offset(0, 1)] & BitField.CELL_VISITED) == 0) | |
| { | |
| neighbours.Add(2); | |
| } | |
| // West neighbour | |
| if (m_stack.Peek().Item1 > 0 && (m_maze[offset(-1, 0)] & BitField.CELL_VISITED) == 0) | |
| { | |
| neighbours.Add(3); | |
| } | |
| // Are there any neighbours available? | |
| if (neighbours.Count > 0) | |
| { | |
| // Choose one available neighbour at random | |
| int next_cell_dir = neighbours[UnityEngine.Random.Seed.Next() % neighbours.Count]; | |
| // Create a path between the neighbour and the current cell | |
| switch (next_cell_dir) | |
| { | |
| case 0: // North | |
| m_maze[offset(0, -1)] |= BitField.CELL_VISITED | BitField.CELL_PATH_S; | |
| m_maze[offset(0, 0)] |= BitField.CELL_PATH_N; | |
| m_stack.Push(Tuple.Create((m_stack.Peek().Item1 + 0), (m_stack.Peek().Item2 - 1))); | |
| break; | |
| case 1: // East | |
| m_maze[offset(+1, 0)] |= BitField.CELL_VISITED | BitField.CELL_PATH_W; | |
| m_maze[offset(0, 0)] |= BitField.CELL_PATH_E; | |
| m_stack.Push(Tuple.Create((m_stack.Peek().Item1 + 1), (m_stack.Peek().Item2 + 0))); | |
| break; | |
| case 2: // South | |
| m_maze[offset(0, +1)] |= BitField.CELL_VISITED | BitField.CELL_PATH_N; | |
| m_maze[offset(0, 0)] |= BitField.CELL_PATH_S; | |
| m_stack.Push(Tuple.Create((m_stack.Peek().Item1 + 0), (m_stack.Peek().Item2 + 1))); | |
| break; | |
| case 3: // West | |
| m_maze[offset(-1, 0)] |= BitField.CELL_VISITED | BitField.CELL_PATH_E; | |
| m_maze[offset(0, 0)] |= BitField.CELL_PATH_W; | |
| m_stack.Push(Tuple.Create((m_stack.Peek().Item1 - 1), (m_stack.Peek().Item2 + 0))); | |
| break; | |
| } | |
| m_nVisitedCells++; | |
| } | |
| else | |
| { | |
| // No available neighbours so backtrack! | |
| m_stack.Pop(); | |
| } | |
| } | |
| } | |
| Initialized = true; | |
| DrawMaze(); | |
| } | |
| // === DRAWING STUFF === | |
| void DrawMaze() | |
| { | |
| // Draw Maze | |
| for (int x = 0; x < m_nMazeWidth; x++) | |
| { | |
| for (int y = 0; y < m_nMazeHeight; y++) | |
| { | |
| // Each cell is inflated by m_nPathWidth, so fill it in | |
| for (int py = 0; py < m_nPathWidth; py++) | |
| { | |
| for (int px = 0; px < m_nPathWidth; px++) | |
| { | |
| if (m_maze[y * m_nMazeWidth + x].HasFlag(BitField.CELL_VISITED)) | |
| { | |
| if ((py + px) % 2 == 0) // even cell! | |
| Instantiate(CenterPrefab, new Vector3(x * (m_nPathWidth + 1) + px, 0, y * (m_nPathWidth + 1) + py), CenterPrefab.transform.localRotation); | |
| else | |
| Instantiate(FloorPrefab, new Vector3(x * (m_nPathWidth + 1) + px, 0, y * (m_nPathWidth + 1) + py), FloorPrefab.transform.localRotation); | |
| //Draw(x * (m_nPathWidth + 1) + px, y * (m_nPathWidth + 1) + py, PIXEL_SOLID, FG_WHITE); // Draw Cell | |
| } | |
| else | |
| { | |
| Instantiate(FloorPrefab, new Vector3(x * (m_nPathWidth + 1) + px, 0, y * (m_nPathWidth + 1) + py), FloorPrefab.transform.localRotation); | |
| //Draw(x * (m_nPathWidth + 1) + px, y * (m_nPathWidth + 1) + py, PIXEL_SOLID, FG_BLUE); // Draw Cell | |
| } | |
| } | |
| } | |
| // Draw passageways between cells | |
| for (int p = 0; p < m_nPathWidth; p++) | |
| { | |
| if (m_maze[y * m_nMazeWidth + x].HasFlag(BitField.CELL_PATH_S)) | |
| { | |
| Instantiate(WallPrefab, new Vector3(x * (m_nPathWidth + 1) + p, 1, y * (m_nPathWidth + 1) + m_nPathWidth), WallPrefab.transform.localRotation); | |
| //Draw(x * (m_nPathWidth + 1) + p, y * (m_nPathWidth + 1) + m_nPathWidth); // Draw South Passage | |
| } | |
| else if (m_maze[y * m_nMazeWidth + x].HasFlag(BitField.CELL_PATH_E) == false) | |
| Instantiate(FloorPrefab, new Vector3(x * (m_nPathWidth + 1) + p, 0, y * (m_nPathWidth + 1) + m_nPathWidth), FloorPrefab.transform.localRotation); | |
| if (m_maze[y * m_nMazeWidth + x].HasFlag(BitField.CELL_PATH_E)) | |
| { | |
| Instantiate(WallPrefab, new Vector3(x * (m_nPathWidth + 1) + m_nPathWidth, 1, y * (m_nPathWidth + 1) + p), WallPrefab.transform.localRotation); | |
| //Draw(x * (m_nPathWidth + 1) + m_nPathWidth, y * (m_nPathWidth + 1) + p); // Draw East Passage | |
| } | |
| else if (m_maze[y * m_nMazeWidth + x].HasFlag(BitField.CELL_PATH_S) == false) | |
| Instantiate(FloorPrefab, new Vector3(x * (m_nPathWidth + 1) + m_nPathWidth, 0, y * (m_nPathWidth + 1) + p), FloorPrefab.transform.localRotation); | |
| } | |
| } | |
| } | |
| } | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment