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