Newer
Older
DungeonShooting / DungeonShooting_Godot / src / framework / map / GenerateDungeon.cs
  1.  
  2. using System;
  3. using System.Collections.Generic;
  4. using Godot;
  5.  
  6. /// <summary>
  7. /// 地牢生成器
  8. /// </summary>
  9. public class GenerateDungeon
  10. {
  11. /// <summary>
  12. /// 过道宽度
  13. /// </summary>
  14. public const int CorridorWidth = 4;
  15.  
  16. /// <summary>
  17. /// tilemap 网格大小
  18. /// </summary>
  19. public const int TileCellSize = 16;
  20. /// <summary>
  21. /// 所有生成的房间, 调用过 Generate() 函数才能获取到值
  22. /// </summary>
  23. public List<RoomInfo> RoomInfos { get; } = new List<RoomInfo>();
  24.  
  25. /// <summary>
  26. /// 起始房间
  27. /// </summary>
  28. public RoomInfo StartRoom { get; private set; }
  29. /// <summary>
  30. /// 生成的房间数量
  31. /// </summary>
  32. private int _maxCount = 15;
  33.  
  34. //用于标记地图上的坐标是否被占用
  35. private Grid<bool> _roomGrid { get; } = new Grid<bool>();
  36. //当前房间数量
  37. private int _count = 0;
  38. //宽高
  39. private int _roomMinWidth = 15;
  40. private int _roomMaxWidth = 35;
  41. private int _roomMinHeight = 15;
  42. private int _roomMaxHeight = 30;
  43.  
  44. //间隔
  45. private int _roomMinInterval = 6;
  46. private int _roomMaxInterval = 10;
  47.  
  48. //房间横轴分散程度
  49. private float _roomHorizontalMinDispersion = 0.7f;
  50. private float _roomHorizontalMaxDispersion = 1.1f;
  51.  
  52. //房间纵轴分散程度
  53. private float _roomVerticalMinDispersion = 0.7f;
  54. private float _roomVerticalMaxDispersion = 1.1f;
  55.  
  56. //区域限制
  57. private bool _enableLimitRange = true;
  58. private int _rangeX = 110;
  59. private int _rangeY = 110;
  60. //找房间失败次数, 过大则会关闭区域限制
  61. private int _maxFailCount = 10;
  62. private int _failCount = 0;
  63.  
  64. //最大尝试次数
  65. private int _maxTryCount = 10;
  66. private enum GenerateRoomErrorCode
  67. {
  68. NoError,
  69. //房间已满
  70. RoomFull,
  71. //超出区域
  72. OutArea,
  73. //没有合适的位置
  74. NoSuitableLocation
  75. // //碰到其他房间或过道
  76. // HasCollision,
  77. // //没有合适的门
  78. // NoProperDoor,
  79. }
  80. /// <summary>
  81. /// 生成房间
  82. /// </summary>
  83. public void Generate()
  84. {
  85. if (StartRoom != null) return;
  86.  
  87. //第一个房间
  88. GenerateRoom(null, 0, out var startRoom);
  89. StartRoom = startRoom;
  90. //如果房间数量不够, 就一直生成
  91. while (_count < _maxCount)
  92. {
  93. var room = Utils.RandomChoose(RoomInfos);
  94. var errorCode = GenerateRoom(room, Utils.RandomRangeInt(0, 3), out var nextRoom);
  95. if (errorCode == GenerateRoomErrorCode.NoError)
  96. {
  97. _failCount = 0;
  98. room.Next.Add(nextRoom);
  99. }
  100. else
  101. {
  102. GD.Print("生成第" + (_count + 1) + "个房间失败! 失败原因: " + errorCode);
  103. if (errorCode == GenerateRoomErrorCode.OutArea)
  104. {
  105. _failCount++;
  106. GD.Print("超出区域失败次数: " + _failCount);
  107. if (_failCount >= _maxFailCount)
  108. {
  109. _enableLimitRange = false;
  110. GD.Print("生成房间失败次数过多, 关闭区域限制!");
  111. }
  112. }
  113. }
  114. }
  115. _roomGrid.Clear();
  116. }
  117.  
  118. //生成房间
  119. private GenerateRoomErrorCode GenerateRoom(RoomInfo prevRoomInfo, int direction, out RoomInfo resultRoom)
  120. {
  121. if (_count >= _maxCount)
  122. {
  123. resultRoom = null;
  124. return GenerateRoomErrorCode.RoomFull;
  125. }
  126.  
  127. //随机选择一个房间
  128. var roomSplit = Utils.RandomChoose(GameApplication.Instance.RoomConfig);
  129. var room = new RoomInfo(_count, roomSplit);
  130. //房间大小
  131. room.Size = new Vector2I((int)roomSplit.RoomInfo.Size.X, (int)roomSplit.RoomInfo.Size.Y);
  132.  
  133. if (prevRoomInfo != null) //表示这不是第一个房间, 就得判断当前位置下的房间是否被遮挡
  134. {
  135. //生成的位置可能会和上一个房间对不上, 需要多次尝试
  136. var tryCount = 0; //当前尝试次数
  137. for (; tryCount < _maxTryCount; tryCount++)
  138. {
  139. //房间间隔
  140. var space = Utils.RandomRangeInt(_roomMinInterval, _roomMaxInterval);
  141. //中心偏移
  142. int offset;
  143. if (direction == 0 || direction == 2)
  144. {
  145. offset = Utils.RandomRangeInt(-(int)(prevRoomInfo.Size.X * _roomVerticalMinDispersion),
  146. (int)(prevRoomInfo.Size.X * _roomVerticalMaxDispersion));
  147. }
  148. else
  149. {
  150. offset = Utils.RandomRangeInt(-(int)(prevRoomInfo.Size.Y * _roomHorizontalMinDispersion),
  151. (int)(prevRoomInfo.Size.Y * _roomHorizontalMaxDispersion));
  152. }
  153.  
  154. //计算房间位置
  155. if (direction == 0) //上
  156. {
  157. room.Position = new Vector2I(prevRoomInfo.Position.X + offset,
  158. prevRoomInfo.Position.Y - room.Size.Y - space);
  159. }
  160. else if (direction == 1) //右
  161. {
  162. room.Position = new Vector2I(prevRoomInfo.Position.X + prevRoomInfo.Size.Y + space,
  163. prevRoomInfo.Position.Y + offset);
  164. }
  165. else if (direction == 2) //下
  166. {
  167. room.Position = new Vector2I(prevRoomInfo.Position.X + offset,
  168. prevRoomInfo.Position.Y + prevRoomInfo.Size.Y + space);
  169. }
  170. else if (direction == 3) //左
  171. {
  172. room.Position = new Vector2I(prevRoomInfo.Position.X - room.Size.X - space,
  173. prevRoomInfo.Position.Y + offset);
  174. }
  175.  
  176. //是否在限制区域内
  177. if (_enableLimitRange)
  178. {
  179. if (room.GetHorizontalStart() < -_rangeX || room.GetHorizontalEnd() > _rangeX ||
  180. room.GetVerticalStart() < -_rangeY || room.GetVerticalEnd() > _rangeY)
  181. {
  182. //超出区域, 直接跳出尝试的循环, 返回 null
  183. resultRoom = null;
  184. return GenerateRoomErrorCode.OutArea;
  185. }
  186. }
  187.  
  188. //是否碰到其他房间或者过道
  189. if (_roomGrid.RectCollision(room.Position - new Vector2(3, 3), room.Size + new Vector2(6, 6)))
  190. {
  191. //碰到其他墙壁, 再一次尝试
  192. continue;
  193. //return GenerateRoomErrorCode.HasCollision;
  194. }
  195.  
  196. _roomGrid.AddRect(room.Position, room.Size, true);
  197.  
  198. //找门, 与上一个房间是否能连通
  199. if (!ConnectDoor(prevRoomInfo, room))
  200. {
  201. _roomGrid.RemoveRect(room.Position, room.Size);
  202. //房间过道没有连接上, 再一次尝试
  203. continue;
  204. //return GenerateRoomErrorCode.NoProperDoor;
  205. }
  206. break;
  207. }
  208.  
  209. //尝试次数用光了, 还没有找到合适的位置
  210. if (tryCount >= _maxTryCount)
  211. {
  212. resultRoom = null;
  213. return GenerateRoomErrorCode.NoSuitableLocation;
  214. }
  215. }
  216.  
  217. _count++;
  218. RoomInfos.Add(room);
  219. if (prevRoomInfo == null)
  220. {
  221. _roomGrid.AddRect(room.Position, room.Size, true);
  222. }
  223.  
  224. //下一个房间
  225. //0上, 1右, 2下, 3左
  226. var dirList = new List<int>(new[] { 0, 1, 2, 3 });
  227. if (prevRoomInfo != null)
  228. {
  229. dirList.Remove(GetReverseDirection(direction));
  230. }
  231.  
  232. //这条线有一定概率不生成下一个房间
  233. if (Utils.RandomRangeInt(0, 2) != 0)
  234. {
  235. while (dirList.Count > 0)
  236. {
  237. var randDir = Utils.RandomChoose(dirList);
  238. GenerateRoom(room, randDir, out var nextRoom);
  239. if (nextRoom == null)
  240. {
  241. break;
  242. }
  243.  
  244. nextRoom.Prev = room;
  245. room.Next.Add(nextRoom);
  246.  
  247. dirList.Remove(randDir);
  248. }
  249. }
  250.  
  251. resultRoom = room;
  252. return GenerateRoomErrorCode.NoError;
  253. }
  254.  
  255. /// <summary>
  256. /// 找两个房间的门
  257. /// </summary>
  258. private bool ConnectDoor(RoomInfo room, RoomInfo nextRoom)
  259. {
  260. //门描述
  261. var roomDoor = new RoomDoorInfo();
  262. var nextRoomDoor = new RoomDoorInfo();
  263. roomDoor.RoomInfo = room;
  264. nextRoomDoor.RoomInfo = nextRoom;
  265. roomDoor.ConnectRoom = nextRoom;
  266. roomDoor.ConnectDoor = nextRoomDoor;
  267. nextRoomDoor.ConnectRoom = room;
  268. nextRoomDoor.ConnectDoor = roomDoor;
  269.  
  270. //先寻找直通门
  271. if (Utils.RandomBoolean())
  272. {
  273. //直行通道, 优先横轴
  274. if (TryConnectHorizontalDoor(room, roomDoor, nextRoom, nextRoomDoor)
  275. || TryConnectVerticalDoor(room, roomDoor, nextRoom, nextRoomDoor))
  276. {
  277. return true;
  278. }
  279. }
  280. else
  281. {
  282. //直行通道, 优先纵轴
  283. if (TryConnectVerticalDoor(room, roomDoor, nextRoom, nextRoomDoor)
  284. || TryConnectHorizontalDoor(room, roomDoor, nextRoom, nextRoomDoor))
  285. {
  286. return true;
  287. }
  288. }
  289. //包含拐角的通道
  290. return TryConnectCrossDoor(room, roomDoor, nextRoom, nextRoomDoor);
  291. }
  292.  
  293. /// <summary>
  294. /// 尝试寻找横轴方向上两个房间的连通的门, 只查找直线通道, 返回是否找到
  295. /// </summary>
  296. private bool TryConnectHorizontalDoor(RoomInfo room, RoomDoorInfo roomDoor, RoomInfo nextRoom, RoomDoorInfo nextRoomDoor)
  297. {
  298. var overlapX = Mathf.Min(room.GetHorizontalEnd(), nextRoom.GetHorizontalEnd()) -
  299. Mathf.Max(room.GetHorizontalStart(), nextRoom.GetHorizontalStart());
  300. //这种情况下x轴有重叠
  301. if (overlapX >= 6)
  302. {
  303. //找到重叠区域
  304. var rangeList = FindPassage(room, nextRoom,
  305. room.GetVerticalStart() < nextRoom.GetVerticalStart() ? DoorDirection.S : DoorDirection.N);
  306. while (rangeList.Count > 0)
  307. {
  308. //找到重叠区域
  309. var range = Utils.RandomChooseAndRemove(rangeList);
  310. var x = Utils.RandomRangeInt(range.X, range.Y);
  311. if (room.GetVerticalStart() < nextRoom.GetVerticalStart()) //room在上, nextRoom在下
  312. {
  313. roomDoor.Direction = DoorDirection.S;
  314. nextRoomDoor.Direction = DoorDirection.N;
  315. roomDoor.OriginPosition = new Vector2(x, room.GetVerticalEnd());
  316. nextRoomDoor.OriginPosition = new Vector2(x, nextRoom.GetVerticalStart());
  317. }
  318. else //room在下, nextRoom在上
  319. {
  320. roomDoor.Direction = DoorDirection.N;
  321. nextRoomDoor.Direction = DoorDirection.S;
  322. roomDoor.OriginPosition = new Vector2(x, room.GetVerticalStart());
  323. nextRoomDoor.OriginPosition = new Vector2(x, nextRoom.GetVerticalEnd());
  324. }
  325.  
  326. //判断门之间的通道是否有物体碰到
  327. if (!AddCorridorToGridRange(roomDoor, nextRoomDoor))
  328. {
  329. //此门不能连通
  330. continue;
  331. }
  332.  
  333. //没有撞到物体
  334. room.Doors.Add(roomDoor);
  335. nextRoom.Doors.Add(nextRoomDoor);
  336. return true;
  337. }
  338. }
  339. return false;
  340. }
  341.  
  342. /// <summary>
  343. /// 尝试寻找纵轴方向上两个房间的连通的门, 只查找直线通道, 返回是否找到
  344. /// </summary>
  345. private bool TryConnectVerticalDoor(RoomInfo room, RoomDoorInfo roomDoor, RoomInfo nextRoom, RoomDoorInfo nextRoomDoor)
  346. {
  347. var overlapY = Mathf.Min(room.GetVerticalEnd(), nextRoom.GetVerticalEnd()) -
  348. Mathf.Max(room.GetVerticalStart(), nextRoom.GetVerticalStart());
  349. //这种情况下y轴有重叠
  350. if (overlapY >= 6)
  351. {
  352. //找到重叠区域
  353. var rangeList = FindPassage(room, nextRoom,
  354. room.GetHorizontalStart() < nextRoom.GetHorizontalStart() ? DoorDirection.E : DoorDirection.W);
  355.  
  356. while (rangeList.Count > 0)
  357. {
  358. //找到重叠区域
  359. var range = Utils.RandomChooseAndRemove(rangeList);
  360. var y = Utils.RandomRangeInt(range.X, range.Y);
  361. if (room.GetHorizontalStart() < nextRoom.GetHorizontalStart()) //room在左, nextRoom在右
  362. {
  363. roomDoor.Direction = DoorDirection.E;
  364. nextRoomDoor.Direction = DoorDirection.W;
  365. roomDoor.OriginPosition = new Vector2(room.GetHorizontalEnd(), y);
  366. nextRoomDoor.OriginPosition = new Vector2(nextRoom.GetHorizontalStart(), y);
  367. }
  368. else //room在右, nextRoom在左
  369. {
  370. roomDoor.Direction = DoorDirection.W;
  371. nextRoomDoor.Direction = DoorDirection.E;
  372. roomDoor.OriginPosition = new Vector2(room.GetHorizontalStart(), y);
  373. nextRoomDoor.OriginPosition = new Vector2(nextRoom.GetHorizontalEnd(), y);
  374. }
  375.  
  376. //判断门之间的通道是否有物体碰到
  377. if (!AddCorridorToGridRange(roomDoor, nextRoomDoor))
  378. {
  379. //此门不能连通
  380. continue;
  381. }
  382.  
  383. //没有撞到物体
  384. room.Doors.Add(roomDoor);
  385. nextRoom.Doors.Add(nextRoomDoor);
  386. return true;
  387. }
  388. }
  389.  
  390. return false;
  391. }
  392.  
  393. /// <summary>
  394. /// 尝试寻找包含拐角的两个房间的连通的门, 返回是否找到
  395. /// </summary>
  396. private bool TryConnectCrossDoor(RoomInfo room, RoomDoorInfo roomDoor, RoomInfo nextRoom, RoomDoorInfo nextRoomDoor)
  397. {
  398. //焦点
  399. Vector2 cross = default;
  400.  
  401. if (room.GetHorizontalStart() > nextRoom.GetHorizontalStart())
  402. {
  403. if (room.GetVerticalStart() > nextRoom.GetVerticalStart())
  404. {
  405. if (Utils.RandomBoolean()) //↑ //→
  406. {
  407. if (!TryConnect_NE_Door(room, nextRoom, roomDoor, nextRoomDoor, ref cross) &&
  408. !TryConnect_WS_Door(room, nextRoom, roomDoor, nextRoomDoor, ref cross))
  409. {
  410. return false;
  411. }
  412. }
  413. else //← //↓
  414. {
  415. if (!TryConnect_WS_Door(room, nextRoom, roomDoor, nextRoomDoor, ref cross) &&
  416. !TryConnect_NE_Door(room, nextRoom, roomDoor, nextRoomDoor, ref cross))
  417. {
  418. return false;
  419. }
  420. }
  421. }
  422. else
  423. {
  424. if (Utils.RandomBoolean()) //↓ //→
  425. {
  426. if (!TryConnect_SE_Door(room, nextRoom, roomDoor, nextRoomDoor, ref cross) &&
  427. !TryConnect_WN_Door(room, nextRoom, roomDoor, nextRoomDoor, ref cross))
  428. {
  429. return false;
  430. }
  431. }
  432. else //← //↑
  433. {
  434. if (!TryConnect_WN_Door(room, nextRoom, roomDoor, nextRoomDoor, ref cross) &&
  435. !TryConnect_SE_Door(room, nextRoom, roomDoor, nextRoomDoor, ref cross))
  436. {
  437. return false;
  438. }
  439. }
  440. }
  441. }
  442. else
  443. {
  444. if (room.GetVerticalStart() > nextRoom.GetVerticalStart()) //→ //↓
  445. {
  446. if (Utils.RandomBoolean())
  447. {
  448. if (!TryConnect_ES_Door(room, nextRoom, roomDoor, nextRoomDoor, ref cross) &&
  449. !TryConnect_NW_Door(room, nextRoom, roomDoor, nextRoomDoor, ref cross))
  450. {
  451. return false;
  452. }
  453. }
  454. else //↑ //←
  455. {
  456. if (!TryConnect_NW_Door(room, nextRoom, roomDoor, nextRoomDoor, ref cross) &&
  457. !TryConnect_ES_Door(room, nextRoom, roomDoor, nextRoomDoor, ref cross))
  458. {
  459. return false;
  460. }
  461. }
  462. }
  463. else
  464. {
  465. if (Utils.RandomBoolean()) //→ //↑
  466. {
  467. if (!TryConnect_EN_Door(room, nextRoom, roomDoor, nextRoomDoor, ref cross) &&
  468. !TryConnect_SW_Door(room, nextRoom, roomDoor, nextRoomDoor, ref cross))
  469. {
  470. return false;
  471. }
  472. }
  473. else //↓ //←
  474. {
  475. if (!TryConnect_SW_Door(room, nextRoom, roomDoor, nextRoomDoor, ref cross) &&
  476. !TryConnect_EN_Door(room, nextRoom, roomDoor, nextRoomDoor, ref cross))
  477. {
  478. return false;
  479. }
  480. }
  481. }
  482. }
  483.  
  484. //判断门之间的通道是否有物体碰到
  485. if (!AddCorridorToGridRange(roomDoor, nextRoomDoor, cross))
  486. {
  487. //此门不能连通
  488. return false;
  489. }
  490.  
  491. roomDoor.HasCross = true;
  492. roomDoor.Cross = cross;
  493. nextRoomDoor.HasCross = true;
  494. nextRoomDoor.Cross = cross;
  495.  
  496. room.Doors.Add(roomDoor);
  497. nextRoom.Doors.Add(nextRoomDoor);
  498. return true;
  499. }
  500.  
  501. private bool FindCrossPassage(RoomInfo room, RoomInfo nextRoom, RoomDoorInfo roomDoor, RoomDoorInfo nextRoomDoor,ref int offset1, ref int offset2)
  502. {
  503. var room1 = room.RoomSplit.RoomInfo;
  504. var room2 = nextRoom.RoomSplit.RoomInfo;
  505. Vector2I? temp1 = null;
  506. Vector2I? temp2 = null;
  507.  
  508. foreach (var areaInfo1 in room1.DoorAreaInfos)
  509. {
  510. if (areaInfo1.Direction == roomDoor.Direction)
  511. {
  512. FindCrossPassage_Area(areaInfo1, room, nextRoom, ref temp1);
  513. }
  514. }
  515.  
  516. foreach (var areaInfo2 in room2.DoorAreaInfos)
  517. {
  518. if (areaInfo2.Direction == nextRoomDoor.Direction)
  519. {
  520. FindCrossPassage_Area(areaInfo2, nextRoom, room, ref temp2);
  521. }
  522. }
  523.  
  524. if (temp1 != null && temp2 != null)
  525. {
  526. offset1 = Utils.RandomRangeInt(temp1.Value.X, temp1.Value.Y);
  527. offset2 = Utils.RandomRangeInt(temp2.Value.X, temp2.Value.Y);
  528. return true;
  529. }
  530.  
  531. return false;
  532. }
  533.  
  534. private void FindCrossPassage_Area(DoorAreaInfo areaInfo, RoomInfo room1, RoomInfo room2, ref Vector2I? areaRange)
  535. {
  536. if (areaInfo.Direction == DoorDirection.N || areaInfo.Direction == DoorDirection.S) //纵向门
  537. {
  538. var num = room1.GetHorizontalStart();
  539. var p1 = num + areaInfo.Start / TileCellSize;
  540. var p2 = num + areaInfo.End / TileCellSize;
  541.  
  542. if (room1.Position.X > room2.Position.X)
  543. {
  544. var range = CalcOverlapRange(room2.GetHorizontalEnd(),
  545. room1.GetHorizontalEnd(), p1, p2);
  546. //交集范围够生成门
  547. if (range.Y - range.X >= CorridorWidth)
  548. {
  549. var tempRange = new Vector2I(Mathf.Abs(room1.Position.X - (int)range.X),
  550. Mathf.Abs(room1.Position.X - (int)range.Y) - CorridorWidth);
  551. if (areaRange == null || tempRange.X < areaRange.Value.X)
  552. {
  553. areaRange = tempRange;
  554. }
  555. }
  556. }
  557. else
  558. {
  559. var range = CalcOverlapRange(room1.GetHorizontalStart(),
  560. room2.GetHorizontalStart(), p1, p2);
  561. //交集范围够生成门
  562. if (range.Y - range.X >= CorridorWidth)
  563. {
  564. var tempRange = new Vector2I(Mathf.Abs(room1.Position.X - (int)range.X),
  565. Mathf.Abs(room1.Position.X - (int)range.Y) - CorridorWidth);
  566. if (areaRange == null || tempRange.Y > areaRange.Value.Y)
  567. {
  568. areaRange = tempRange;
  569. }
  570. }
  571. }
  572. }
  573. else //横向门
  574. {
  575. var num = room1.GetVerticalStart();
  576. var p1 = num + areaInfo.Start / TileCellSize;
  577. var p2 = num + areaInfo.End / TileCellSize;
  578.  
  579. if (room1.Position.Y > room2.Position.Y)
  580. {
  581. var range = CalcOverlapRange(room2.GetVerticalEnd(),
  582. room1.GetVerticalEnd(), p1, p2);
  583. //交集范围够生成门
  584. if (range.Y - range.X >= CorridorWidth)
  585. {
  586. var tempRange = new Vector2I(Mathf.Abs(room1.Position.Y - (int)range.X),
  587. Mathf.Abs(room1.Position.Y - (int)range.Y) - CorridorWidth);
  588. if (areaRange == null || tempRange.X < areaRange.Value.X)
  589. {
  590. areaRange = tempRange;
  591. }
  592. }
  593. }
  594. else
  595. {
  596. var range = CalcOverlapRange(room1.GetVerticalStart(),
  597. room2.GetVerticalStart(), p1, p2);
  598. //交集范围够生成门
  599. if (range.Y - range.X >= CorridorWidth)
  600. {
  601. var tempRange = new Vector2I(Mathf.Abs(room1.Position.Y - (int)range.X),
  602. Mathf.Abs(room1.Position.Y - (int)range.Y) - CorridorWidth);
  603. if (areaRange == null || tempRange.Y > areaRange.Value.Y)
  604. {
  605. areaRange = tempRange;
  606. }
  607. }
  608. }
  609. }
  610. }
  611.  
  612. private bool TryConnect_NE_Door(RoomInfo room, RoomInfo nextRoom, RoomDoorInfo roomDoor, RoomDoorInfo nextRoomDoor, ref Vector2 cross)
  613. {
  614. var offset1 = 0;
  615. var offset2 = 0;
  616. roomDoor.Direction = DoorDirection.N; //↑
  617. nextRoomDoor.Direction = DoorDirection.E; //→
  618.  
  619. if (!FindCrossPassage(room, nextRoom, roomDoor, nextRoomDoor, ref offset1, ref offset2))
  620. {
  621. return false;
  622. }
  623. roomDoor.OriginPosition = new Vector2(room.GetHorizontalStart() + offset1, room.GetVerticalStart());
  624. nextRoomDoor.OriginPosition = new Vector2(nextRoom.GetHorizontalEnd(),
  625. nextRoom.GetVerticalStart() + offset2);
  626. cross = new Vector2(roomDoor.OriginPosition.X, nextRoomDoor.OriginPosition.Y);
  627. return true;
  628. }
  629.  
  630. private bool TryConnect_WS_Door(RoomInfo room, RoomInfo nextRoom, RoomDoorInfo roomDoor, RoomDoorInfo nextRoomDoor, ref Vector2 cross)
  631. {
  632. //ok
  633. var offset1 = 0;
  634. var offset2 = 0;
  635. roomDoor.Direction = DoorDirection.W; //←
  636. nextRoomDoor.Direction = DoorDirection.S; //↓
  637. if (!FindCrossPassage(room, nextRoom, roomDoor, nextRoomDoor, ref offset1, ref offset2))
  638. {
  639. return false;
  640. }
  641. roomDoor.OriginPosition = new Vector2(room.GetHorizontalStart(), room.GetVerticalStart() + offset1);
  642. nextRoomDoor.OriginPosition = new Vector2(nextRoom.GetHorizontalStart() + offset2, nextRoom.GetVerticalEnd());
  643. cross = new Vector2(nextRoomDoor.OriginPosition.X, roomDoor.OriginPosition.Y);
  644. return true;
  645. }
  646.  
  647. private bool TryConnect_SE_Door(RoomInfo room, RoomInfo nextRoom, RoomDoorInfo roomDoor, RoomDoorInfo nextRoomDoor, ref Vector2 cross)
  648. {
  649. var offset1 = 0;
  650. var offset2 = 0;
  651. roomDoor.Direction = DoorDirection.S; //↓
  652. nextRoomDoor.Direction = DoorDirection.E; //→
  653. if (!FindCrossPassage(room, nextRoom, roomDoor, nextRoomDoor, ref offset1, ref offset2))
  654. {
  655. return false;
  656. }
  657.  
  658. roomDoor.OriginPosition = new Vector2(room.GetHorizontalStart() + offset1, room.GetVerticalEnd());
  659. nextRoomDoor.OriginPosition = new Vector2(nextRoom.GetHorizontalEnd(),
  660. nextRoom.GetVerticalStart() + offset2);
  661. cross = new Vector2(roomDoor.OriginPosition.X, nextRoomDoor.OriginPosition.Y);
  662. return true;
  663. }
  664.  
  665. private bool TryConnect_WN_Door(RoomInfo room, RoomInfo nextRoom, RoomDoorInfo roomDoor, RoomDoorInfo nextRoomDoor, ref Vector2 cross)
  666. {
  667. var offset1 = 0;
  668. var offset2 = 0;
  669. roomDoor.Direction = DoorDirection.W; //←
  670. nextRoomDoor.Direction = DoorDirection.N; //↑
  671. if (!FindCrossPassage(room, nextRoom, roomDoor, nextRoomDoor, ref offset1, ref offset2))
  672. {
  673. return false;
  674. }
  675.  
  676. roomDoor.OriginPosition =
  677. new Vector2(room.GetHorizontalStart(), room.GetVerticalStart() + offset1); //
  678. nextRoomDoor.OriginPosition = new Vector2(nextRoom.GetHorizontalStart() + offset2,
  679. nextRoom.GetVerticalStart());
  680. cross = new Vector2(nextRoomDoor.OriginPosition.X, roomDoor.OriginPosition.Y);
  681. return true;
  682. }
  683. private bool TryConnect_ES_Door(RoomInfo room, RoomInfo nextRoom, RoomDoorInfo roomDoor, RoomDoorInfo nextRoomDoor, ref Vector2 cross)
  684. {
  685. var offset1 = 0;
  686. var offset2 = 0;
  687. roomDoor.Direction = DoorDirection.E; //→
  688. nextRoomDoor.Direction = DoorDirection.S; //↓
  689.  
  690. if (!FindCrossPassage(room, nextRoom, roomDoor, nextRoomDoor, ref offset1, ref offset2))
  691. {
  692. return false;
  693. }
  694. roomDoor.OriginPosition = new Vector2(room.GetHorizontalEnd(), room.GetVerticalStart() + offset1);
  695. nextRoomDoor.OriginPosition = new Vector2(nextRoom.GetHorizontalStart() + offset2,
  696. nextRoom.GetVerticalEnd());
  697. cross = new Vector2(nextRoomDoor.OriginPosition.X, roomDoor.OriginPosition.Y);
  698. return true;
  699. }
  700. private bool TryConnect_NW_Door(RoomInfo room, RoomInfo nextRoom, RoomDoorInfo roomDoor, RoomDoorInfo nextRoomDoor, ref Vector2 cross)
  701. {
  702. var offset1 = 0;
  703. var offset2 = 0;
  704. roomDoor.Direction = DoorDirection.N; //↑
  705. nextRoomDoor.Direction = DoorDirection.W; //←
  706.  
  707. if (!FindCrossPassage(room, nextRoom, roomDoor, nextRoomDoor, ref offset1, ref offset2))
  708. {
  709. return false;
  710. }
  711. roomDoor.OriginPosition = new Vector2(room.GetHorizontalStart() + offset1, room.GetVerticalStart());
  712. nextRoomDoor.OriginPosition = new Vector2(nextRoom.GetHorizontalStart(),
  713. nextRoom.GetVerticalStart() + offset2);
  714. cross = new Vector2(roomDoor.OriginPosition.X, nextRoomDoor.OriginPosition.Y);
  715. return true;
  716. }
  717. private bool TryConnect_EN_Door(RoomInfo room, RoomInfo nextRoom, RoomDoorInfo roomDoor, RoomDoorInfo nextRoomDoor, ref Vector2 cross)
  718. {
  719. var offset1 = 0;
  720. var offset2 = 0;
  721. roomDoor.Direction = DoorDirection.E; //→
  722. nextRoomDoor.Direction = DoorDirection.N; //↑
  723.  
  724. if (!FindCrossPassage(room, nextRoom, roomDoor, nextRoomDoor, ref offset1, ref offset2))
  725. {
  726. return false;
  727. }
  728. roomDoor.OriginPosition = new Vector2(room.GetHorizontalEnd(),
  729. room.GetVerticalStart() + offset1);
  730. nextRoomDoor.OriginPosition = new Vector2(nextRoom.GetHorizontalStart() + offset2, nextRoom.GetVerticalStart());
  731. cross = new Vector2(nextRoomDoor.OriginPosition.X, roomDoor.OriginPosition.Y);
  732. return true;
  733. }
  734.  
  735. private bool TryConnect_SW_Door(RoomInfo room, RoomInfo nextRoom, RoomDoorInfo roomDoor, RoomDoorInfo nextRoomDoor, ref Vector2 cross)
  736. {
  737. var offset1 = 0;
  738. var offset2 = 0;
  739. roomDoor.Direction = DoorDirection.S; //↓
  740. nextRoomDoor.Direction = DoorDirection.W; //←
  741.  
  742. if (!FindCrossPassage(room, nextRoom, roomDoor, nextRoomDoor, ref offset1, ref offset2))
  743. {
  744. return false;
  745. }
  746. roomDoor.OriginPosition = new Vector2(room.GetHorizontalStart() + offset1,
  747. room.GetVerticalEnd());
  748. nextRoomDoor.OriginPosition = new Vector2(nextRoom.GetHorizontalStart(), nextRoom.GetVerticalStart() + offset2);
  749. cross = new Vector2(roomDoor.OriginPosition.X, nextRoomDoor.OriginPosition.Y);
  750. return true;
  751. }
  752.  
  753. /// <summary>
  754. /// 查找房间的连接通道, 函数返回是否找到对应的门, 通过 result 返回 x/y 轴坐标
  755. /// </summary>
  756. /// <param name="room">第一个房间</param>
  757. /// <param name="nextRoom">第二个房间</param>
  758. /// <param name="direction">第一个房间连接方向</param>
  759. private List<Vector2I> FindPassage(RoomInfo room, RoomInfo nextRoom, DoorDirection direction)
  760. {
  761. var room1 = room.RoomSplit.RoomInfo;
  762. var room2 = nextRoom.RoomSplit.RoomInfo;
  763. //用于存储符合生成条件的区域
  764. var rangeList = new List<Vector2I>();
  765. foreach (var doorAreaInfo1 in room1.DoorAreaInfos)
  766. {
  767. if (doorAreaInfo1.Direction == direction)
  768. {
  769. //第二个门的方向
  770. var direction2 = GetReverseDirection(direction);
  771. foreach (var doorAreaInfo2 in room2.DoorAreaInfos)
  772. {
  773. if (doorAreaInfo2.Direction == direction2)
  774. {
  775. Vector2 range;
  776. if (direction == DoorDirection.E || direction == DoorDirection.W) //第二个门向← 或者 第二个门向→
  777. {
  778. range = CalcOverlapRange(
  779. room.GetVerticalStart() * TileCellSize + doorAreaInfo1.Start, room.GetVerticalStart() * TileCellSize + doorAreaInfo1.End,
  780. nextRoom.GetVerticalStart() * TileCellSize + doorAreaInfo2.Start, nextRoom.GetVerticalStart() * TileCellSize + doorAreaInfo2.End
  781. );
  782. }
  783. else //第二个门向↑ 或者 第二个门向↓
  784. {
  785. range = CalcOverlapRange(
  786. room.GetHorizontalStart() * TileCellSize + doorAreaInfo1.Start, room.GetHorizontalStart() * TileCellSize + doorAreaInfo1.End,
  787. nextRoom.GetHorizontalStart() * TileCellSize + doorAreaInfo2.Start, nextRoom.GetHorizontalStart() * TileCellSize + doorAreaInfo2.End
  788. );
  789. }
  790. //交集范围够生成门
  791. if (range.Y - range.X >= CorridorWidth * TileCellSize)
  792. {
  793. rangeList.Add(new Vector2I((int)(range.X / 16), (int)(range.Y / 16) - CorridorWidth));
  794. }
  795. }
  796. }
  797. }
  798. }
  799. return rangeList;
  800. }
  801. /// <summary>
  802. /// 用于计算重叠区域坐标, 可以理解为一维轴上4个点的中间两个点, 返回的x为起始点, y为结束点
  803. /// </summary>
  804. private Vector2 CalcOverlapRange(float start1, float end1, float start2, float end2)
  805. {
  806. return new Vector2(Mathf.Max(start1, start2), Mathf.Min(end1, end2));
  807. }
  808.  
  809. /// <summary>
  810. /// 返回 p1 - p2 是否在 start - end 范围内
  811. /// </summary>
  812. private bool IsInRange(float start, float end, float p1, float p2)
  813. {
  814. return p1 >= start && p2 <= end;
  815. }
  816. //返回指定方向的反方向
  817. //0上, 1右, 2下, 3左
  818. private int GetReverseDirection(int direction)
  819. {
  820. switch (direction)
  821. {
  822. case 0: return 2;
  823. case 1: return 3;
  824. case 2: return 0;
  825. case 3: return 1;
  826. }
  827.  
  828. return 2;
  829. }
  830. //返回参数方向的反方向
  831. private DoorDirection GetReverseDirection(DoorDirection direction)
  832. {
  833. switch (direction)
  834. {
  835. case DoorDirection.E:
  836. return DoorDirection.W;
  837. case DoorDirection.W:
  838. return DoorDirection.E;
  839. case DoorDirection.S:
  840. return DoorDirection.N;
  841. case DoorDirection.N:
  842. return DoorDirection.S;
  843. }
  844.  
  845. return DoorDirection.S;
  846. }
  847.  
  848. //将两个门间的过道占用数据存入RoomGrid
  849. private bool AddCorridorToGridRange(RoomDoorInfo door1, RoomDoorInfo door2)
  850. {
  851. var point1 = door1.OriginPosition;
  852. var point2 = door2.OriginPosition;
  853. var pos = new Vector2(Mathf.Min(point1.X, point2.X), Mathf.Min(point1.Y, point2.Y));
  854. var size = new Vector2(
  855. point1.X == point2.X ? CorridorWidth : Mathf.Abs(point1.X - point2.X),
  856. point1.Y == point2.Y ? CorridorWidth : Mathf.Abs(point1.Y - point2.Y)
  857. );
  858.  
  859. Vector2 collPos;
  860. Vector2 collSize;
  861. if (point1.X == point2.X) //纵向加宽, 防止贴到其它墙
  862. {
  863. collPos = new Vector2(pos.X - 3, pos.Y);
  864. collSize = new Vector2(size.X + 6, size.Y);
  865. }
  866. else //横向加宽, 防止贴到其它墙
  867. {
  868. collPos = new Vector2(pos.X, pos.Y - 3);
  869. collSize = new Vector2(size.X, size.Y + 6);
  870. }
  871.  
  872. if (_roomGrid.RectCollision(collPos, collSize))
  873. {
  874. return false;
  875. }
  876.  
  877. _roomGrid.AddRect(pos, size, true);
  878. return true;
  879. }
  880.  
  881. //将两个门间的过道占用数据存入RoomGrid, 该重载加入拐角点
  882. private bool AddCorridorToGridRange(RoomDoorInfo door1, RoomDoorInfo door2, Vector2 cross)
  883. {
  884. var point1 = door1.OriginPosition;
  885. var point2 = door2.OriginPosition;
  886. var pos1 = new Vector2(Mathf.Min(point1.X, cross.X), Mathf.Min(point1.Y, cross.Y));
  887. var size1 = new Vector2(
  888. point1.X == cross.X ? CorridorWidth : Mathf.Abs(point1.X - cross.X),
  889. point1.Y == cross.Y ? CorridorWidth : Mathf.Abs(point1.Y - cross.Y)
  890. );
  891. var pos2 = new Vector2(Mathf.Min(point2.X, cross.X), Mathf.Min(point2.Y, cross.Y));
  892. var size2 = new Vector2(
  893. point2.X == cross.X ? CorridorWidth : Mathf.Abs(point2.X - cross.X),
  894. point2.Y == cross.Y ? CorridorWidth : Mathf.Abs(point2.Y - cross.Y)
  895. );
  896.  
  897. Vector2 collPos1;
  898. Vector2 collSize1;
  899. if (point1.X == cross.X) //纵向加宽, 防止贴到其它墙
  900. {
  901. collPos1 = new Vector2(pos1.X - 3, pos1.Y);
  902. collSize1 = new Vector2(size1.X + 6, size1.Y);
  903. }
  904. else //横向加宽, 防止贴到其它墙
  905. {
  906. collPos1 = new Vector2(pos1.X, pos1.Y - 3);
  907. collSize1 = new Vector2(size1.X, size1.Y + 6);
  908. }
  909.  
  910. if (_roomGrid.RectCollision(collPos1, collSize1))
  911. {
  912. return false;
  913. }
  914.  
  915. Vector2 collPos2;
  916. Vector2 collSize2;
  917. if (point2.X == cross.X) //纵向加宽, 防止贴到其它墙
  918. {
  919. collPos2 = new Vector2(pos2.X - 3, pos2.Y);
  920. collSize2 = new Vector2(size2.X + 6, size2.Y);
  921. }
  922. else //横向加宽, 防止贴到其它墙
  923. {
  924. collPos2 = new Vector2(pos2.X, pos2.Y - 3);
  925. collSize2 = new Vector2(size2.X, size2.Y + 6);
  926. }
  927.  
  928. if (_roomGrid.RectCollision(collPos2, collSize2))
  929. {
  930. return false;
  931. }
  932.  
  933. _roomGrid.AddRect(pos1, size1, true);
  934. _roomGrid.AddRect(pos2, size2, true);
  935. return true;
  936. }
  937. }