PageRenderTime 80ms CodeModel.GetById 17ms RepoModel.GetById 1ms app.codeStats 0ms

/Assets/Scripts/MapGenerator.cs

https://gitlab.com/Space-Shark/cave-search-guy
C# | 512 lines | 422 code | 88 blank | 2 comment | 125 complexity | 7e93cf8824334e62aada9837023bf058 MD5 | raw file
  1. using UnityEngine;
  2. using System.Collections;
  3. using System.Collections.Generic;
  4. using System;
  5. public class MapGenerator : MonoBehaviour {
  6. public bool autoCamera = true;
  7. public PlayerController2D player2D;
  8. public PlayerController3D player3D;
  9. public CameraController2D camera2D;
  10. public CameraController3D camera3D;
  11. public bool is2D = false;
  12. private float baseOrthoCameraY = 12f;
  13. private float baseOrthoCameraZ = -4f;
  14. private float baseOrthoCameraSize = 36f;
  15. private MeshGenerator meshGen;
  16. private int baseWidth = 128;
  17. private int baseHeight = 72;
  18. private int baseBorderSize = 10;
  19. public int width = 128;
  20. public int height = 72;
  21. public int borderSize = 10;
  22. public int wallThreshold = 50;
  23. public int roomThreshold = 50;
  24. public int passageWidth = 1;
  25. public string seed;
  26. public bool useRandomSeed;
  27. [Range(0,100)]
  28. public int randomFillPercent;
  29. int[,] map;
  30. void Awake() {
  31. if (player2D == null)
  32. player2D = FindObjectOfType<PlayerController2D> ();
  33. if (player3D == null)
  34. player3D = FindObjectOfType<PlayerController3D> ();
  35. if (camera2D == null)
  36. camera2D = FindObjectOfType<CameraController2D> ();
  37. if (camera3D == null)
  38. camera3D = FindObjectOfType<CameraController3D> ();
  39. meshGen = GetComponent<MeshGenerator> ();
  40. }
  41. void Start() {
  42. SceneInit ();
  43. GenerateMap ();
  44. }
  45. void Update() {
  46. if (Input.GetMouseButtonDown(0) || Input.GetButtonDown("A")) {
  47. GenerateMap ();
  48. }
  49. if (Input.GetMouseButton(1) || Input.GetButton("B")) {
  50. SmoothMap ();
  51. ProcessMap ();
  52. GenerateMesh ();
  53. }
  54. if (Input.GetKeyDown (KeyCode.LeftControl) || Input.GetButtonDown("X")) {
  55. is2D = !is2D;
  56. SwapPlayer ();
  57. SceneInit ();
  58. GenerateMesh ();
  59. }
  60. if (Input.GetKeyDown (KeyCode.LeftAlt) || Input.GetButtonDown ("Y")) {
  61. autoCamera = !autoCamera;
  62. SceneInit ();
  63. }
  64. }
  65. void SwapPlayer() {
  66. // It's now 2D
  67. if (is2D) {
  68. player2D.transform.position = new Vector3 (player3D.transform.position.x, player3D.transform.position.z, 0);
  69. } else {
  70. player3D.transform.position = new Vector3 (player2D.transform.position.x, player3D.transform.position.y, player2D.transform.position.y);
  71. }
  72. }
  73. void SceneInit() {
  74. if (is2D) {
  75. player2D.TurnOn ();
  76. player3D.TurnOff ();
  77. camera2D.TurnOn ();
  78. camera3D.TurnOff ();
  79. } else {
  80. player2D.TurnOff ();
  81. player3D.TurnOn ();
  82. camera2D.TurnOff ();
  83. camera3D.TurnOn ();
  84. }
  85. camera2D.autoCamera = autoCamera;
  86. camera3D.autoCamera = autoCamera;
  87. Camera camera = Camera.main;
  88. if (autoCamera) {
  89. float scaledWidth = (float)width / baseWidth;
  90. float scaledHeight = (float)height / baseHeight;
  91. float scale = Mathf.Max (scaledWidth, scaledHeight);
  92. camera.orthographic = true;
  93. camera.orthographicSize = baseOrthoCameraSize * scale;
  94. camera.transform.rotation = Quaternion.identity;
  95. if (is2D) {
  96. camera.transform.position = new Vector3 (0, 0, -20);
  97. } else {
  98. camera.transform.position = new Vector3 (0, baseOrthoCameraY * scale, baseOrthoCameraZ * scale);
  99. camera.transform.Rotate (new Vector3 (75, 0, 0));
  100. }
  101. if (scaledWidth > scaledHeight) {
  102. borderSize = Mathf.RoundToInt ((baseHeight * scaledWidth - height) / 2) + baseBorderSize;
  103. } else {
  104. borderSize = Mathf.RoundToInt ((baseWidth * scaledHeight - width) / 2) + baseBorderSize;
  105. }
  106. } else {
  107. if (is2D) {
  108. camera2D.SetupCamera ();
  109. } else {
  110. camera.orthographic = false;
  111. camera3D.SetupCamera ();
  112. }
  113. }
  114. }
  115. void GenerateMap() {
  116. map = new int[width, height];
  117. RandomFillMap ();
  118. for (int i = 0; i < 3; i++) {
  119. SmoothMap ();
  120. }
  121. ProcessMap ();
  122. GenerateMesh ();
  123. }
  124. void GenerateMesh() {
  125. int[,] borderedMap = new int[width + borderSize * 2, height + borderSize * 2];
  126. for (int x = 0; x < borderedMap.GetLength(0); x++) {
  127. for (int y = 0; y < borderedMap.GetLength(1); y++) {
  128. if (x >= borderSize && x < width + borderSize && y >= borderSize && y < height + borderSize) {
  129. borderedMap [x, y] = map [x - borderSize, y - borderSize];
  130. } else {
  131. borderedMap [x, y] = 1;
  132. }
  133. }
  134. }
  135. meshGen.is2D = is2D;
  136. meshGen.GenerateMesh (borderedMap, 1);
  137. }
  138. //
  139. void ProcessMap() {
  140. List<List<Coord>> wallRegions = GetRegions (1);
  141. foreach (List<Coord> wallRegion in wallRegions) {
  142. if (wallRegion.Count < wallThreshold) {
  143. foreach (Coord tile in wallRegion) {
  144. map [tile.tileX, tile.tileY] = 0;
  145. }
  146. }
  147. }
  148. List<List<Coord>> roomRegions = GetRegions (0);
  149. List<Room> survivingRooms = new List<Room> ();
  150. foreach (List<Coord> roomRegion in roomRegions) {
  151. if (roomRegion.Count < roomThreshold) {
  152. foreach (Coord tile in roomRegion) {
  153. map [tile.tileX, tile.tileY] = 1;
  154. }
  155. } else {
  156. survivingRooms.Add (new Room (roomRegion, map));
  157. }
  158. }
  159. survivingRooms.Sort ();
  160. survivingRooms [0].isMainRoom = true;
  161. survivingRooms [0].isAccessibleFromMainRoom = true;
  162. ConnectClosesRooms (survivingRooms);
  163. }
  164. void ConnectClosesRooms(List<Room> allRooms, bool forceAccessibilityFromMainRoom = false) {
  165. List<Room> roomListA = new List<Room> ();
  166. List<Room> roomListB = new List<Room> ();
  167. if (forceAccessibilityFromMainRoom) {
  168. foreach (Room room in allRooms) {
  169. if (room.isAccessibleFromMainRoom) {
  170. roomListB.Add (room);
  171. } else {
  172. roomListA.Add (room);
  173. }
  174. }
  175. } else {
  176. roomListA = allRooms;
  177. roomListB = allRooms;
  178. }
  179. int bestDistance = 0;
  180. Coord bestTileA = new Coord ();
  181. Coord bestTileB = new Coord ();
  182. Room bestRoomA = new Room ();
  183. Room bestRoomB = new Room ();
  184. bool possibleConnectionFound = false;
  185. foreach (Room roomA in roomListA) {
  186. if (!forceAccessibilityFromMainRoom) {
  187. possibleConnectionFound = false;
  188. if (roomA.connectedRooms.Count > 0) {
  189. continue;
  190. }
  191. }
  192. foreach (Room roomB in roomListB) {
  193. if (roomA == roomB || roomA.IsConnected(roomB))
  194. continue;
  195. for (int tileIndexA = 0; tileIndexA < roomA.edgeTiles.Count; tileIndexA++) {
  196. for (int tileIndexB = 0; tileIndexB < roomB.edgeTiles.Count; tileIndexB++) {
  197. Coord tileA = roomA.edgeTiles [tileIndexA];
  198. Coord tileB = roomB.edgeTiles [tileIndexB];
  199. int distanceBetweenRooms = (int) (Mathf.Pow (tileA.tileX - tileB.tileX, 2) + Mathf.Pow (tileA.tileY - tileB.tileY, 2));
  200. if (distanceBetweenRooms < bestDistance || !possibleConnectionFound) {
  201. bestDistance = distanceBetweenRooms;
  202. possibleConnectionFound = true;
  203. bestTileA = tileA;
  204. bestTileB = tileB;
  205. bestRoomA = roomA;
  206. bestRoomB = roomB;
  207. }
  208. }
  209. }
  210. }
  211. if (possibleConnectionFound && !forceAccessibilityFromMainRoom) {
  212. CreatePassage (bestRoomA, bestRoomB, bestTileA, bestTileB);
  213. }
  214. }
  215. if (possibleConnectionFound && forceAccessibilityFromMainRoom) {
  216. CreatePassage (bestRoomA, bestRoomB, bestTileA, bestTileB);
  217. ConnectClosesRooms (allRooms, true);
  218. }
  219. if (!forceAccessibilityFromMainRoom) {
  220. ConnectClosesRooms (allRooms, true);
  221. }
  222. }
  223. void CreatePassage(Room roomA, Room roomB, Coord tileA, Coord tileB) {
  224. Room.ConnectRooms (roomA, roomB);
  225. List<Coord> line = GetLine (tileA, tileB);
  226. foreach (Coord c in line) {
  227. DrawCircle (c, passageWidth);
  228. }
  229. }
  230. void DrawCircle(Coord c, int r) {
  231. for (int x = -r; x <= r; x++) {
  232. for (int y = -r; y <= r; y++) {
  233. if (x * x + y * y <= r * r) {
  234. int drawX = c.tileX + x;
  235. int drawY = c.tileY + y;
  236. if(IsInMapRange(drawX, drawY)) {
  237. map[drawX,drawY] = 0;
  238. }
  239. }
  240. }
  241. }
  242. }
  243. List<Coord> GetLine(Coord from, Coord to) {
  244. List<Coord> line = new List<Coord> ();
  245. int x = from.tileX;
  246. int y = from.tileY;
  247. int dx = to.tileX - from.tileX;
  248. int dy = to.tileY - from.tileY;
  249. bool inverted = false;
  250. int step = Math.Sign (dx);
  251. int gradientStep = Math.Sign (dy);
  252. int longest = Mathf.Abs (dx);
  253. int shortest = Mathf.Abs (dy);
  254. if (longest < shortest) {
  255. inverted = true;
  256. longest = Mathf.Abs (dy);
  257. shortest = Mathf.Abs (dx);
  258. step = Math.Sign (dy);
  259. gradientStep = Math.Sign (dx);
  260. }
  261. int gradientAccumulation = longest / 2;
  262. for (int i = 0; i < longest; i++) {
  263. line.Add (new Coord (x, y));
  264. if (inverted) {
  265. y += step;
  266. } else {
  267. x += step;
  268. }
  269. gradientAccumulation += shortest;
  270. if (gradientAccumulation >= longest) {
  271. if (inverted) {
  272. x += gradientStep;
  273. } else {
  274. y += gradientStep;
  275. }
  276. gradientAccumulation -= longest;
  277. }
  278. }
  279. return line;
  280. }
  281. Vector3 CoordToWorldPoint(Coord tile) {
  282. return new Vector3 (-width / 2 + 0.5f + tile.tileX, 2, -height / 2 + 0.5f + tile.tileY);
  283. }
  284. List<List<Coord>> GetRegions(int tileType) {
  285. List<List<Coord>> regions = new List<List<Coord>> ();
  286. int[,] mapFlags = new int[width, height];
  287. for (int x = 0; x < width; x++) {
  288. for (int y = 0; y < height; y++) {
  289. if (mapFlags [x, y] == 0 && map [x, y] == tileType) {
  290. List<Coord> newRegion = GetRegionTiles (x, y);
  291. regions.Add (newRegion);
  292. foreach (Coord tile in newRegion) {
  293. mapFlags [tile.tileX, tile.tileY] = 1;
  294. }
  295. }
  296. }
  297. }
  298. return regions;
  299. }
  300. List<Coord> GetRegionTiles (int startX, int startY) {
  301. List<Coord> tiles = new List<Coord> ();
  302. int[,] mapFlags = new int[width, height];
  303. int tileType = map [startX, startY];
  304. Queue<Coord> queue = new Queue<Coord> ();
  305. queue.Enqueue (new Coord (startX, startY));
  306. mapFlags [startX, startY] = 1;
  307. while (queue.Count > 0) {
  308. Coord tile = queue.Dequeue ();
  309. tiles.Add (tile);
  310. for (int x = tile.tileX - 1; x <= tile.tileX + 1; x++) {
  311. for (int y = tile.tileY - 1; y <= tile.tileY + 1; y++) {
  312. if (IsInMapRange (x, y) && (y == tile.tileY || x == tile.tileX)) {
  313. if (mapFlags [x, y] == 0 && map [x, y] == tileType) {
  314. mapFlags [x, y] = 1;
  315. queue.Enqueue (new Coord (x, y));
  316. }
  317. }
  318. }
  319. }
  320. }
  321. return tiles;
  322. }
  323. bool IsInMapRange(int x, int y) {
  324. return x >= 0 && x < width && y >= 0 && y < height;
  325. }
  326. void RandomFillMap() {
  327. if (useRandomSeed) {
  328. seed = Time.time.ToString ();
  329. }
  330. System.Random rngod = new System.Random (seed.GetHashCode ());
  331. for (int x = 0; x < width; x++) {
  332. for (int y = 0; y < height; y++) {
  333. if (x == 0 || x == width - 1 || y == 0 || y == height - 1)
  334. map [x, y] = 1;
  335. else
  336. map [x, y] = (rngod.Next (0, 100) < randomFillPercent) ? 1 : 0;
  337. }
  338. }
  339. }
  340. void SmoothMap() {
  341. for (int x = 0; x < width; x++) {
  342. for (int y = 0; y < height; y++) {
  343. int neighborWallCount = GetSurroundingWallCount (x, y);
  344. if (neighborWallCount > 4)
  345. map [x, y] = 1;
  346. else if (neighborWallCount < 4)
  347. map [x, y] = 0;
  348. }
  349. }
  350. }
  351. int GetSurroundingWallCount(int x, int y) {
  352. int wallCount = 0;
  353. for (int i = x - 1; i <= x + 1; i++) {
  354. for (int j = y - 1; j <= y + 1; j++) {
  355. if (IsInMapRange(i,j)) {
  356. if (i != x || j != y) {
  357. wallCount += map [i, j];
  358. }
  359. } else {
  360. wallCount++;
  361. }
  362. }
  363. }
  364. return wallCount;
  365. }
  366. struct Coord {
  367. public int tileX;
  368. public int tileY;
  369. public Coord(int x, int y) {
  370. tileX = x;
  371. tileY = y;
  372. }
  373. }
  374. class Room : IComparable<Room> {
  375. public List<Coord> tiles;
  376. public List<Coord> edgeTiles;
  377. public List<Room> connectedRooms;
  378. public int roomSize;
  379. public bool isAccessibleFromMainRoom;
  380. public bool isMainRoom;
  381. public Room() {
  382. }
  383. public Room(List<Coord> roomTiles, int[,] map) {
  384. tiles = roomTiles;
  385. roomSize = tiles.Count;
  386. connectedRooms = new List<Room>();
  387. edgeTiles = new List<Coord>();
  388. foreach(Coord tile in tiles) {
  389. for(int x = tile.tileX - 1 ; x <= tile.tileX + 1 ; x++) {
  390. for(int y = tile.tileY - 1 ; y <= tile.tileY + 1; y++) {
  391. if(x == tile.tileX || y == tile.tileY) {
  392. if(map[x,y] == 1) {
  393. edgeTiles.Add(tile);
  394. }
  395. }
  396. }
  397. }
  398. }
  399. }
  400. public void SetAccessibleFromMainRoom() {
  401. if (!isAccessibleFromMainRoom) {
  402. isAccessibleFromMainRoom = true;
  403. foreach (Room connectedRoom in connectedRooms) {
  404. connectedRoom.SetAccessibleFromMainRoom ();
  405. }
  406. }
  407. }
  408. public static void ConnectRooms(Room roomA, Room roomB) {
  409. if (roomA.isAccessibleFromMainRoom) {
  410. roomB.SetAccessibleFromMainRoom ();
  411. } else if (roomB.isAccessibleFromMainRoom) {
  412. roomA.SetAccessibleFromMainRoom ();
  413. }
  414. roomA.connectedRooms.Add (roomB);
  415. roomB.connectedRooms.Add (roomA);
  416. }
  417. public bool IsConnected(Room otherRoom) {
  418. return connectedRooms.Contains (otherRoom);
  419. }
  420. public int CompareTo(Room otherRoom) {
  421. return otherRoom.roomSize.CompareTo (roomSize);
  422. }
  423. }
  424. }