Search code examples
c#unity-game-enginegame-physics

GameObject counted as destroyed but not really destroyed


I am currently making a Breakout style game and have a problem with destroying bricks when there are more than 5 or 6 balls in play (there is no solid number that recreates this bug, it just happens when there are a lot of balls in play). There is a double and triple ball power up so the number of balls can increase very quickly in a short amount of time.

In my GameManager Script I keep track of the initial number of bricks at the start of the scene and subtract 1 everytime a ball comes into contact with a brick. After the number of Bricks is zero the player wins and the game is over. The game works with a small number of balls however the process seems to break down when too many balls get involved.

Relevant code for GameManager.cs:

void Start()
{
    livesText.text = "Lives: " + Lives;
    numOfBalls = GameObject.FindGameObjectsWithTag("Ball").Length;
    numOfBricks = GameObject.FindGameObjectsWithTag("Brick").Length;
    //Debug.Log(numOfBalls);
}

public void UpdateBrickNumber()
{
    numOfBricks--;
    if(numOfBricks <= 0)
    {
        numOfBricks = 0;
        GameOver();
    }
}

void GameOver()
{
    gameOver = true;
    if(numOfBricks == 0)
    {
        WinPanel.SetActive(true);
    }
    else
    {
        GameOverPanel.SetActive(true);
    }
}
...

Ball code for handling Collisions:

void OnCollisionEnter2D(Collision2D collision)
{
    if (collision.transform.CompareTag("Brick"))
    {
        Debug.Log("Brick Hit");
        //Chance of powerup to spawn
        int rand = Random.Range(1, 101);
        //Chance of each power up
        int rand2 = Random.Range(1, 101);



        if (rand < 7)
        {
            if (rand2 >=1 && rand2<=20) {
                Instantiate(SpeedBall, collision.transform.position, collision.transform.rotation);
                //Debug.Log(" Speed Power Up Created ");
            }
            ...
            Rest of  power ups
            ...
        }
        Transform newExplosion = Instantiate(explosion, collision.transform.position, collision.transform.rotation);
        Destroy(newExplosion.gameObject, 2f);
        gm.UpdateBrickNumber();
        Destroy(collision.gameObject);
    }
}

The Debug log does show that the correct number of bricks were hit but I think some balls are hitting the same brick before it has a chance to be destroyed. How can I make sure the bricks are destroyed before another ball hits the collider on the brick?

The bug in action: Picture of the bug in action


Solution

  • Going off the hunch that you're getting multiple collisions in a frame, resulting in a lower brick count than remaining number of bricks, this should fix it:

    void OnCollisionEnter2D(Collision2D collision)
    {   //Check if the brick has already been processed
        if (collision.transform.CompareTag("Brick") && collision.collider.enabled) 
        {
            Debug.Log("Brick Hit");
            //Chance of powerup to spawn
            int rand = Random.Range(1, 101);
            //Chance of each power up
            int rand2 = Random.Range(1, 101);
    
    
    
            if (rand < 7)
            {
                if (rand2 >=1 && rand2<=20) {
                    Instantiate(SpeedBall, collision.transform.position, collision.transform.rotation);
                    //Debug.Log(" Speed Power Up Created ");
                }
                ...
                Rest of  power ups
                ...
            }
            Transform newExplosion = Instantiate(explosion, collision.transform.position, collision.transform.rotation);
            Destroy(newExplosion.gameObject, 2f);
            gm.UpdateBrickNumber();
            //Mark the brick as hit already so other balls can't hit it!
            collision.collider.enabled = false; 
            Destroy(collision.gameObject);
        }
    }
    

    Explanation

    From the documentation:

    Actual object destruction is always delayed until after the current Update loop, but will always be done before rendering.

    Destroy is not instant. It will take place after the update loop, meaning if two balls hit in the same frame, you're reducing your count by two, but only destroying one brick.