Newer
Older
DungeonShooting / DungeonShooting_Godot / src / framework / generate / GenerateDungeon.cs
  1.  
  2. using System.Collections.Generic;
  3. using Godot;
  4.  
  5. /// <summary>
  6. /// 地牢生成器
  7. /// </summary>
  8. public class GenerateDungeon
  9. {
  10. public readonly TileMap TileMap;
  11.  
  12. public RoomInfo StartRoom;
  13.  
  14. public readonly Grid<bool> RoomGrid = new Grid<bool>();
  15.  
  16. private List<RoomInfo> _roomInfos = new List<RoomInfo>();
  17.  
  18. private int _count = 0;
  19.  
  20. /// <summary>
  21. /// 生成的房间数量
  22. /// </summary>
  23. private int _maxCount = 15;
  24.  
  25. /// <summary>
  26. /// 过道宽度
  27. /// </summary>
  28. private int _corridorWidth = 4;
  29.  
  30. //宽高
  31. private int _roomMinWidth = 20;
  32. private int _roomMaxWidth = 45;
  33. private int _roomMinHeight = 20;
  34.  
  35. private int _roomMaxHeight = 35;
  36.  
  37. //间隔
  38. private int _roomMinInterval = 6;
  39. private int _roomMaxInterval = 10;
  40.  
  41. //房间横轴分散程度
  42. private float _roomHorizontalMinDispersion = 0.7f;
  43.  
  44. private float _roomHorizontalMaxDispersion = 1.1f;
  45.  
  46. //房间纵轴分散程度
  47. private float _roomVerticalMinDispersion = 0.7f;
  48. private float _roomVerticalMaxDispersion = 1.1f;
  49.  
  50. public GenerateDungeon(TileMap tileMap)
  51. {
  52. TileMap = tileMap;
  53. }
  54.  
  55. public void Generate()
  56. {
  57. //第一个房间
  58. StartRoom = GenerateRoom(null, 0);
  59.  
  60. //如果房间数量不够, 就一直生成
  61. while (_count < _maxCount)
  62. {
  63. var room = Utils.RandChoose(_roomInfos);
  64. var nextRoom = GenerateRoom(room, Utils.RandRangeInt(0, 3));
  65. if (nextRoom != null)
  66. {
  67. room.Next.Add(nextRoom);
  68. }
  69. }
  70.  
  71. foreach (var info in _roomInfos)
  72. {
  73. //临时铺上地砖
  74. var id = (int)TileMap.TileSet.GetTilesIds()[0];
  75. for (int i = 0; i < info.Size.x; i++)
  76. {
  77. for (int j = 0; j < info.Size.y; j++)
  78. {
  79. TileMap.SetCell(i + (int)info.Position.x, j + (int)info.Position.y, id);
  80. }
  81. }
  82. }
  83. }
  84.  
  85. //生成房间
  86. private RoomInfo GenerateRoom(RoomInfo prevRoomInfo, int direction)
  87. {
  88. if (_count >= _maxCount)
  89. {
  90. return null;
  91. }
  92.  
  93. var room = new RoomInfo(_count);
  94. //房间大小
  95. room.Size = new Vector2(Utils.RandRangeInt(_roomMinWidth, _roomMaxWidth),
  96. Utils.RandRangeInt(_roomMinHeight, _roomMaxHeight));
  97.  
  98. if (prevRoomInfo != null) //表示这不是第一个房间, 就得判断当前位置下的房间是否被遮挡
  99. {
  100. //房间间隔
  101. var space = Utils.RandRangeInt(_roomMinInterval, _roomMaxInterval);
  102. //中心偏移
  103. int offset;
  104. if (direction == 0 || direction == 2)
  105. {
  106. offset = Utils.RandRangeInt(-(int)(prevRoomInfo.Size.x * _roomVerticalMinDispersion),
  107. (int)(prevRoomInfo.Size.x * _roomVerticalMaxDispersion));
  108. }
  109. else
  110. {
  111. offset = Utils.RandRangeInt(-(int)(prevRoomInfo.Size.y * _roomHorizontalMinDispersion),
  112. (int)(prevRoomInfo.Size.y * _roomHorizontalMaxDispersion));
  113. }
  114.  
  115. //计算房间位置
  116. if (direction == 0) //上
  117. {
  118. room.Position = new Vector2(prevRoomInfo.Position.x + offset,
  119. prevRoomInfo.Position.y - room.Size.y - space);
  120. }
  121. else if (direction == 1) //右
  122. {
  123. room.Position = new Vector2(prevRoomInfo.Position.x + prevRoomInfo.Size.y + space,
  124. prevRoomInfo.Position.y + offset);
  125. }
  126. else if (direction == 2) //下
  127. {
  128. room.Position = new Vector2(prevRoomInfo.Position.x + offset,
  129. prevRoomInfo.Position.y + prevRoomInfo.Size.y + space);
  130. }
  131. else if (direction == 3) //左
  132. {
  133. room.Position = new Vector2(prevRoomInfo.Position.x - room.Size.x - space,
  134. prevRoomInfo.Position.y + offset);
  135. }
  136.  
  137. //是否碰到其他房间或者过道
  138. if (RoomGrid.RectCollision(room.Position - new Vector2(3, 3), room.Size + new Vector2(6, 6)))
  139. {
  140. return null;
  141. }
  142.  
  143. RoomGrid.AddRect(room.Position, room.Size, true);
  144.  
  145. //找门, 与上一个房间是否能连通
  146. if (!ConnectDoor(prevRoomInfo, room))
  147. {
  148. RoomGrid.RemoveRect(room.Position, room.Size);
  149. return null;
  150. }
  151. }
  152.  
  153. _count++;
  154. _roomInfos.Add(room);
  155. if (prevRoomInfo == null)
  156. {
  157. RoomGrid.AddRect(room.Position, room.Size, true);
  158. }
  159.  
  160. //下一个房间
  161. //0上, 1右, 2下, 3左
  162. var dirList = new List<int>(new[] { 0, 1, 2, 3 });
  163. if (prevRoomInfo != null)
  164. {
  165. dirList.Remove(GetReverseDirection(direction));
  166. }
  167.  
  168. //这条线有一定概率不生成下一个房间
  169. if (Utils.RandRangeInt(0, 2) != 0)
  170. {
  171. while (dirList.Count > 0)
  172. {
  173. var randDir = Utils.RandChoose(dirList);
  174. var nextRoom = GenerateRoom(room, randDir);
  175. if (nextRoom == null)
  176. {
  177. break;
  178. }
  179.  
  180. nextRoom.Prev = room;
  181. room.Next.Add(nextRoom);
  182.  
  183. dirList.Remove(randDir);
  184. }
  185. }
  186.  
  187. return room;
  188. }
  189.  
  190. /// <summary>
  191. /// 找两个房间的门
  192. /// </summary>
  193. private bool ConnectDoor(RoomInfo room, RoomInfo nextRoom)
  194. {
  195. //门描述
  196. var roomDoor = new RoomDoor();
  197. var nextRoomDoor = new RoomDoor();
  198. roomDoor.ConnectRoom = nextRoom;
  199. nextRoomDoor.ConnectRoom = room;
  200.  
  201. var overlapX = Mathf.Min(room.Position.x + room.Size.x, nextRoom.Position.x + nextRoom.Size.x) -
  202. Mathf.Max(room.Position.x, nextRoom.Position.x);
  203. //这种情况下x轴有重叠
  204. if (overlapX >= 6)
  205. {
  206. //找到重叠区域
  207. var range = CalcRange(room.Position.x, room.Position.x + room.Size.x,
  208. nextRoom.Position.x, nextRoom.Position.x + nextRoom.Size.x);
  209. var x = Utils.RandRangeInt((int)range.x + 1, (int)range.y - _corridorWidth - 1);
  210.  
  211. if (room.Position.y < nextRoom.Position.y) //room在上, nextRoom在下
  212. {
  213. roomDoor.Direction = DoorDirection.S;
  214. nextRoomDoor.Direction = DoorDirection.N;
  215. roomDoor.OriginPosition = new Vector2(x, room.Position.y + room.Size.y);
  216. nextRoomDoor.OriginPosition = new Vector2(x, nextRoom.Position.y);
  217. }
  218. else //room在下, nextRoom在上
  219. {
  220. roomDoor.Direction = DoorDirection.N;
  221. nextRoomDoor.Direction = DoorDirection.S;
  222. roomDoor.OriginPosition = new Vector2(x, room.Position.y);
  223. nextRoomDoor.OriginPosition = new Vector2(x, nextRoom.Position.y + nextRoom.Size.y);
  224. }
  225.  
  226. //判断门之间的通道是否有物体碰到
  227. if (!AddCorridorToGridRange(roomDoor.OriginPosition, nextRoomDoor.OriginPosition))
  228. {
  229. //此门不能连通
  230. return false;
  231. }
  232.  
  233. //没有撞到物体
  234. room.Doors.Add(roomDoor);
  235. nextRoom.Doors.Add(nextRoomDoor);
  236. return true;
  237. }
  238.  
  239. var overlapY = Mathf.Min(room.Position.y + room.Size.y, nextRoom.Position.y + nextRoom.Size.y) -
  240. Mathf.Max(room.Position.y, nextRoom.Position.y);
  241. //这种情况下y轴有重叠
  242. if (overlapY >= 6)
  243. {
  244. //找到重叠区域
  245. var range = CalcRange(room.Position.y, room.Position.y + room.Size.y,
  246. nextRoom.Position.y, nextRoom.Position.y + nextRoom.Size.y);
  247. var y = Utils.RandRangeInt((int)range.x + 1, (int)range.y - _corridorWidth - 1);
  248.  
  249. if (room.Position.x < nextRoom.Position.x) //room在左, nextRoom在右
  250. {
  251. roomDoor.Direction = DoorDirection.E;
  252. nextRoomDoor.Direction = DoorDirection.W;
  253. roomDoor.OriginPosition = new Vector2(room.Position.x + room.Size.x, y);
  254. nextRoomDoor.OriginPosition = new Vector2(nextRoom.Position.x, y);
  255. }
  256. else //room在右, nextRoom在左
  257. {
  258. roomDoor.Direction = DoorDirection.W;
  259. nextRoomDoor.Direction = DoorDirection.E;
  260. roomDoor.OriginPosition = new Vector2(room.Position.x, y);
  261. nextRoomDoor.OriginPosition = new Vector2(nextRoom.Position.x + nextRoom.Size.x, y);
  262. }
  263.  
  264. //判断门之间的通道是否有物体碰到
  265. if (!AddCorridorToGridRange(roomDoor.OriginPosition, nextRoomDoor.OriginPosition))
  266. {
  267. //此门不能连通
  268. return false;
  269. }
  270.  
  271. //没有撞到物体
  272. room.Doors.Add(roomDoor);
  273. nextRoom.Doors.Add(nextRoomDoor);
  274. return true;
  275. }
  276.  
  277.  
  278. var offset1 = Mathf.Max((int)overlapX + 2, 2);
  279. var offset2 = Mathf.Max((int)overlapY + 2, 2);
  280.  
  281. //焦点
  282. Vector2 cross;
  283.  
  284. //这种情况下x和y轴都没有重叠, 那么就只能生成拐角通道了
  285. if (room.Position.x > nextRoom.Position.x)
  286. {
  287. if (room.Position.y > nextRoom.Position.y)
  288. {
  289. if (Utils.RandBoolean())
  290. {
  291. roomDoor.Direction = DoorDirection.N; //↑
  292. nextRoomDoor.Direction = DoorDirection.E; //→
  293.  
  294. roomDoor.OriginPosition = new Vector2(room.Position.x + offset1, room.Position.y);
  295. nextRoomDoor.OriginPosition = new Vector2(nextRoom.Position.x + nextRoom.Size.x,
  296. nextRoom.Position.y + nextRoom.Size.y - offset2 - 6);
  297. cross = new Vector2(roomDoor.OriginPosition.x, nextRoomDoor.OriginPosition.y);
  298. }
  299. else
  300. {
  301. roomDoor.Direction = DoorDirection.W; //←
  302. nextRoomDoor.Direction = DoorDirection.S; //↓
  303.  
  304. roomDoor.OriginPosition = new Vector2(room.Position.x, room.Position.y + offset2);
  305. nextRoomDoor.OriginPosition = new Vector2(nextRoom.Position.x + nextRoom.Size.x - offset1 - 6,
  306. nextRoom.Position.y + nextRoom.Size.y);
  307. cross = new Vector2(nextRoomDoor.OriginPosition.x, roomDoor.OriginPosition.y);
  308. }
  309. }
  310. else
  311. {
  312. if (Utils.RandBoolean())
  313. {
  314. roomDoor.Direction = DoorDirection.S; //↓
  315. nextRoomDoor.Direction = DoorDirection.E; //→
  316.  
  317. roomDoor.OriginPosition = new Vector2(room.Position.x + offset1, room.Position.y + room.Size.y);
  318. nextRoomDoor.OriginPosition = new Vector2(nextRoom.Position.x + nextRoom.Size.x,
  319. nextRoom.Position.y + offset2);
  320. cross = new Vector2(roomDoor.OriginPosition.x, nextRoomDoor.OriginPosition.y);
  321. }
  322. else
  323. {
  324. roomDoor.Direction = DoorDirection.W; //←
  325. nextRoomDoor.Direction = DoorDirection.N; //↑
  326.  
  327. roomDoor.OriginPosition =
  328. new Vector2(room.Position.x, room.Position.y + room.Size.y - offset2 - 6); //
  329. nextRoomDoor.OriginPosition = new Vector2(nextRoom.Position.x + nextRoom.Size.x - offset2 - 6,
  330. nextRoom.Position.y);
  331. cross = new Vector2(nextRoomDoor.OriginPosition.x, roomDoor.OriginPosition.y);
  332. }
  333. }
  334. }
  335. else
  336. {
  337. if (room.Position.y > nextRoom.Position.y)
  338. {
  339. if (Utils.RandBoolean())
  340. {
  341. roomDoor.Direction = DoorDirection.E; //→
  342. nextRoomDoor.Direction = DoorDirection.S; //↓
  343.  
  344. roomDoor.OriginPosition = new Vector2(room.Position.x + room.Size.x, room.Position.y + offset2);
  345. nextRoomDoor.OriginPosition = new Vector2(nextRoom.Position.x + offset1,
  346. nextRoom.Position.y + nextRoom.Size.y);
  347. cross = new Vector2(nextRoomDoor.OriginPosition.x, roomDoor.OriginPosition.y);
  348. }
  349. else
  350. {
  351. roomDoor.Direction = DoorDirection.N; //↑
  352. nextRoomDoor.Direction = DoorDirection.W; //←
  353.  
  354. roomDoor.OriginPosition = new Vector2(room.Position.x + room.Size.x - offset1 - 6, room.Position.y);
  355. nextRoomDoor.OriginPosition = new Vector2(nextRoom.Position.x,
  356. nextRoom.Position.y + nextRoom.Size.y - offset2 - 6);
  357. cross = new Vector2(roomDoor.OriginPosition.x, nextRoomDoor.OriginPosition.y);
  358. }
  359. }
  360. else
  361. {
  362. if (Utils.RandBoolean())
  363. {
  364. roomDoor.Direction = DoorDirection.E; //→
  365. nextRoomDoor.Direction = DoorDirection.N; //↑
  366.  
  367. roomDoor.OriginPosition = new Vector2(room.Position.x + room.Size.x,
  368. room.Position.y + room.Size.y - offset2 - 6);
  369. nextRoomDoor.OriginPosition = new Vector2(nextRoom.Position.x + offset1, nextRoom.Position.y);
  370. cross = new Vector2(nextRoomDoor.OriginPosition.x, roomDoor.OriginPosition.y);
  371. }
  372. else
  373. {
  374. roomDoor.Direction = DoorDirection.S; //↓
  375. nextRoomDoor.Direction = DoorDirection.W; //←
  376.  
  377. roomDoor.OriginPosition = new Vector2(room.Position.x + room.Size.x - offset1 - 6,
  378. room.Position.y + room.Size.y);
  379. nextRoomDoor.OriginPosition = new Vector2(nextRoom.Position.x, nextRoom.Position.y + offset2);
  380. cross = new Vector2(roomDoor.OriginPosition.x, nextRoomDoor.OriginPosition.y);
  381. }
  382. }
  383. }
  384.  
  385. //判断门之间的通道是否有物体碰到
  386. if (!AddCorridorToGridRange(roomDoor.OriginPosition, nextRoomDoor.OriginPosition, cross))
  387. {
  388. //此门不能连通
  389. return false;
  390. }
  391.  
  392. roomDoor.HasCross = true;
  393. roomDoor.Cross = cross;
  394.  
  395. room.Doors.Add(roomDoor);
  396. nextRoom.Doors.Add(nextRoomDoor);
  397. return true;
  398. }
  399.  
  400. private Vector2 CalcRange(float start1, float end1, float start2, float end2)
  401. {
  402. return new Vector2(Mathf.Max(start1, start2), Mathf.Min(end1, end2));
  403. }
  404.  
  405. private int GetReverseDirection(int direction)
  406. {
  407. switch (direction)
  408. {
  409. case 0: return 2;
  410. case 1: return 3;
  411. case 2: return 0;
  412. case 3: return 1;
  413. }
  414.  
  415. return 2;
  416. }
  417.  
  418. private bool AddCorridorToGridRange(Vector2 point1, Vector2 point2)
  419. {
  420. var pos = new Vector2(Mathf.Min(point1.x, point2.x), Mathf.Min(point1.y, point2.y));
  421. var size = new Vector2(
  422. point1.x == point2.x ? _corridorWidth : Mathf.Abs(point1.x - point2.x),
  423. point1.y == point2.y ? _corridorWidth : Mathf.Abs(point1.y - point2.y)
  424. );
  425. if (RoomGrid.RectCollision(pos, size))
  426. {
  427. return false;
  428. }
  429.  
  430. RoomGrid.AddRect(pos, size, true);
  431. return true;
  432. }
  433.  
  434. private bool AddCorridorToGridRange(Vector2 point1, Vector2 point2, Vector2 cross)
  435. {
  436. var pos1 = new Vector2(Mathf.Min(point1.x, cross.x), Mathf.Min(point1.y, cross.y));
  437. var size1 = new Vector2(
  438. point1.x == cross.x ? _corridorWidth : Mathf.Abs(point1.x - cross.x),
  439. point1.y == cross.y ? _corridorWidth : Mathf.Abs(point1.y - cross.y)
  440. );
  441. var pos2 = new Vector2(Mathf.Min(point2.x, cross.x), Mathf.Min(point2.y, cross.y));
  442. var size2 = new Vector2(
  443. point2.x == cross.x ? _corridorWidth : Mathf.Abs(point2.x - cross.x),
  444. point2.y == cross.y ? _corridorWidth : Mathf.Abs(point2.y - cross.y)
  445. );
  446. if (RoomGrid.RectCollision(pos1, size1) || RoomGrid.RectCollision(pos2, size2))
  447. {
  448. return false;
  449. }
  450.  
  451. RoomGrid.AddRect(pos1, size1, true);
  452. RoomGrid.AddRect(pos2, size2, true);
  453. return true;
  454. }
  455. }