Source file unity-csharp/arcade-classics/breakout/BrickGrid.cs from the GDnD code examples. Download raw file

// GDnD wiki example
// Demonstrates: building a grid of objects from a loop, tracking them in a list, and detecting level clearance
// Related pages: [[overview-arcade-classics-as-learning-projects]], [[csharp-collections]], [[unity-prefabs-scripting]]
 
using System;
using System.Collections.Generic;
using UnityEngine;
 
// Spawns the brick wall and owns the "how many bricks are left?" question. The
// double loop and the List<Brick> are the step-2 learning targets: arrays/lists
// and level reset. See [[csharp-collections]] and [[unity-prefabs-scripting]].
public class BrickGrid : MonoBehaviour
{
    [Header("Prefab")]
    [SerializeField] private Brick brickPrefab;
 
    [Header("Layout")]
    [SerializeField] private int columns = 10;
    [SerializeField] private int rows = 5;
    [SerializeField] private Vector2 brickSize = new Vector2(1.4f, 0.5f);
    [SerializeField] private Vector2 spacing = new Vector2(0.1f, 0.1f);
    [SerializeField] private float topMargin = 1f;
 
    // Forwarded brick score, and a one-shot signal when the last brick is cleared.
    public event Action<int> BrickDestroyed;
    public event Action LevelCleared;
 
    private readonly List<Brick> activeBricks = new List<Brick>();
 
    public void BuildLevel()
    {
        ClearExistingBricks();
 
        Camera cam = Camera.main;
        float cellWidth = brickSize.x + spacing.x;
        float cellHeight = brickSize.y + spacing.y;
 
        // Centre the wall horizontally and hang it from just below the top edge.
        float startX = -((columns - 1) * cellWidth) / 2f;
        float startY = cam.orthographicSize - topMargin;
 
        for (int row = 0; row < rows; row++)
        {
            for (int column = 0; column < columns; column++)
            {
                Vector3 position = new Vector3(
                    startX + column * cellWidth,
                    startY - row * cellHeight,
                    0f
                );
 
                Brick brick = Instantiate(brickPrefab, position, Quaternion.identity, transform);
                brick.transform.localScale = new Vector3(brickSize.x, brickSize.y, 1f);
                brick.Destroyed += OnBrickDestroyed;
                activeBricks.Add(brick);
            }
        }
    }
 
    private void OnBrickDestroyed(int score)
    {
        BrickDestroyed?.Invoke(score);
 
        // Tidy the list, then check for a cleared level.
        activeBricks.RemoveAll(brick => brick == null);
        if (activeBricks.Count == 0)
        {
            LevelCleared?.Invoke();
        }
    }
 
    private void ClearExistingBricks()
    {
        foreach (Brick brick in activeBricks)
        {
            if (brick != null)
            {
                brick.Destroyed -= OnBrickDestroyed;
                Destroy(brick.gameObject);
            }
        }
 
        activeBricks.Clear();
    }
}