📜  随机位置统一 2d - C# (1)

📅  最后修改于: 2023-12-03 15:28:51.266000             🧑  作者: Mango

随机位置统一 2D - C#

在2D游戏中,我们经常需要生成随机位置的物体,而且需要保证它们不会重叠。这里提供一种随机位置统一的解决方案,确保在整个游戏世界中,生成的随机物体不会重叠。

实现方案

我们将整个游戏场景划分为一个二维网格(grid),然后将每个网格标记为已占用或未占用。当需要生成一个随机物体时,我们只需要在一个未占用的网格位置上生成物体,然后将该网格标记为已占用。

为了实现这一方案,我们需要进行以下步骤:

  1. 划分网格:将整个游戏世界划分为n*m个网格。每个网格的大小应该与物体的大小相同或更大。
  2. 初始化网格:将所有网格标记为未占用状态。
  3. 随机生成位置:从所有未占用的网格中随机选取一个,生成物体并将该网格标记为已占用状态。

这种方式可以确保生成的物体不会重叠,并且可以在整个游戏世界中生成。但是,在某些情况下,这种方式可能会导致某些网格被占用,但是无法生成物体。例如,在网格的边缘附近,可能会没有足够的网格来生成物体。为了解决这个问题,我们可以使用动态网格(Dynamic Grid)来解决。

动态网格

动态网格是一种特殊的网格,它可以在需要时自动创建和删除。当某些网格被占用时,我们可以添加动态网格来保证能够生成物体。当动态网格不再需要时,我们可以删除它们以减少内存使用。

我们可以定义一个动态网格类,它包含以下成员:

public class DynamicGrid
{
    public int width;           // 网格的宽度
    public int height;          // 网格的高度
    public Vector2Int min;      // 网格的最小索引
    public Vector2Int max;      // 网格的最大索引
    public bool[,] occupied;    // 网格占用状态

    // 构造函数
    public DynamicGrid(int w, int h)
    {
        width = w;
        height = h;
        min = new Vector2Int(0, 0);
        max = new Vector2Int(w - 1, h - 1);
        occupied = new bool[w, h];
    }

    // 添加动态网格,使物体在该网格中心生成
    public Vector2Int AddGrid(Vector2 pos, int w, int h)
    {
        // 计算动态网格的索引
        int x = (int)(pos.x / w);
        int y = (int)(pos.y / h);

        // 如果网格已经被占用,则向四周扩展网格
        while (occupied[x, y])
        {
            // 计算当前网格周围的网格
            List<Vector2Int> neighbors = GetNeighbors(new Vector2Int(x, y));

            // 找到一个未占用的网格,生成物体
            foreach (Vector2Int neighbor in neighbors)
            {
                if (!occupied[neighbor.x, neighbor.y])
                {
                    x = neighbor.x;
                    y = neighbor.y;
                    break;
                }
            }

            // 如果周围都被占用了,则扩展网格
            if (occupied[x, y])
            {
                Expand();
            }
        }

        // 标记该网格已占用
        occupied[x, y] = true;

        // 返回网格的中心点
        return new Vector2Int((x + 0.5f) * w, (y + 0.5f) * h);
    }

    // 删除动态网格
    public void RemoveGrid(Vector2Int pos, int w, int h)
    {
        // 计算网格的索引
        int x = pos.x / w;
        int y = pos.y / h;

        // 标记网格未占用
        occupied[x, y] = false;

        // 如果该网格周围的网格都未占用,则删除该网格
        List<Vector2Int> neighbors = GetNeighbors(new Vector2Int(x, y));
        bool canRemove = true;
        foreach (Vector2Int neighbor in neighbors)
        {
            if (occupied[neighbor.x, neighbor.y])
            {
                canRemove = false;
                break;
            }
        }

        if (canRemove)
        {
            Remove(new Vector2Int(x, y));
        }
    }

    // 扩展网格
    private void Expand()
    {
        // 扩展网格边界
        if (min.x > 0) min.x--;
        if (min.y > 0) min.y--;
        if (max.x < width - 1) max.x++;
        if (max.y < height - 1) max.y++;

        // 创建新的网格状态数组
        bool[,] newOccupied = new bool[width, height];

        // 复制原来的网格状态
        for (int x = min.x; x <= max.x; x++)
        {
            for (int y = min.y; y <= max.y; y++)
            {
                newOccupied[x, y] = occupied[x, y];
            }
        }

        // 将新的网格状态赋值给原来的网格状态
        occupied = newOccupied;
    }

    // 删除动态网格
    private void Remove(Vector2Int pos)
    {
        // 移动所有网格
        for (int x = pos.x + 1; x <= max.x; x++)
        {
            for (int y = min.y; y <= max.y; y++)
            {
                occupied[x - 1, y] = occupied[x, y];
            }
        }

        // 缩小网格边界
        max.x--;

        // 创建新的网格状态数组
        bool[,] newOccupied = new bool[width, height];

        // 复制原来的网格状态
        for (int x = min.x; x <= max.x; x++)
        {
            for (int y = min.y; y <= max.y; y++)
            {
                newOccupied[x, y] = occupied[x, y];
            }
        }

        // 将新的网格状态赋值给原来的网格状态
        occupied = newOccupied;
    }

    // 获取相邻的网格
    private List<Vector2Int> GetNeighbors(Vector2Int pos)
    {
        List<Vector2Int> neighbors = new List<Vector2Int>();

        if (pos.x > min.x) neighbors.Add(new Vector2Int(pos.x - 1, pos.y));
        if (pos.x < max.x) neighbors.Add(new Vector2Int(pos.x + 1, pos.y));
        if (pos.y > min.y) neighbors.Add(new Vector2Int(pos.x, pos.y - 1));
        if (pos.y < max.y) neighbors.Add(new Vector2Int(pos.x, pos.y + 1));

        return neighbors;
    }
}
使用范例
public class RandomObject : MonoBehaviour
{
    public float width = 1.0f;
    public float height = 1.0f;
    public int gridSize = 10;

    private DynamicGrid grid;

    private void Start()
    {
        // 创建动态网格
        grid = new DynamicGrid(gridSize, gridSize);
    }

    private void Update()
    {
        // 随机生成物体的位置
        Vector2 pos = GetRandomPosition();

        // 添加动态网格并生成物体
        Vector2Int gridPos = grid.AddGrid(pos, (int)(width * gridSize), (int)(height * gridSize));
        Instantiate(gameObject, gridPos, Quaternion.identity);
    }

    private Vector2 GetRandomPosition()
    {
        float x = Random.Range(0.0f, 10.0f);
        float y = Random.Range(0.0f, 10.0f);
        return new Vector2(x, y);
    }

    private void OnDestroy()
    {
        // 删除动态网格
        grid.RemoveGrid(transform.position, (int)(width * gridSize), (int)(height * gridSize));
    }
}
参考资料

以上介绍完成,可以在C#中实现随机位置统一2D功能。