Menu
CoddyTech

Multiball

Lesson 15 of 16 in Coddy's Unity Game: Brick Breaker course.

A Multiball power-up splits the ball: two copies fly off from where it is. Instantiate copies a scene object as it copies a prefab, but only what the Inspector would save comes across: public and [SerializeField] fields such as speed and paddle. A private field like launched starts at its default, false, and the Rigidbody's velocity starts at zero. So a copy has to be told what to do, or it rides the paddle.

With several balls in play, the GameManager keeps them in a List<Ball>, and a lost ball costs a life only when it is the last one. Slimes that split work the same way:

List<Slime> slimes = new List<Slime>();

public void Split(Slime slime)
{
    Slime half = Instantiate(slime, slime.transform.position, Quaternion.identity);
    half.Hop(Vector3.left);   // the copy's private fields start fresh: tell it what to do
    slimes.Add(half);
}

public void SlimeDied(Slime slime)
{
    slimes.Remove(slime);
    Destroy(slime.gameObject);
    if (slimes.Count == 0) Debug.Log("Room cleared");
}

The first ball in the list may be a copy, since the original can be lost while copies fly on, so code that needs the ball uses the list, not the Ball field from the scene.

challenge icon

Challenge

Hard
  1. In GameManager, add using System.Collections.Generic; and List<Ball> balls = new List<Ball>();. Start() adds ball to it first.
  2. Write public void SplitBall(): two copies of balls[0] at its position, one launched toward new Vector3(-0.6f, 1f, 0f) and one toward new Vector3(0.6f, 1f, 0f), both added to the list.
  3. Change BallLost: while more than one ball is in the list, the lost ball is removed from it and destroyed, and no life is lost. When a level is cleared, put balls[0] back on the paddle instead of ball.
  4. In PowerUp, a Multiball calls GameManager.Instance.SplitBall() when caught.

Press Play, catch a pink power-up, then Check.

Try it yourself

using UnityEngine;
using UnityEngine.SceneManagement;
using TMPro;

public class GameManager : MonoBehaviour
{
    public static GameManager Instance;

    public Brick brickPrefab;
    public Transform wall;
    public string[] levels;
    public float spacingX = 1.75f;
    public float spacingY = 0.75f;
    public float topY = 6f;

    public Ball ball;
    public int lives = 3;
    public TMP_Text scoreText;
    public TMP_Text livesText;
    public GameObject gameOverPanel;
    public GameObject[] powerUps;
    public float dropChance = 0.15f;

    int score;
    int level;
    int bricksLeft;

    void Awake()
    {
        Instance = this;
    }

    void Start()
    {
        BuildLevel(0);
        ShowHud();
    }

    void BuildLevel(int index)
    {
        level = index;
        string[] rows = levels[index].Split('/');
        for (int row = 0; row < rows.Length; row++)
        {
            string line = rows[row];
            float left = -(line.Length - 1) * spacingX / 2f;
            for (int col = 0; col < line.Length; col++)
            {
                char c = line[col];
                if (c == '.') continue;
                Vector3 pos = new Vector3(left + col * spacingX, topY - row * spacingY, 0f);
                Brick brick = Instantiate(brickPrefab, pos, Quaternion.identity, wall);
                brick.SetHits(c - '0');
                bricksLeft++;
            }
        }
    }

    public void BrickBroken(Brick brick)
    {
        score += brick.points;
        bricksLeft--;
        ShowHud();
        if (Random.value < dropChance)
        {
            GameObject prefab = powerUps[Random.Range(0, powerUps.Length)];
            Instantiate(prefab, brick.transform.position, Quaternion.identity);
        }
        if (bricksLeft == 0)
        {
            ball.ResetToPaddle();
            BuildLevel((level + 1) % levels.Length);
        }
    }

    public void BallLost(Ball lost)
    {
        lives--;
        ShowHud();
        if (lives == 0)
        {
            gameOverPanel.SetActive(true);
            lost.gameObject.SetActive(false);
        }
        else lost.ResetToPaddle();
    }

    public void PlayAgain()
    {
        SceneManager.LoadScene(SceneManager.GetActiveScene().buildIndex);
    }

    void ShowHud()
    {
        scoreText.text = "Score: " + score;
        livesText.text = "Lives: " + lives;
    }
}
quiz iconTest yourself

This lesson includes a short quiz. Start the lesson to answer it and track your progress.

All lessons in Unity Game: Brick Breaker