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