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