如何阻止实例化的预制件重叠 - Unity 2D?

How to stop Instantiated prefabs from overlapping - Unity 2D?

正如标题所说,我想知道如何才能阻止预制件重叠?

这是我当前的代码

{
    List<GameObject> prefabList = new List<GameObject>();
    public GameObject Room1;
    public GameObject Room2;
    public GameObject Room3;


    void Start()
    {
        prefabList.Add(Room1);
        prefabList.Add(Room2);
        prefabList.Add(Room3);

        for (int i = 0; i < 5; i++) 
        {

            int prefabIndex = UnityEngine.Random.Range(0, prefabList.Count - 0);
            var position = new Vector3(Random.Range(-20.0f, 20.0f), Random.Range(-10.0f, 10.0f));
            Instantiate(prefabList[prefabIndex], position, Quaternion.identity);
        }
    }
}

我想重新尝试将预制件放置在不同的位置,但不知道该怎么做?

感谢任何帮助。

首先你需要弄清楚你的房间有多大。一旦你有了一个变量中的数据,我们就称之为 offSet。一旦创建了第一个房间,您需要一个 if 语句来查看下一个预制件的位置是否有房间。如果有,则将第二个预制件偏移房间长度或宽度的距离。

Instantiate(prefabList[prefabIndex], offSet, Quaternion.identity);

有几种方法可以解决这个问题,但这是一个有趣的问题。

用这行代码:

var position = new Vector3(Random.Range(-20.0f, 20.0f), Random.Range(-10.0f, 10.0f));

您在整个房间内随机分布对象,但您可能 运行 在房间中无法生存 space,生成的对象数量更多。

游戏有几种方法可以解决“重叠对象”的问题,但我更喜欢在这种情况下使用网格系统,而不是尝试在关卡生成中涉及物理学。

网格系统

您应该确定要生成的最大预制对象,然后将您的地图分解成具有该大小或更大单元格的网格。然后,您可以创建一个 元组列表 ,每个元组代表该网格上的一个单元格。从列表中随机select 1个元组,并在生成对象时将其删除,以保证该区域不会出现其他对象。

    [SerializeField]
    List<GameObject> prefabList = new List<GameObject>();
    [SerializeField]
    int numberToSpawn = 5;
    [SerializeField]
    Vector3 mapAnchor = Vector3.zero;
    [SerializeField]
    float mapWidth = 40f;
    [SerializeField]
    float mapHeight = 20f;
    [SerializeField]
    float cellSize = 1f;
    // Start is called before the first frame update
    void Start()
    {
        //calculate the number of cells along the width and height of the map
        int cellWidth = Mathf.RoundToInt(mapWidth / cellSize);
        int cellHeight = Mathf.RoundToInt(mapHeight / cellSize);
        //Generate a list of tuples that represent each individual cell on the map
        List<(int,int)> cells = gridInstantiate(cellWidth, cellHeight);

        for (int i = 0; i<5; i++)
        {
            
            //Randomly select the cell on the grid
            int randCellIndex = Random.Range(0, cells.Count);
            (int x, int y) = cells[randCellIndex];
            cells.RemoveAt(randCellIndex);
            //Instantiate the object at x and y on the grid, converting it back into world space
            Vector3 position = mapAnchor;
            position.x = position.x + x * cellSize;
            position.y = position.y + y * cellSize;
            Instantiate(prefabList[Random.Range(0, prefabList.Count)], position, Quaternion.identity);
        }
    }

    List<(int,int)> gridInstantiate(int cellWidth, int cellHeight)
    {
        List<(int,int)> cells = new List<(int, int)>();
        for (int i = 0; i < mapWidth; i++)
        {
            for (int j = 0; j < mapHeight; j++)
            {
                cells.Add((i, j));
            }
        }
        return cells;
    }

这就是用上面的代码生成随机圆圈的样子:

为了使对象更自然地生成,您可以添加一些额外的代码来平滑它,方法是为每个单元格提供一个缓冲区,并允许实例化在该缓冲区内随机化作为偏移量,以防止它们如此完美地对齐:

再添加一个属性:

    [SerializeField]
    float cellBuffer = 1f;

修改cellheight/cellwidth计算:

        int cellWidth = Mathf.RoundToInt(mapWidth / (cellSize + cellBuffer));
        int cellHeight = Mathf.RoundToInt(mapHeight / (cellSize + cellBuffer));

在缓冲区范围内给位置一个随机偏移量:

            Vector3 offset = new Vector3(Random.Range(-cellBuffer / 2, cellBuffer / 2), Random.Range(-cellBuffer / 2, cellBuffer / 2), 0);
            Instantiate(prefabList[Random.Range(0, prefabList.Count)], position + offset, Quaternion.identity);

这是上面代码更改添加的内容,另一个等距钻石精灵设置为另一个潜在的预制件,numberToSpawn 设置为 30:

为什么要这样做,而不是随机生成并检查重叠?

如果您随机生成并检查重叠并且新对象的 space 为零,则有可能 运行 进入竞争条件。您要么不生成对象,要么继续尝试生成对象并失败。使用此策略,单元格列表将包含地图上所有剩余的“开放”位置以在其上生成对象,因此您无需执行任何重叠检查。此外,当该列表为空时,您可以知道没有更多空间来生成对象。