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

// GDnD wiki example
// Demonstrates: the game loop owning score and lives, level reset on clear, and game over on no lives
// Related pages: [[overview-arcade-classics-as-learning-projects]], [[unity-gamemanager-pattern]], [[observer-pattern]], [[ui-design]]
 
using UnityEngine;
 
// Central controller: owns score and lives, builds each level, serves the ball,
// and responds to the two events that drive Breakout — a brick destroyed and a
// level cleared. This is the step-2 version of [[unity-gamemanager-pattern]].
public class BreakoutGame : MonoBehaviour
{
    [SerializeField] private Ball2D ball;
    [SerializeField] private BrickGrid grid;
    [SerializeField] private int startingLives = 3;
    [Tooltip("Optional on-screen HUD. If empty, messages print to the Console.")]
    [SerializeField] private ArcadeHud hud;
 
    public int Score { get; private set; }
    public int Lives { get; private set; }
 
    private void OnEnable()
    {
        ball.Missed += OnBallMissed;
        grid.BrickDestroyed += OnBrickDestroyed;
        grid.LevelCleared += OnLevelCleared;
    }
 
    private void OnDisable()
    {
        ball.Missed -= OnBallMissed;
        grid.BrickDestroyed -= OnBrickDestroyed;
        grid.LevelCleared -= OnLevelCleared;
    }
 
    private void Start()
    {
        StartGame();
    }
 
    private void StartGame()
    {
        Score = 0;
        Lives = startingLives;
        RefreshHud();
        grid.BuildLevel();
        ball.Serve();
    }
 
    private void OnBrickDestroyed(int value)
    {
        Score += value;
        RefreshHud();
    }
 
    private void OnBallMissed()
    {
        Lives--;
        RefreshHud();
        ShowMessage($"Ball lost. Lives remaining: {Lives}");
 
        if (Lives > 0)
        {
            ball.Serve();
        }
        else
        {
            ShowMessage($"Game over. Final score: {Score}");
            StartGame();
        }
    }
 
    private void OnLevelCleared()
    {
        ShowMessage("Level cleared!");
        grid.BuildLevel();
        ball.Serve();
    }
 
    // HUD helpers. The game owns the numbers; ArcadeHud only displays them.
    // With no HUD assigned, messages fall back to the Console instead of throwing.
    private void RefreshHud()
    {
        if (hud != null)
        {
            hud.SetStatus($"Score {Score}   Lives {Lives}");
        }
    }
 
    private void ShowMessage(string message)
    {
        if (hud != null)
        {
            hud.ShowMessage(message);
        }
        else
        {
            Debug.Log(message);
        }
    }
}