✨ What You'll Learn:

  • How it all comes together

  • A fully documented version of your grid system script

✅ Full Script Example:

[System.Serializable]
public class TileRow
{
    public List tiles; // A row of tile GameObjects
}

public class InfiniteGrid : MonoBehaviour
{
    [SerializeField] private List gridRows; // Full grid made up of rows
    [SerializeField] private float tileSpacing = 50f; // Distance between each tile

    public void ScrollUp()
    {
        TileRow lastRow = gridRows[gridRows.Count - 1];
        foreach (GameObject tile in lastRow.tiles)
        {
            float newY = gridRows[0].tiles[0].transform.position.y + tileSpacing;
            tile.transform.position = new Vector3(tile.transform.position.x, newY, 0);
        }
        gridRows.Insert(0, lastRow);
        gridRows.RemoveAt(gridRows.Count - 1);
        UpdateEdgeTiles();
    }

    public void ScrollDown()
    {
        TileRow firstRow = gridRows[0];
        foreach (GameObject tile in firstRow.tiles)
        {
            float newY = gridRows[gridRows.Count - 1].tiles[0].transform.position.y - tileSpacing;
            tile.transform.position = new Vector3(tile.transform.position.x, newY, 0);
        }
        gridRows.Add(firstRow);
        gridRows.RemoveAt(0);
        UpdateEdgeTiles();
    }

    public void ScrollLeft()
    {
        foreach (TileRow row in gridRows)
        {
            GameObject lastTile = row.tiles[row.tiles.Count - 1];
            float newX = row.tiles[0].transform.position.x - tileSpacing;
            lastTile.transform.position = new Vector3(newX, lastTile.transform.position.y, 0);
            row.tiles.Insert(0, lastTile);
            row.tiles.RemoveAt(row.tiles.Count - 1);
        }
        UpdateEdgeTiles();
    }

    public void ScrollRight()
    {
        foreach (TileRow row in gridRows)
        {
            GameObject firstTile = row.tiles[0];
            float newX = row.tiles[row.tiles.Count - 1].transform.position.x + tileSpacing;
            firstTile.transform.position = new Vector3(newX, firstTile.transform.position.y, 0);
            row.tiles.Add(firstTile);
            row.tiles.RemoveAt(0);
        }
        UpdateEdgeTiles();
    }

    private void UpdateEdgeTiles()
    {
        foreach (GameObject tile in gridRows[0].tiles)
            tile.GetComponent()?.UpdateVisual();

        foreach (GameObject tile in gridRows[gridRows.Count - 1].tiles)
            tile.GetComponent()?.UpdateVisual();

        for (int rowIndex = 1; rowIndex < gridRows.Count - 1; rowIndex++)
        {
            TileRow currentRow = gridRows[rowIndex];
            currentRow.tiles[0].GetComponent()?.UpdateVisual();
            currentRow.tiles[currentRow.tiles.Count - 1].GetComponent()?.UpdateVisual();
        }
    }
}

✅ TileVisualUpdater Example:

public class TileVisualUpdater : MonoBehaviour
{
    public void UpdateVisual()
    {
        // Pick a random terrain type: 0 = Grass, 1 = Water, 2 = Dirt
        int tileType = Random.Range(0, 3);

        // Change the tile's appearance based on the type
        switch (tileType)
        {
            case 0: SetGrass(); break;
            case 1: SetWater(); break;
            case 2: SetDirt(); break;
        }
    }

    void SetGrass() {
        // Apply grass visuals
    }

    void SetWater() {
        // Apply water visuals
    }

    void SetDirt() {
        // Apply dirt visuals
    }
}