之前看到了《房间和迷宫:一个地牢生成算法》,于是自己尝试着做了一下Unity的实现,同时将自己的理解记录下来。
主要代码在 RandomRoom脚本中
using System.Collections; using System.Collections.Generic; using UnityEngine; public class RandomRoom : MonoBehaviour { //地图大小 public int height = 128; public int width = 128; //房间大小 public int roomMaxSize = 5; //预制引用 public GameObject wallPrefab; public GameObject floorPrefab; public GameObject roomPrefab; public GameObject opendoorPrefab; public GameObject closedoorPrefab; //生成房间次数 public int createTimes = 100; //当前区域数量 private int currentRegionNum = -1; //记录区域数组 private int[,] regions; //记录房间数组 private Listrooms; //记录边缘数组 private List bounds; //地图数据 private int[,] mapArray; //tile类型 private enum tileType { floor, wall, room, opendoor, closedoor }; //4个基本方向 private Vector2[] baseDirection = { new Vector2(-1, 0), new Vector2(1, 0), new Vector2(0, -1), new Vector2(0, 1) }; //迷宫父物体 private GameObject mazeParent; //生成迷宫 public void generate () { creatBoard (mapArray); for (int i = 0; i < createTimes; i++) { addRooms (); } growMaze (new Vector2 (1, 1)); connectRegions (); removeDeadEnds (); drawMaze (); //Debug.Log(1); } //增加房间 public void addRooms () { Room room = CreateRoom (); bool overLap = false; if (room.X2 >= width || room.Y2 >= width) { overLap = true; } if (room.X1 >= width || room.Y1 >= width) { overLap = true; } for (int i = 0; i < rooms.Count; i++) { if (room.inAnotherRoom (rooms [i])) { overLap = true; break; } } if (!overLap) { rooms.Add (room); currentRegionNum++; fillRoom (room); } } //初始化迷宫 public void creatBoard (int[,] map) { for (int i = 0; i < width; i++) { for (int j = 0; j < height; j++) { map [i, j] = (int)tileType.wall; if (i == -1 || j == -1 || i == width || j == height) { bounds.Add (new Vector2 (i, j)); //map[i, j] = 0; } } } } //生成过道 public void growMaze (Vector2 start) { List cells = new List (); List unmadeCells = new List (); Vector2 cell; _startRegion (); _carve (start, tileType.floor); cells.Add (start); while (cells.Count > 0) { cell = cells [cells.Count - 1]; unmadeCells = new List (); foreach (Vector2 dir in baseDirection) { if (_canCarve (cell, dir)) unmadeCells.Add (dir); } if (unmadeCells.Count > 0) { Vector2 dir; //Debug.Log(unmadeCells[0]); dir = unmadeCells [Random.Range (0, unmadeCells.Count)]; _carve (cell + dir, tileType.floor); _carve (cell + dir * 2, tileType.floor); cells.Add (cell + dir * 2); } else { cells.RemoveAt (cells.Count - 1); } } } bool _canCarve(Vector2 pos, Vector2 direction) { // Must end in bounds. if (bounds.Contains (pos + direction * 3)) return false; // Destination must not be open. Vector2 temppos = pos + direction * 2; if (temppos.x > 0 && temppos.x < width - 1 && temppos.y > 0 & temppos.y < height - 1) { return mapArray [(int)temppos.x, (int)temppos.y] == (int)tileType.wall; } else { return false; } } void _carve (Vector2 pos, tileType type) { mapArray [(int)pos.x, (int)pos.y] = (int)type; regions [(int)pos.x, (int)pos.y] = currentRegionNum; } void _startRegion () { currentRegionNum++; } //连接区域 public void connectRegions () { //找出连接多个区域的连接点 List [] connectors = new List [currentRegionNum]; for (int k = 1; k < currentRegionNum; k++) { connectors [k] = new List (); for (int i = 1; i < width - 1; i++) { for (int j = 1; j < height - 1; j++) { if (roundWallCount (i, j) > 2) continue; if (mapArray [i, j] != (int)tileType.wall) continue; if (regions [i, j] == k) { if (regions [i + 1, j] != regions [i, j] && mapArray [i + 1, j] != (int)tileType.wall) { connectors [k].Add (new Vector2 (i, j)); continue; } if (regions [i - 1, j] != regions [i, j] && mapArray [i - 1, j] != (int)tileType.wall) { connectors [k].Add (new Vector2 (i, j)); continue; } if (regions [i, j + 1] != regions [i, j] && mapArray [i, j + 1] != (int)tileType.wall) { connectors [k].Add (new Vector2 (i, j)); continue; } if (regions [i, j - 1] != regions [i, j] && mapArray [i, j - 1] != (int)tileType.wall) { connectors [k].Add (new Vector2 (i, j)); continue; } } } } Vector2 temppos = new Vector2 (); if (connectors [k].Count > 0) { temppos = connectors [k] [Random.Range (0, connectors [k].Count - 1)]; mapArray [(int)temppos.x, (int)temppos.y] = (int)tileType.closedoor; } } } //删除死胡同 public void removeDeadEnds () { //查找所有路的集合 List floors = new List (); for (int i = 1; i < width - 1; i++) { for (int j = 1; j < height - 1; j++) { if (mapArray [i, j] == (int)tileType.floor) { floors.Add (new Vector2 (i, j)); } } } while (true) { bool noDeadLoad = true; foreach (Vector2 floor in floors) { if (roundWallCount ((int)floor.x, (int)floor.y) > 2) { floors.Remove (floor); mapArray [(int)floor.x, (int)floor.y] = (int)tileType.wall; noDeadLoad = false; break; } } if (noDeadLoad) break; } } //查找死胡同 public void findDeadLoad(List floors) { } //计算周边墙的数量 public int roundWallCount (int i, int j) { int roundwallcount = 0; if (mapArray [i + 1, j] == (int)tileType.wall) roundwallcount++; if (mapArray [i - 1, j] == (int)tileType.wall) roundwallcount++; if (mapArray [i, j + 1] == (int)tileType.wall) roundwallcount++; if (mapArray [i, j - 1] == (int)tileType.wall) roundwallcount++; return roundwallcount; } //增加连接点 public void addJunction() { } //查找连接多区域的连接点 public void findConnector() { } //填充房间 public void fillRoom (Room room) { for (int i = room.X1; i < room.X2; i++) { for (int j = room.Y1; j < room.Y2; j++) { mapArray [i, j] = room.roomData [i - room.X1, j - room.Y1]; regions [i, j] = currentRegionNum; bounds.Add (new Vector2 (i, j)); } } } //创建随机大小房间 public Room CreateRoom () { int roomHeight = Random.Range (2, roomMaxSize) + 2; int roomWidth = Random.Range (2, roomMaxSize) + 2; int x = Random.Range (1, width / 2) * 2; int y = Random.Range (1, height / 2) * 2; Room room = new Room (x, y, x + roomWidth, y + roomHeight); return room; } //绘制迷宫 public void drawMaze() { mazeParent = new GameObject (); mazeParent.name = "Maze"; for (int i = 0; i < width; i++) { //string log = ""; for (int j = 0; j < height; j++) { //log += mapArray[i, j]; if (mapArray [i, j] == (int)tileType.floor) { GameObject go = Instantiate (floorPrefab, new Vector3 (i, j, 0), Quaternion.identity) as GameObject; go.transform.SetParent (mazeParent.transform); } else if (mapArray [i, j] == (int)tileType.wall) { GameObject go = Instantiate (wallPrefab, new Vector3 (i, j, 0), Quaternion.identity) as GameObject; go.transform.SetParent (mazeParent.transform); } else if (mapArray [i, j] == (int)tileType.room) { GameObject go = Instantiate (roomPrefab, new Vector3 (i, j, 0), Quaternion.identity) as GameObject; go.transform.SetParent (mazeParent.transform); } else if (mapArray [i, j] == (int)tileType.opendoor) { GameObject go = Instantiate (opendoorPrefab, new Vector3 (i, j, 0), Quaternion.identity) as GameObject; go.transform.SetParent (mazeParent.transform); } else if (mapArray [i, j] == (int)tileType.closedoor) { GameObject go = Instantiate (closedoorPrefab, new Vector3 (i, j, 0), Quaternion.identity) as GameObject; go.transform.SetParent (mazeParent.transform); } } //Debug.Log(log); } } // Use this for initialization void Start() { mapArray = new int[height, width]; mazeParent = new GameObject (); mazeParent.name = "Maze"; rooms = new List (); bounds = new List (); regions = new int[height, width]; // creatBoard(mapArray); //drawMaze(); generate (); } // Update is called once per frame void Update () { if (Input.GetKeyDown (KeyCode.R)) { ReCreateMap (); } } private void ReCreateMap () { mapArray = new int[height, width]; bounds = new List (); regions = new int[height, width]; GameObject.Destroy (mazeParent); Destroy (GameObject.Find ("Maze")); mazeParent = new GameObject (); mazeParent.name = "Maze"; rooms = new List (); generate (); } //计算周围墙的数量 //参数t为计算几圈 int CheckNeighborWalls (int[,] array, int i, int j, int t) { int count = 0; for (int i2 = i - t; i2 < i + t + 1; i2++) { for (int j2 = j - t; j2 < j + t + 1; j2++) { if (i2 > 0 && i2 < width && j2 >= 0 && j2 < height) { if (array [i2, j2] == (int)tileType.wall) { count++; } } } } if (array [i, j] == (int)tileType.wall) count--;//因为上面的循环包含了自身,属于要将自身减掉 return count; } } using System; using System.Collections.Generic; using System.Linq; using System.Text; public class Room { private enum tileType { floor, wall, room }; private enum RoomType { empty, bedroom, classroom }; public int X1, Y1, X2, Y2; public int roomType=(int)RoomType.empty; public int[,] roomData; public Room(int x1, int y1, int x2, int y2) { X1 = x1; Y1 = y1; X2 = x2; Y2 = y2; roomData = new int[X2-X1,Y2-Y1]; CreateRoomData(roomType); } public void CreateRoomData (int roomType) { switch (roomType) { case (int)RoomType.empty: for (int i = 0; i < X2 - X1; i++) { for (int j = 0; j < Y2 - Y1; j++) { if (i == 0 || i == X2 - X1 - 1 || j == 0 || j == Y2 - Y1 - 1) { roomData [i, j] = (int)tileType.wall; } else { roomData [i, j] = (int)tileType.room; } //roomData[i, j] = (int)tileType.room; } } break; default: for (int i = 0; i < X2 - X1; i++) { for (int j = 0; j < Y2 - Y1; j++) { if (i == 0 || i == X2 - X1 - 1 || j == 0 || j == Y2 - Y1 - 1) { roomData [i, j] = (int)tileType.wall; } else { roomData [i, j] = (int)tileType.room; } //roomData[i, j] = (int)tileType.room; } } break; } } public bool outOfMap(int[,] map) { return false; } public bool inAnotherRoom (Room other) { if (X1 >= other.X1 && X1 <= other.X2 && Y1 >= other.Y1 && Y1 <= other.Y2) { return true; } if (X2 <= other.X2 && X2 >= other.X1 && Y2 <= other.Y2 && Y2 >= other.Y1) { return true; } if (X1 >= other.X1 && X1 <= other.X2 && Y2 >= other.Y1 && Y2 <= other.Y2) { return true; } if (X2 <= other.X2 && X2 >= other.X1 && Y1 <= other.Y2 && Y1 >= other.Y1) { return true; } return false; } }
暂无关于此日志的评论。