using System.Collections.Generic; using System.Linq; using Godot; using Godot.Collections; public static class TileMapUtils { public const int MainSource = 0; public const int MainTerrainSet = 0; public const int MainTerrain = 0; /// <summary> /// 生成自动图块和地形, 并烘焙好导航网格 /// </summary> public static Rect2I GenerateTerrain(TileMap tileMap, NavigationRegion2D navigationRegion, AutoTileConfig autoTileConfig) { var list = new List<Vector2I>(); var xStart = int.MaxValue; var yStart = int.MaxValue; var xEnd = int.MinValue; var yEnd = int.MinValue; var temp = tileMap.GetUsedCells(MapLayer.AutoFloorLayer); var autoCellLayerGrid = temp.ToHashSet(); foreach (var (x, y) in autoCellLayerGrid) { //计算范围 if (x < xStart) xStart = x; else if (x > xEnd) xEnd = x; if (y < yStart) yStart = y; else if (y > yEnd) yEnd = y; //填充墙壁 if (!autoCellLayerGrid.Contains(new Vector2I(x, y - 1))) { var left = autoCellLayerGrid.Contains(new Vector2I(x - 1, y - 1)); var right = autoCellLayerGrid.Contains(new Vector2I(x + 1, y - 1)); if (left && right) { var tileCellData1 = autoTileConfig.Wall_Vertical_SingleTop; tileMap.SetCell(MapLayer.AutoFloorLayer, new Vector2I(x, y - 2), tileCellData1.SourceId, tileCellData1.AutoTileCoords); var tileCellData2 = autoTileConfig.Wall_Vertical_SingleBottom; tileMap.SetCell(MapLayer.AutoFloorLayer, new Vector2I(x, y - 1), tileCellData2.SourceId, tileCellData2.AutoTileCoords); } else if (left) { var tileCellData1 = autoTileConfig.Wall_Vertical_LeftTop; tileMap.SetCell(MapLayer.AutoFloorLayer, new Vector2I(x, y - 2), tileCellData1.SourceId, tileCellData1.AutoTileCoords); var tileCellData2 = autoTileConfig.Wall_Vertical_LeftBottom; tileMap.SetCell(MapLayer.AutoFloorLayer, new Vector2I(x, y - 1), tileCellData2.SourceId, tileCellData2.AutoTileCoords); } else if (right) { var tileCellData1 = autoTileConfig.Wall_Vertical_RightTop; tileMap.SetCell(MapLayer.AutoFloorLayer, new Vector2I(x, y - 2), tileCellData1.SourceId, tileCellData1.AutoTileCoords); var tileCellData2 = autoTileConfig.Wall_Vertical_RightBottom; tileMap.SetCell(MapLayer.AutoFloorLayer, new Vector2I(x, y - 1), tileCellData2.SourceId, tileCellData2.AutoTileCoords); } else { var tileCellData1 = autoTileConfig.Wall_Vertical_CenterTop; tileMap.SetCell(MapLayer.AutoFloorLayer, new Vector2I(x, y - 2), tileCellData1.SourceId, tileCellData1.AutoTileCoords); var tileCellData2 = autoTileConfig.Wall_Vertical_CenterBottom; tileMap.SetCell(MapLayer.AutoFloorLayer, new Vector2I(x, y - 1), tileCellData2.SourceId, tileCellData2.AutoTileCoords); } } } //绘制临时边界 var temp1 = new List<Vector2I>(); for (var x = xStart - 3; x <= xEnd + 3; x++) { var p1 = new Vector2I(x, yStart - 5); var p2 = new Vector2I(x, yEnd + 3); temp1.Add(p1); temp1.Add(p2); //上横 tileMap.SetCell(MapLayer.AutoFloorLayer, p1, autoTileConfig.TopMask.SourceId, autoTileConfig.TopMask.AutoTileCoords); //下横 tileMap.SetCell(MapLayer.AutoFloorLayer, p2, autoTileConfig.TopMask.SourceId, autoTileConfig.TopMask.AutoTileCoords); } for (var y = yStart - 5; y <= yEnd + 3; y++) { var p1 = new Vector2I(xStart - 3, y); var p2 = new Vector2I(xEnd + 3, y); temp1.Add(p1); temp1.Add(p2); //左竖 tileMap.SetCell(MapLayer.AutoFloorLayer, p1, autoTileConfig.TopMask.SourceId, autoTileConfig.TopMask.AutoTileCoords); //右竖 tileMap.SetCell(MapLayer.AutoFloorLayer, p2, autoTileConfig.TopMask.SourceId, autoTileConfig.TopMask.AutoTileCoords); } //计算需要绘制的图块 var temp2 = new List<Vector2I>(); for (var x = xStart - 2; x <= xEnd + 2; x++) { for (var y = yStart - 4; y <= yEnd + 2; y++) { if (!autoCellLayerGrid.Contains(new Vector2I(x, y)) && !autoCellLayerGrid.Contains(new Vector2I(x, y + 1)) && !autoCellLayerGrid.Contains(new Vector2I(x, y + 2))) { list.Add(new Vector2I(x, y)); if (!IsMaskCollisionGround(autoCellLayerGrid, x, y)) { temp2.Add(new Vector2I(x, y)); } } } } var arr = new Array<Vector2I>(list); //绘制自动图块 tileMap.SetCellsTerrainConnect(MapLayer.AutoFloorLayer, arr, MainTerrainSet, MainTerrain, false); //擦除临时边界 for (var i = 0; i < temp1.Count; i++) { tileMap.EraseCell(MapLayer.AutoFloorLayer, temp1[i]); } //计算区域 var rect = Utils.CalcRect(autoCellLayerGrid); if (rect.Size != Vector2I.Zero) { rect.Position -= new Vector2I(2, 3); rect.Size += new Vector2I(4, 5); } //开始绘制导航网格 GenerateNavigation(navigationRegion, rect.Position, rect.Size); //擦除临时边界2 for (var i = 0; i < temp2.Count; i++) { tileMap.EraseCell(MapLayer.AutoFloorLayer, temp2[i]); } //将墙壁移动到指定层 MoveTerrainCell(tileMap, autoTileConfig, autoCellLayerGrid, rect.Position, rect.Size); return rect; } private static bool IsMaskCollisionGround(HashSet<Vector2I> autoCellLayerGrid, int x, int y) { for (var i = -2; i <= 2; i++) { for (var j = -2; j <= 4; j++) { if (autoCellLayerGrid.Contains(new Vector2I(x + i, y + j))) { return true; } } } return false; } //生成导航网格 private static void GenerateNavigation(NavigationRegion2D navigationRegion, Vector2I currRoomPosition, Vector2I currRoomSize) { var navigationPolygon = navigationRegion.NavigationPolygon; if (navigationPolygon != null) { navigationPolygon.Clear(); navigationPolygon.ClearPolygons(); navigationPolygon.ClearOutlines(); } else { navigationPolygon = ResourceManager.Load<NavigationPolygon>(ResourcePath.resource_navigation_NavigationPolygon_tres); navigationRegion.NavigationPolygon = navigationPolygon; } var endPos = currRoomPosition + currRoomSize; navigationPolygon.AddOutline(new [] { currRoomPosition * GameConfig.TileCellSize, new Vector2(endPos.X, currRoomPosition.Y) * GameConfig.TileCellSize, endPos * GameConfig.TileCellSize, new Vector2(currRoomPosition.X, endPos.Y) * GameConfig.TileCellSize }); navigationRegion.BakeNavigationPolygon(false); } //将自动生成的图块从 MapLayer.AutoFloorLayer 移动到指定图层中 private static void MoveTerrainCell(TileMap tileMap, AutoTileConfig autoTileConfig, HashSet<Vector2I> autoCellLayerGrid, Vector2I currRoomPosition, Vector2I currRoomSize) { tileMap.ClearLayer(MapLayer.AutoTopLayer); tileMap.ClearLayer(MapLayer.AutoMiddleLayer); var x = currRoomPosition.X; var y = currRoomPosition.Y - 1; var w = currRoomSize.X; var h = currRoomSize.Y + 1; for (var i = 0; i < w; i++) { for (var j = 0; j < h; j++) { var pos = new Vector2I(x + i, y + j); if (!autoCellLayerGrid.Contains(pos) && tileMap.GetCellSourceId(MapLayer.AutoFloorLayer, pos) != -1) { var atlasCoords = tileMap.GetCellAtlasCoords(MapLayer.AutoFloorLayer, pos); var layer = autoTileConfig.GetLayer(atlasCoords); if (layer == MapLayer.AutoMiddleLayer) { layer = MapLayer.AutoMiddleLayer; } else if (layer == MapLayer.AutoTopLayer) { layer = MapLayer.AutoTopLayer; } else { Debug.LogError($"异常图块: {pos}, 这个图块的图集坐标'{atlasCoords}'不属于'MiddleMapLayer'和'TopMapLayer'!"); continue; } tileMap.EraseCell(MapLayer.AutoFloorLayer, pos); tileMap.SetCell(layer, pos, MainSource, atlasCoords); } } } } }