Unity - Grid generation 和 Tile neighbors,如何阅读它们?

Unity - Grid generation and Tile neighbours, How do I read them?

所以目前我正在构建一个小游戏,我正在使用元胞自动机来生成世界。

所以我的问题来了。 我正在尝试找到一种方法来读取和分配 Tiles 的邻居。

就像一个 Tile 将被创建,它的 4 个邻居(上面,右边,左边和下面)将被分配给它,它可以通过代码访问这些 Tile。 (世界生成需要)

我不想使用 Rayscast 或 Spheres 来检测邻居,因为它会降低性能并很快变得混乱

几周来我一直在努力寻找一种方法,但我不明白应该如何解决这个问题

这是我的世界生成代码。

public class WorldGenerator : MonoBehaviour
{
    public int mapSize;
    public string worldSeed;
    public bool useRandomSeed;

    [Range(0, 100)]
    public int percentOfMapIsLand;
    public GameObject tile;
    public List<GameObject> tiles;

    public int[,] mapFilled;

    // Start is called before the first frame update
    void Start()
    {
        RandomlyFillMap();
        GenerateStartingGrid();
        AssignTileNeighbors();
    }

    void RandomlyFillMap()
    {
        mapFilled = new int[mapSize, mapSize];

        if (useRandomSeed) { worldSeed = Time.time.ToString(); }

        System.Random randomNumber = new System.Random(worldSeed.GetHashCode());

        for (int x = 0; x < mapSize; x++)
        {
            for (int y = 0; y < mapSize; y++)
            {
                mapFilled[x, y] = (randomNumber.Next(0, 100) < percentOfMapIsLand) ? 1 : 0;
            }
        }
    }

    void GenerateStartingGrid() 
    {
        for (int x = 0; x < mapSize; x++)
        {
            for (int y = 0; y < mapSize; y++)
            {
                if( mapFilled[x,y] == 0) 
                {
                    Vector3 tilePosition = new Vector3(-mapSize / 2 + x, 0, -mapSize / 2 + y);

                    GameObject newTile = Instantiate(tile, tilePosition, Quaternion.Euler(Vector3.right * 90f)) as GameObject;
                    newTile.transform.SetParent(transform, false);
                    tiles.Add(newTile);        
                }
                else { continue; }
                
            }
        }
    }

    void AssignTileNeighbors() 
    {
        for (int x = 0; x < mapSize; x++)
        {
            for (int y = 0; y < mapSize; y++)
            {
                //ASIGN NEIGHBOURS HERE//


            }
        }
    }
}

这是我的 Tile 对象代码

 public class Tile : MonoBehaviour
{
    public GameObject neighbor_UP;
    public GameObject neighbor_RIGHT;
    public GameObject neighbor_LEFT;
    public GameObject neighbor_DOWN;
}

首先,您不仅需要存储 int 值,还需要存储您的图块,因此我会将值与图块一起存储。

public class Tile : MonoBehaviour
{
    public bool IsLand;

    // What you have you have ;)
    public Vector2Int GridPosition;

    public Tile neighbor_UP;
    public Tile neighbor_RIGHT;
    public Tile neighbor_LEFT;
    public Tile neighbor_DOWN;
}

然后让我们假设网格看起来像

Y
^ 03 13 23 33
| 02 12 22 32
| 01 11 21 31  
| 00 10 20 30
  ----------> X

其中第一个值为 x 第二个值为 ymapSize = 4 正如您所见,您可以通过执行操作轻松地遍历图块(例如当前图块为 21)

  • 向上:y + 1,例子:2122
  • 向下:y - 1,例子:2120
  • 左:x - 1,示例:2112
  • 右:x + 1,例子:2132

那么你可以做

public class WorldGenerator : MonoBehaviour
{
    public int mapSize;
    public string worldSeed;
    public bool useRandomSeed;

    [Range(0, 100)]
    public int percentOfMapIsLand;
    // Make your prefabs of type Tile instead
    public Tile tile;

    // instead store Tile instances
    public Tile[,] mapFilled;

    void Start()
    {
        RandomlyFillMap();
        AssignTileNeighbors();
    }

    void RandomlyFillMap()
    {
        // In one go cretae the random values and all tile instances
        mapFilled = new Tile[mapSize, mapSize];

        if (useRandomSeed) 
        { 
            worldSeed = Time.time.ToString(); 
        }

        var randomNumber = new System.Random(worldSeed.GetHashCode());

        for (int x = 0; x < mapSize; x++)
        {
            for (int y = 0; y < mapSize; y++)
            {
                // To make things easier I would rather create all the tiles
                // Even though this means a bit more overhead ONCE
                // That's up to you ofcourse .. you could as well just not create them and then 
                // the according values in the neighbors would be not assigned
                var isLand = randomNumber.Next(0, 100) < percentOfMapIsLand;

                var tilePosition = new Vector3(-mapSize / 2 + x, 0, -mapSize / 2 + y);
                var newTile = Instantiate(tile, tilePosition, Quaternion.Euler(Vector3.right * 90f));
                newTile.transform.SetParent(transform, false);
                newTile.IsLand = isLand; 
                newTile.GridPosition = new Vector2Int(x, y);
                
                // Then simply disable the tiles you don't need
                newTile.SetActive(isLand); 
                // And store them in the map
                mapFilled[x, y] = newTile;
            }
        }
    }

    void AssignTileNeighbors() 
    {
        for (int x = 0; x < mapSize; x++)
        {
            for (int y = 0; y < mapSize; y++)
            {
                // And now we just use the rules from before to get the neighbors
                // for the edges the neighbors will stay unassigned
                var tile = mapFilled[x,y];
                // For each assignment we check whether the neighbor would still be within the map bounds
                // if not, that means we are an edge tile and there exists no further neighbor
                tile.neighbor_UP = y < mapSize - 1 ? mapFilled[x, y + 1];
                tile.neighbor_DOWN = y > 0 ? mapFilled[x, y - 1];
                tile.neighbor_LEFT = x > 0 ? mapFilled[x - 1, y];
                tile.neighbor_RIGHT = x < mapSize - 1 ? mapFilled[x + 1, y];
            }
        }
    }