//从这个点开始获取区域,广度优先算法。 private List <CaveCoord> GetRegionTiles(int startX, int startY, TileType tileType, ref bool[,] mapFlags) { List <CaveCoord> tiles = new List <CaveCoord>(); Queue <CaveCoord> queue = new Queue <CaveCoord>(); queue.Enqueue(new CaveCoord(startX, startY)); mapFlags[startX, startY] = true; while (queue.Count > 0) { CaveCoord tile = queue.Dequeue(); //弹出队列第一个,添加到要返回的列表里面。 tiles.Add(tile); // 遍历上下左右四格 for (int i = 0; i < 4; i++) { int x = tile.tileX + GameMathf.UpDownLeftRight[i, 0]; int y = tile.tileY + GameMathf.UpDownLeftRight[i, 1]; if (IsInMapRange(x, y) && mapFlags[x, y] == false && map[x, y] == tileType) { mapFlags[x, y] = true; queue.Enqueue(new CaveCoord(x, y)); } } } return(tiles); }
//获取两点直接线段经过的点。 private List <CaveCoord> GetLine(CaveCoord from, CaveCoord to) { List <CaveCoord> line = new List <CaveCoord>(); int x = from.tileX; int y = from.tileY; int dx = to.tileX - from.tileX; int dy = to.tileY - from.tileY; bool inverted = false; int step = Math.Sign(dx); int gradientStep = Math.Sign(dy); int longest = Mathf.Abs(dx); int shortest = Mathf.Abs(dy); if (longest < shortest) { inverted = true; longest = Mathf.Abs(dy); shortest = Mathf.Abs(dx); step = Math.Sign(dy); gradientStep = Math.Sign(dx); } int gradientAccumulation = longest / 2; //梯度积累,最长边的一半。 for (int i = 0; i < longest; i++) { line.Add(new CaveCoord(x, y)); if (inverted) { y += step; } else { x += step; } gradientAccumulation += shortest; //梯度每次增长为短边的长度。 if (gradientAccumulation >= longest) { if (inverted) { x += gradientStep; } else { y += gradientStep; } gradientAccumulation -= longest; } } return(line); }
//创建两个房间的通道。 private void CreatePassage(CaveRoom roomA, CaveRoom roomB, CaveCoord tileA, CaveCoord tileB) { CaveRoom.ConnectRooms(roomA, roomB); //Debug.DrawLine(CoordToWorldPoint(tileA), CoordToWorldPoint(tileB), Color.green, 100); List <CaveCoord> line = GetLine(tileA, tileB); foreach (CaveCoord coord in line) { DrawCircle(coord, passageWidth); } }
/// <summary> /// 更新区域平均点 /// </summary> private void UpdateAverageCoord() { if (tiles.Count == 0) { Debug.LogWarning("CaveRegion tiles Count Is Zero."); averageCoord = new CaveCoord(0, 0); return; } float x = 0, y = 0; for (int i = 0; i < tiles.Count; i++) { x += tiles[i].tileX; y += tiles[i].tileY; } averageCoord = new CaveCoord(Mathf.RoundToInt(x / tiles.Count), Mathf.RoundToInt(y / tiles.Count)); }
//以点c为原点,r为半径,画圈(拆墙)。 private void DrawCircle(CaveCoord c, int r) { for (int x = -r; x <= r; x++) { for (int y = -r; y <= r; y++) { if (x * x + y * y <= r * r) { int drawX = c.tileX + x; int drawY = c.tileY + y; if (IsInMapRange(drawX, drawY)) { map[drawX, drawY] = TileType.Empty; } } } } }
/// <summary> /// 两坐标之间平方之和 /// </summary> public float SqrMagnitude(CaveCoord coordB) { return((tileX - coordB.tileX) * (tileX - coordB.tileX) + (tileY - coordB.tileY) * (tileY - coordB.tileY)); }
/// <summary> /// 获取坐标对应实际位置 /// </summary> public Vector3 GetPosition(CaveCoord tile) { return(offset + new Vector3(tile.tileX, 0, tile.tileY)); }
//把xy坐标转换成实际坐标。 private Vector3 CoordToWorldPoint(CaveCoord tile) { return(new Vector3(-width / 2 + .5f + tile.tileX, 2, -height / 2 + .5f + tile.tileY)); }
//连接各个房间。每个房间两两比较,找到最近房间(相对前一个房间)连接之,对第二个房间来说不一定就是最近的。 //第二个参数为False时,第一步操作:为所有房间都连接到最近房间。 //第二个参数为True时,第二步操作:就是把所有房间都连接到主房间。 private void ConnectClosestRooms(List <CaveRoom> allRooms, bool forceAccessibilityFromMainRoom = false) { #region 属于第二步操作:roomListA 是还没连接到主房间的房间队列, roomListB 是已经连接到房间B的队列。 List <CaveRoom> roomListA = new List <CaveRoom>(); List <CaveRoom> roomListB = new List <CaveRoom>(); if (forceAccessibilityFromMainRoom) //是否需要强制连接(直接或间接)到主房间。 { foreach (CaveRoom room in allRooms) { if (room.isAccessibleFromMainRoom) { roomListB.Add(room); //已经连接到主房间的加到ListB。 } else { roomListA.Add(room); //没有连接到主房间的加到ListA。为空时将结束递归。 } } } else { roomListA = allRooms; roomListB = allRooms; } #endregion int bestDistance = 0; CaveCoord bestTileA = new CaveCoord(); CaveCoord bestTileB = new CaveCoord(); CaveRoom bestRoomA = new CaveRoom(); CaveRoom bestRoomB = new CaveRoom(); bool possibleConnectionFound = false; foreach (CaveRoom roomA in roomListA) //遍历没连接到主房间的ListA。 { if (!forceAccessibilityFromMainRoom) //第一步:如果没有要求连到主房间。 { possibleConnectionFound = false; //那就不能完成连接任务,需要不止一次连接。 if (roomA.connectedRooms.Count > 0) //有连接房间,跳过,继续找下一个连接房间。 { continue; } } #region 遍历roomListB,找到距离当前roomA最近的roomB。 foreach (CaveRoom roomB in roomListB) { if (roomA == roomB || roomA.IsConnected(roomB)) { continue; } for (int tileIndexA = 0; tileIndexA < roomA.edgeTiles.Count; tileIndexA++) { for (int tileIndexB = 0; tileIndexB < roomB.edgeTiles.Count; tileIndexB++) { CaveCoord tileA = roomA.edgeTiles[tileIndexA]; CaveCoord tileB = roomB.edgeTiles[tileIndexB]; int distanceBetweenRooms = (int)tileA.SqrMagnitude(tileB); //如果找到更近的(相对roomA)房间,更新最短路径。 if (distanceBetweenRooms < bestDistance || !possibleConnectionFound) { bestDistance = distanceBetweenRooms; possibleConnectionFound = true; bestTileA = tileA; bestTileB = tileB; bestRoomA = roomA; bestRoomB = roomB; } } } } #endregion //第一步:找到新的两个连接房间,但是没有要求连接主房间。创建通道。 if (possibleConnectionFound && !forceAccessibilityFromMainRoom) { CreatePassage(bestRoomA, bestRoomB, bestTileA, bestTileB); } } //第一步到第二步:当连接完所有房间,但是还没有要求全部连接到主房间,那就开始连接到主房间。 if (!forceAccessibilityFromMainRoom) { ConnectClosestRooms(allRooms, true); } //第二步:当成功找到能连接到主房间,通路,继续找一下个能需要连到主房间的房间。 if (possibleConnectionFound && forceAccessibilityFromMainRoom) { CreatePassage(bestRoomA, bestRoomB, bestTileA, bestTileB); ConnectClosestRooms(allRooms, true); } }