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