Newer
Older
DungeonShooting / DungeonShooting_Godot / src / framework / map / GenerateDungeon.cs
@小李xl 小李xl on 16 Feb 2023 20 KB 小修改
  1.  
  2. using System.Collections.Generic;
  3. using Godot;
  4.  
  5. /// <summary>
  6. /// 地牢生成器
  7. /// </summary>
  8. public class GenerateDungeon
  9. {
  10. /// <summary>
  11. /// 过道宽度
  12. /// </summary>
  13. public const int CorridorWidth = 4;
  14. /// <summary>
  15. /// 所有生成的房间, 调用过 Generate() 函数才能获取到值
  16. /// </summary>
  17. public List<RoomInfo> RoomInfos { get; } = new List<RoomInfo>();
  18.  
  19. /// <summary>
  20. /// 起始房间
  21. /// </summary>
  22. public RoomInfo StartRoom { get; private set; }
  23. /// <summary>
  24. /// 生成的房间数量
  25. /// </summary>
  26. private int _maxCount = 15;
  27.  
  28. //用于标记地图上的坐标是否被占用
  29. private Grid<bool> _roomGrid { get; } = new Grid<bool>();
  30. //当前房间数量
  31. private int _count = 0;
  32. //宽高
  33. private int _roomMinWidth = 15;
  34. private int _roomMaxWidth = 35;
  35. private int _roomMinHeight = 15;
  36. private int _roomMaxHeight = 30;
  37.  
  38. //间隔
  39. private int _roomMinInterval = 6;
  40. private int _roomMaxInterval = 10;
  41.  
  42. //房间横轴分散程度
  43. private float _roomHorizontalMinDispersion = 0.7f;
  44. private float _roomHorizontalMaxDispersion = 1.1f;
  45.  
  46. //房间纵轴分散程度
  47. private float _roomVerticalMinDispersion = 0.7f;
  48. private float _roomVerticalMaxDispersion = 1.1f;
  49.  
  50. //区域限制
  51. private bool _enableLimitRange = true;
  52. private int _rangeX = 110;
  53. private int _rangeY = 110;
  54. //找房间失败次数, 过大则会关闭区域限制
  55. private int _maxFailCount = 10;
  56. private int _failCount = 0;
  57.  
  58. private enum GenerateRoomErrorCode
  59. {
  60. NoError,
  61. //房间已满
  62. RoomFull,
  63. //超出区域
  64. OutArea,
  65. //碰到其他房间或过道
  66. HasCollision,
  67. //没有合适的门
  68. NoProperDoor,
  69. }
  70. /// <summary>
  71. /// 生成房间
  72. /// </summary>
  73. public void Generate()
  74. {
  75. if (StartRoom != null) return;
  76.  
  77. //第一个房间
  78. GenerateRoom(null, 0, out var startRoom);
  79. StartRoom = startRoom;
  80. //如果房间数量不够, 就一直生成
  81. while (_count < _maxCount)
  82. {
  83. var room = Utils.RandChoose(RoomInfos);
  84. var errorCode = GenerateRoom(room, Utils.RandRangeInt(0, 3), out var nextRoom);
  85. if (errorCode == GenerateRoomErrorCode.NoError)
  86. {
  87. _failCount = 0;
  88. room.Next.Add(nextRoom);
  89. }
  90. else
  91. {
  92. GD.Print("生成第" + (_count + 1) + "个房间失败! 失败原因: " + errorCode);
  93. if (errorCode == GenerateRoomErrorCode.OutArea)
  94. {
  95. _failCount++;
  96. GD.Print("超出区域失败次数: " + _failCount);
  97. if (_failCount >= _maxFailCount)
  98. {
  99. _enableLimitRange = false;
  100. GD.Print("生成房间失败次数过多, 关闭区域限制!");
  101. }
  102. }
  103. }
  104. }
  105. _roomGrid.Clear();
  106. }
  107.  
  108. //生成房间
  109. private GenerateRoomErrorCode GenerateRoom(RoomInfo prevRoomInfo, int direction, out RoomInfo resultRoom)
  110. {
  111. if (_count >= _maxCount)
  112. {
  113. resultRoom = null;
  114. return GenerateRoomErrorCode.RoomFull;
  115. }
  116.  
  117. var packedScene = ResourceManager.Load<PackedScene>(
  118. Utils.RandChoose(
  119. ResourcePath.resource_map_Room1_tscn,
  120. ResourcePath.resource_map_Room2_tscn
  121. )
  122. );
  123. var template = packedScene.Instantiate<TileMap>();
  124. var room = new RoomInfo(_count, template);
  125. //房间大小
  126. var usedRect = template.GetUsedRect();
  127. room.Size = usedRect.Size;
  128.  
  129. //随机生成房间 (老流程)
  130. // room.Size = new Vector2(Utils.RandRangeInt(_roomMinWidth, _roomMaxWidth),
  131. // Utils.RandRangeInt(_roomMinHeight, _roomMaxHeight));
  132.  
  133. if (prevRoomInfo != null) //表示这不是第一个房间, 就得判断当前位置下的房间是否被遮挡
  134. {
  135. //房间间隔
  136. var space = Utils.RandRangeInt(_roomMinInterval, _roomMaxInterval);
  137. //中心偏移
  138. int offset;
  139. if (direction == 0 || direction == 2)
  140. {
  141. offset = Utils.RandRangeInt(-(int)(prevRoomInfo.Size.X * _roomVerticalMinDispersion),
  142. (int)(prevRoomInfo.Size.X * _roomVerticalMaxDispersion));
  143. }
  144. else
  145. {
  146. offset = Utils.RandRangeInt(-(int)(prevRoomInfo.Size.Y * _roomHorizontalMinDispersion),
  147. (int)(prevRoomInfo.Size.Y * _roomHorizontalMaxDispersion));
  148. }
  149.  
  150. //计算房间位置
  151. if (direction == 0) //上
  152. {
  153. room.Position = new Vector2I(prevRoomInfo.Position.X + offset,
  154. prevRoomInfo.Position.Y - room.Size.Y - space);
  155. }
  156. else if (direction == 1) //右
  157. {
  158. room.Position = new Vector2I(prevRoomInfo.Position.X + prevRoomInfo.Size.Y + space,
  159. prevRoomInfo.Position.Y + offset);
  160. }
  161. else if (direction == 2) //下
  162. {
  163. room.Position = new Vector2I(prevRoomInfo.Position.X + offset,
  164. prevRoomInfo.Position.Y + prevRoomInfo.Size.Y + space);
  165. }
  166. else if (direction == 3) //左
  167. {
  168. room.Position = new Vector2I(prevRoomInfo.Position.X - room.Size.X - space,
  169. prevRoomInfo.Position.Y + offset);
  170. }
  171. //是否在限制区域内
  172. if (_enableLimitRange)
  173. {
  174. if (room.Position.X < -_rangeX || room.Position.X + room.Size.X > _rangeX || room.Position.Y < -_rangeY || room.Position.Y + room.Size.Y > _rangeY)
  175. {
  176. resultRoom = null;
  177. return GenerateRoomErrorCode.OutArea;
  178. }
  179. }
  180.  
  181. //是否碰到其他房间或者过道
  182. if (_roomGrid.RectCollision(room.Position - new Vector2(3, 3), room.Size + new Vector2(6, 6)))
  183. {
  184. resultRoom = null;
  185. return GenerateRoomErrorCode.HasCollision;
  186. }
  187.  
  188. _roomGrid.AddRect(room.Position, room.Size, true);
  189.  
  190. //找门, 与上一个房间是否能连通
  191. if (!ConnectDoor(prevRoomInfo, room))
  192. {
  193. _roomGrid.RemoveRect(room.Position, room.Size);
  194. resultRoom = null;
  195. return GenerateRoomErrorCode.NoProperDoor;
  196. }
  197. }
  198.  
  199. _count++;
  200. RoomInfos.Add(room);
  201. if (prevRoomInfo == null)
  202. {
  203. _roomGrid.AddRect(room.Position, room.Size, true);
  204. }
  205.  
  206. //下一个房间
  207. //0上, 1右, 2下, 3左
  208. var dirList = new List<int>(new[] { 0, 1, 2, 3 });
  209. if (prevRoomInfo != null)
  210. {
  211. dirList.Remove(GetReverseDirection(direction));
  212. }
  213.  
  214. //这条线有一定概率不生成下一个房间
  215. if (Utils.RandRangeInt(0, 2) != 0)
  216. {
  217. while (dirList.Count > 0)
  218. {
  219. var randDir = Utils.RandChoose(dirList);
  220. GenerateRoom(room, randDir, out var nextRoom);
  221. if (nextRoom == null)
  222. {
  223. break;
  224. }
  225.  
  226. nextRoom.Prev = room;
  227. room.Next.Add(nextRoom);
  228.  
  229. dirList.Remove(randDir);
  230. }
  231. }
  232.  
  233. resultRoom = room;
  234. return GenerateRoomErrorCode.NoError;
  235. }
  236.  
  237. /// <summary>
  238. /// 找两个房间的门
  239. /// </summary>
  240. private bool ConnectDoor(RoomInfo room, RoomInfo nextRoom)
  241. {
  242. //门描述
  243. var roomDoor = new RoomDoorInfo();
  244. var nextRoomDoor = new RoomDoorInfo();
  245. roomDoor.RoomInfo = room;
  246. nextRoomDoor.RoomInfo = nextRoom;
  247. roomDoor.ConnectRoom = nextRoom;
  248. roomDoor.ConnectDoor = nextRoomDoor;
  249. nextRoomDoor.ConnectRoom = room;
  250. nextRoomDoor.ConnectDoor = roomDoor;
  251.  
  252. var overlapX = Mathf.Min(room.Position.X + room.Size.X, nextRoom.Position.X + nextRoom.Size.X) -
  253. Mathf.Max(room.Position.X, nextRoom.Position.X);
  254. //这种情况下x轴有重叠
  255. if (overlapX >= 6)
  256. {
  257. //找到重叠区域
  258. var range = CalcOverlapRange(room.Position.X, room.Position.X + room.Size.X,
  259. nextRoom.Position.X, nextRoom.Position.X + nextRoom.Size.X);
  260. var x = Utils.RandRangeInt((int)range.X + 1, (int)range.Y - CorridorWidth - 1);
  261.  
  262. if (room.Position.Y < nextRoom.Position.Y) //room在上, nextRoom在下
  263. {
  264. roomDoor.Direction = DoorDirection.S;
  265. nextRoomDoor.Direction = DoorDirection.N;
  266. roomDoor.OriginPosition = new Vector2(x, room.Position.Y + room.Size.Y);
  267. nextRoomDoor.OriginPosition = new Vector2(x, nextRoom.Position.Y);
  268. }
  269. else //room在下, nextRoom在上
  270. {
  271. roomDoor.Direction = DoorDirection.N;
  272. nextRoomDoor.Direction = DoorDirection.S;
  273. roomDoor.OriginPosition = new Vector2(x, room.Position.Y);
  274. nextRoomDoor.OriginPosition = new Vector2(x, nextRoom.Position.Y + nextRoom.Size.Y);
  275. }
  276.  
  277. //判断门之间的通道是否有物体碰到
  278. if (!AddCorridorToGridRange(roomDoor, nextRoomDoor))
  279. {
  280. //此门不能连通
  281. return false;
  282. }
  283.  
  284. //没有撞到物体
  285. room.Doors.Add(roomDoor);
  286. nextRoom.Doors.Add(nextRoomDoor);
  287. return true;
  288. }
  289.  
  290. var overlapY = Mathf.Min(room.Position.Y + room.Size.Y, nextRoom.Position.Y + nextRoom.Size.Y) -
  291. Mathf.Max(room.Position.Y, nextRoom.Position.Y);
  292. //这种情况下y轴有重叠
  293. if (overlapY >= 6)
  294. {
  295. //找到重叠区域
  296. var range = CalcOverlapRange(room.Position.Y, room.Position.Y + room.Size.Y,
  297. nextRoom.Position.Y, nextRoom.Position.Y + nextRoom.Size.Y);
  298. var y = Utils.RandRangeInt((int)range.X + 1, (int)range.Y - CorridorWidth - 1);
  299.  
  300. if (room.Position.X < nextRoom.Position.X) //room在左, nextRoom在右
  301. {
  302. roomDoor.Direction = DoorDirection.E;
  303. nextRoomDoor.Direction = DoorDirection.W;
  304. roomDoor.OriginPosition = new Vector2(room.Position.X + room.Size.X, y);
  305. nextRoomDoor.OriginPosition = new Vector2(nextRoom.Position.X, y);
  306. }
  307. else //room在右, nextRoom在左
  308. {
  309. roomDoor.Direction = DoorDirection.W;
  310. nextRoomDoor.Direction = DoorDirection.E;
  311. roomDoor.OriginPosition = new Vector2(room.Position.X, y);
  312. nextRoomDoor.OriginPosition = new Vector2(nextRoom.Position.X + nextRoom.Size.X, y);
  313. }
  314.  
  315. //判断门之间的通道是否有物体碰到
  316. if (!AddCorridorToGridRange(roomDoor, nextRoomDoor))
  317. {
  318. //此门不能连通
  319. return false;
  320. }
  321.  
  322. //没有撞到物体
  323. room.Doors.Add(roomDoor);
  324. nextRoom.Doors.Add(nextRoomDoor);
  325. return true;
  326. }
  327.  
  328. var offset1 = Mathf.Clamp((int)overlapX + 2, 2, 6);
  329. var offset2 = Mathf.Clamp((int)overlapY + 2, 2, 6);
  330.  
  331. //焦点
  332. Vector2 cross;
  333.  
  334. //这种情况下x和y轴都没有重叠, 那么就只能生成拐角通道了
  335. if (room.Position.X > nextRoom.Position.X)
  336. {
  337. if (room.Position.Y > nextRoom.Position.Y)
  338. {
  339. if (Utils.RandBoolean())
  340. {
  341. roomDoor.Direction = DoorDirection.N; //↑
  342. nextRoomDoor.Direction = DoorDirection.E; //→
  343.  
  344. roomDoor.OriginPosition = new Vector2(room.Position.X + offset1, room.Position.Y);
  345. nextRoomDoor.OriginPosition = new Vector2(nextRoom.Position.X + nextRoom.Size.X,
  346. nextRoom.Position.Y + nextRoom.Size.Y - offset2 - 6);
  347. cross = new Vector2(roomDoor.OriginPosition.X, nextRoomDoor.OriginPosition.Y);
  348. }
  349. else
  350. {
  351. roomDoor.Direction = DoorDirection.W; //←
  352. nextRoomDoor.Direction = DoorDirection.S; //↓
  353. roomDoor.OriginPosition = new Vector2(room.Position.X, room.Position.Y + offset2);
  354. nextRoomDoor.OriginPosition = new Vector2(nextRoom.Position.X + nextRoom.Size.X - offset1 - 6,
  355. nextRoom.Position.Y + nextRoom.Size.Y);
  356. cross = new Vector2(nextRoomDoor.OriginPosition.X, roomDoor.OriginPosition.Y);
  357. }
  358. }
  359. else
  360. {
  361. if (Utils.RandBoolean())
  362. {
  363. roomDoor.Direction = DoorDirection.S; //↓
  364. nextRoomDoor.Direction = DoorDirection.E; //→
  365.  
  366. roomDoor.OriginPosition = new Vector2(room.Position.X + offset1, room.Position.Y + room.Size.Y);
  367. nextRoomDoor.OriginPosition = new Vector2(nextRoom.Position.X + nextRoom.Size.X,
  368. nextRoom.Position.Y + offset2);
  369. cross = new Vector2(roomDoor.OriginPosition.X, nextRoomDoor.OriginPosition.Y);
  370. }
  371. else
  372. {
  373. roomDoor.Direction = DoorDirection.W; //←
  374. nextRoomDoor.Direction = DoorDirection.N; //↑
  375.  
  376. roomDoor.OriginPosition =
  377. new Vector2(room.Position.X, room.Position.Y + room.Size.Y - offset2 - 6); //
  378. nextRoomDoor.OriginPosition = new Vector2(nextRoom.Position.X + nextRoom.Size.X - offset2 - 6,
  379. nextRoom.Position.Y);
  380. cross = new Vector2(nextRoomDoor.OriginPosition.X, roomDoor.OriginPosition.Y);
  381. }
  382. }
  383. }
  384. else
  385. {
  386. if (room.Position.Y > nextRoom.Position.Y)
  387. {
  388. if (Utils.RandBoolean())
  389. {
  390. roomDoor.Direction = DoorDirection.E; //→
  391. nextRoomDoor.Direction = DoorDirection.S; //↓
  392.  
  393. roomDoor.OriginPosition = new Vector2(room.Position.X + room.Size.X, room.Position.Y + offset2);
  394. nextRoomDoor.OriginPosition = new Vector2(nextRoom.Position.X + offset1,
  395. nextRoom.Position.Y + nextRoom.Size.Y);
  396. cross = new Vector2(nextRoomDoor.OriginPosition.X, roomDoor.OriginPosition.Y);
  397. }
  398. else
  399. {
  400. roomDoor.Direction = DoorDirection.N; //↑
  401. nextRoomDoor.Direction = DoorDirection.W; //←
  402.  
  403. roomDoor.OriginPosition = new Vector2(room.Position.X + room.Size.X - offset1 - 6, room.Position.Y);
  404. nextRoomDoor.OriginPosition = new Vector2(nextRoom.Position.X,
  405. nextRoom.Position.Y + nextRoom.Size.Y - offset2 - 6);
  406. cross = new Vector2(roomDoor.OriginPosition.X, nextRoomDoor.OriginPosition.Y);
  407. }
  408. }
  409. else
  410. {
  411. if (Utils.RandBoolean())
  412. {
  413. roomDoor.Direction = DoorDirection.E; //→
  414. nextRoomDoor.Direction = DoorDirection.N; //↑
  415.  
  416. roomDoor.OriginPosition = new Vector2(room.Position.X + room.Size.X,
  417. room.Position.Y + room.Size.Y - offset2 - 6);
  418. nextRoomDoor.OriginPosition = new Vector2(nextRoom.Position.X + offset1, nextRoom.Position.Y);
  419. cross = new Vector2(nextRoomDoor.OriginPosition.X, roomDoor.OriginPosition.Y);
  420. }
  421. else
  422. {
  423. roomDoor.Direction = DoorDirection.S; //↓
  424. nextRoomDoor.Direction = DoorDirection.W; //←
  425.  
  426. roomDoor.OriginPosition = new Vector2(room.Position.X + room.Size.X - offset1 - 6,
  427. room.Position.Y + room.Size.Y);
  428. nextRoomDoor.OriginPosition = new Vector2(nextRoom.Position.X, nextRoom.Position.Y + offset2);
  429. cross = new Vector2(roomDoor.OriginPosition.X, nextRoomDoor.OriginPosition.Y);
  430. }
  431. }
  432. }
  433.  
  434. //判断门之间的通道是否有物体碰到
  435. if (!AddCorridorToGridRange(roomDoor, nextRoomDoor, cross))
  436. {
  437. //此门不能连通
  438. return false;
  439. }
  440.  
  441. roomDoor.HasCross = true;
  442. roomDoor.Cross = cross;
  443. nextRoomDoor.HasCross = true;
  444. nextRoomDoor.Cross = cross;
  445.  
  446. room.Doors.Add(roomDoor);
  447. nextRoom.Doors.Add(nextRoomDoor);
  448. return true;
  449. }
  450.  
  451. //用于计算重叠区域坐标, 可以理解为一维轴上4个点的中间两个点
  452. private Vector2 CalcOverlapRange(float start1, float end1, float start2, float end2)
  453. {
  454. return new Vector2(Mathf.Max(start1, start2), Mathf.Min(end1, end2));
  455. }
  456.  
  457. //返回参数方向的反方向
  458. private int GetReverseDirection(int direction)
  459. {
  460. switch (direction)
  461. {
  462. case 0: return 2;
  463. case 1: return 3;
  464. case 2: return 0;
  465. case 3: return 1;
  466. }
  467.  
  468. return 2;
  469. }
  470.  
  471. //将两个门间的过道占用数据存入RoomGrid
  472. private bool AddCorridorToGridRange(RoomDoorInfo door1, RoomDoorInfo door2)
  473. {
  474. var point1 = door1.OriginPosition;
  475. var point2 = door2.OriginPosition;
  476. var pos = new Vector2(Mathf.Min(point1.X, point2.X), Mathf.Min(point1.Y, point2.Y));
  477. var size = new Vector2(
  478. point1.X == point2.X ? CorridorWidth : Mathf.Abs(point1.X - point2.X),
  479. point1.Y == point2.Y ? CorridorWidth : Mathf.Abs(point1.Y - point2.Y)
  480. );
  481.  
  482. Vector2 collPos;
  483. Vector2 collSize;
  484. if (point1.X == point2.X) //纵向加宽, 防止贴到其它墙
  485. {
  486. collPos = new Vector2(pos.X - 3, pos.Y);
  487. collSize = new Vector2(size.X + 6, size.Y);
  488. }
  489. else //横向加宽, 防止贴到其它墙
  490. {
  491. collPos = new Vector2(pos.X, pos.Y - 3);
  492. collSize = new Vector2(size.X, size.Y + 6);
  493. }
  494.  
  495. if (_roomGrid.RectCollision(collPos, collSize))
  496. {
  497. return false;
  498. }
  499.  
  500. _roomGrid.AddRect(pos, size, true);
  501. return true;
  502. }
  503.  
  504. //将两个门间的过道占用数据存入RoomGrid, 该重载
  505. private bool AddCorridorToGridRange(RoomDoorInfo door1, RoomDoorInfo door2, Vector2 cross)
  506. {
  507. var point1 = door1.OriginPosition;
  508. var point2 = door2.OriginPosition;
  509. var pos1 = new Vector2(Mathf.Min(point1.X, cross.X), Mathf.Min(point1.Y, cross.Y));
  510. var size1 = new Vector2(
  511. point1.X == cross.X ? CorridorWidth : Mathf.Abs(point1.X - cross.X),
  512. point1.Y == cross.Y ? CorridorWidth : Mathf.Abs(point1.Y - cross.Y)
  513. );
  514. var pos2 = new Vector2(Mathf.Min(point2.X, cross.X), Mathf.Min(point2.Y, cross.Y));
  515. var size2 = new Vector2(
  516. point2.X == cross.X ? CorridorWidth : Mathf.Abs(point2.X - cross.X),
  517. point2.Y == cross.Y ? CorridorWidth : Mathf.Abs(point2.Y - cross.Y)
  518. );
  519.  
  520. Vector2 collPos1;
  521. Vector2 collSize1;
  522. if (point1.X == cross.X) //纵向加宽, 防止贴到其它墙
  523. {
  524. collPos1 = new Vector2(pos1.X - 3, pos1.Y);
  525. collSize1 = new Vector2(size1.X + 6, size1.Y);
  526. }
  527. else //横向加宽, 防止贴到其它墙
  528. {
  529. collPos1 = new Vector2(pos1.X, pos1.Y - 3);
  530. collSize1 = new Vector2(size1.X, size1.Y + 6);
  531. }
  532.  
  533. if (_roomGrid.RectCollision(collPos1, collSize1))
  534. {
  535. return false;
  536. }
  537.  
  538. Vector2 collPos2;
  539. Vector2 collSize2;
  540. if (point2.X == cross.X) //纵向加宽, 防止贴到其它墙
  541. {
  542. collPos2 = new Vector2(pos2.X - 3, pos2.Y);
  543. collSize2 = new Vector2(size2.X + 6, size2.Y);
  544. }
  545. else //横向加宽, 防止贴到其它墙
  546. {
  547. collPos2 = new Vector2(pos2.X, pos2.Y - 3);
  548. collSize2 = new Vector2(size2.X, size2.Y + 6);
  549. }
  550.  
  551. if (_roomGrid.RectCollision(collPos2, collSize2))
  552. {
  553. return false;
  554. }
  555.  
  556. _roomGrid.AddRect(pos1, size1, true);
  557. _roomGrid.AddRect(pos2, size2, true);
  558. return true;
  559. }
  560. }