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