Newer
Older
DungeonShooting / DungeonShooting_Godot / src / framework / map / TileMapUtils.cs

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);
                }
            }
        }
    }
}