格子地图数据保存

一个格子用一个bit保存

这种方式最简单,每个格子都用一个bit记录。

  优点:执行速度快,没有额外逻辑

  缺点:地图特别大时,数据大小也将线性增大,比如:4096*4096个格子的地图,数据大小为2MB

public class CellsDataV1
{
    public static int BitsLenToBytesLen(int bitsLen)
    {
        int bytesLen = (bitsLen + 7) / 8; // 1byte=8bit, 向上取整
        return bytesLen;
    }

    public static byte[] BitAryToBytes(BitArray bitAry)
    {
        int bytesLen = BitsLenToBytesLen(bitAry.Length);
        byte[] bytes = new byte[bytesLen];
        bitAry.CopyTo(bytes, 0);
        return bytes;
    }

    private RectInt m_Bounds = new RectInt(0, 0, 1, 1);
    public RectInt bounds
    {
        get { return m_Bounds; }
    }

    private BitArray m_BitAry;

    public void SetBounds(int x, int y, int w, int h)
    {
        m_Bounds.x = x; 
        m_Bounds.y = y;
        m_Bounds.width = w; 
        m_Bounds.height = h;
    }

    public void InitEmpty()
    {
        int size = m_Bounds.width * m_Bounds.height;
        int bytesLen = BitsLenToBytesLen(size);
        m_BitAry = new BitArray(bytesLen);
    }

    private int LocalCellXYToIdx(int localCellX, int localCellY)
    {
        int idx = localCellX + localCellY * m_Bounds.width; //先列再行
        return idx;
    }

    public void AddCellXY(int cellX, int cellY)
    {
        int localCellX = cellX - m_Bounds.x;
        int localCellY = cellY - m_Bounds.y;
        int idx = LocalCellXYToIdx(localCellX, localCellY);
        if (m_BitAry[idx]) 
            return;

        m_BitAry[idx] = true;
    }

    public bool IsCellXYOutside(int cellX, int cellY)
    {
        if (cellX < m_Bounds.x || cellY < m_Bounds.y) return true;
        if (cellX >= m_Bounds.x + m_Bounds.width) return true;
        if (cellY >= m_Bounds.x + m_Bounds.height) return true;
        return false;
    }

    public bool IsCellXYAdded(int cellX, int cellY)
    {
        int localCellX = cellX - m_Bounds.x;
        int localCellY = cellY - m_Bounds.y;
        int idx = LocalCellXYToIdx(localCellX, localCellY);
        bool ret = m_BitAry[idx];
        return ret;
    }

}

 

用矩形来记录格子信息

每个格子使用1x1矩形记录,记录的过程中检查如果小的矩形可以合并为一个大的矩形则合并为用一个大矩形记录。

  优点:记录的格子越多越密集,数据大小越小。

  缺点:记录的格子很少或很离散时,数据可能并不小;且需要执行矩形合并逻辑,会有一定消耗。

 

矩形合并例子:

00 AA AB AC 00 00     00 AA AA AA 00 00
00 AD AE AF AG 00     00 AA AA AA AB 00
00 AH AI AJ 00 00 ->  00 AA AA AA 00 00
00 00 AK AL 00 00     00 00 AC AC 00 00

从12个零碎的格子位置(1x1矩形),合并为2个大矩形+1个格子位置(1x1矩形)。

public class CellsDataV2
{
    private RectInt m_Bounds = new RectInt(0, 0, 1, 1);

    private readonly HashSet<Vector2Int> m_CellPosSet = new HashSet<Vector2Int>(); //没法被合并为矩形的格子位置

    private readonly List<RectInt> m_RectList = new List<RectInt>(); //已经合并生成的矩形

    private int m_CellPosAddedSinceLastRebuild = 0;

    public void SetBounds(int x, int y, int w, int h)
    {
        m_Bounds.x = x;
        m_Bounds.y = y;
        m_Bounds.width = w;
        m_Bounds.height = h;
    }

    public void AddCellPos(Vector2Int cellPos)
    {
        if (m_CellPosSet.Contains(cellPos) || IsCellInRectList(cellPos)) return;
        m_CellPosSet.Add(cellPos);

        m_CellPosAddedSinceLastRebuild++;
        if (m_CellPosAddedSinceLastRebuild >= 6)
            RebuildRects();
    }

    public bool IsCellXYAdded(int cellX, int cellY)
    {
        Vector2Int cellPos = new Vector2Int(cellX, cellY);
        return IsCellPosAdded(cellPos);
    }

    public bool IsCellPosAdded(Vector2Int cellPos)
    {
        if (m_CellPosSet.Contains(cellPos)) return true;
        return IsCellInRectList(cellPos);
    }

    private void RebuildRects()
    {
        var rectList = new List<RectInt>();
        bool[,] covered = new bool[m_Bounds.width, m_Bounds.height];
        
        for (int y = 0; y < m_Bounds.height; y++)
        {
            int worldY = m_Bounds.y + y;
            for (int x = 0; x < m_Bounds.width; x++)
            {
                if (!IsCellXYAdded(m_Bounds.x + x, worldY) || covered[y, x]) continue;

                //左下角开始
                int rectX = x, rectX2 = x;
                int rectY = y, rectY2 = y;

                //x方向向右扩展
                int tempRectX2 = rectX2 + 1;
                while (tempRectX2 < m_Bounds.width
                    && IsCellXYAdded(m_Bounds.x + tempRectX2, worldY)
                    && !covered[y, tempRectX2]
                    )
                {
                    rectX2 = tempRectX2;
                    tempRectX2++;
                }

                //y方向向上扩展
                bool canExpandY = true;
                int tempRectY2 = rectY2 + 1;
                while (canExpandY && tempRectY2 < m_Bounds.height)
                {
                    for (int checkX = rectX; checkX <= rectX2; checkX++) //每1列
                    {
                        if (!IsCellXYAdded(m_Bounds.x + checkX, m_Bounds.y + tempRectY2) || covered[tempRectY2, checkX])
                        {
                            canExpandY = false;
                            break;
                        }
                    }
                    if (canExpandY)
                    {
                        rectY2 = tempRectY2;
                        tempRectY2++;
                    }
                }

                //创建矩形并标记覆盖区域
                int w = rectX2 - rectX + 1;
                int h = rectY2 - rectY + 1;
                var rect = new RectInt(m_Bounds.x + rectX, m_Bounds.y + rectY, w, h);
                rectList.Add(rect);

                //更新覆盖状态
                for (int markY = rectY; markY <= rectY2; markY++)
                {
                    for (int markX = rectX; markX <= rectX2; markX++)
                    {
                        covered[markY, markX] = true;
                    }
                }

                //回退x坐标以处理当前行剩余区域
                x = rectX2;
            }
        }

        m_CellPosSet.Clear();
        m_RectList.Clear();
        m_CellPosAddedSinceLastRebuild = 0;

        foreach (var item in rectList)
        {
            if (item.width == 1 && item.height == 1)
            {
                m_CellPosSet.Add(item.position);
            }
            else
            {
                m_RectList.Add(item);
            }
        }
    }

    private bool IsCellInRectList(Vector2Int cellPos)
    {
        foreach (var rect in m_RectList)
        {
            if (rect.Contains(cellPos))
            {
                return true;
            }
        }
        return false;
    }

 

posted @ 2026-04-17 00:32  yanghui01  阅读(2)  评论(0)    收藏  举报