Adding Power Ball Power-Up

Hello,

Before I dive into to Laser Defender section, I want to add Power/Fire Ball power-up to my game. I’ve found this topic related to what I want: [Solved]BoxCollider2D IsTrigger through the Script

I can call isTrigger true and get the power sometimes, but sometimes it doesn’t work and this line of error appears on Console: (For second time picking up the powerup, I always get the error)

MissingReferenceException: The object of type 'Block' has been destroyed but you are still trying to access it.
Your script should either check if it is null or you should not destroy the object.

Here is what I do:

  1. I have this static variable within Block class:
public static List<Block> blocks;
  1. This code on Start method of Block class to get array of Blocks
  if (blocks == null || blocks.Count <= 0)
        {
            // written long-hand to make it easier to read
            Block[] tempBlocks = FindObjectsOfType<Block>();    // gets the BrickPc game objects as an array
            blocks = new List<Block>(tempBlocks);    // initialise our list from an array
            Debug.Log(blocks);
        }
  1. And I remove destroyed blocks from array (Here may be where I am wrong):
   private void DestroyBlock()
    {
        PlayBlockDestroyedSFX();
        blocks.Remove(this);
        Destroy(gameObject);
        level.BlockDestroyed();
        TriggerSparklesVFX();
        PowerUpInstantiate();

    }
  1. Here I start coroutine in Paddle class when powerup object collides with paddle:
if (other.gameObject.name == "powerUpPowerBall(Clone)")
        {
            StartCoroutine(PowerBall());
        }
  1. Finally, I get isTrigger to true:
 IEnumerator PowerBall()
    {

        LoadPowerBallSprite();

        foreach (Block blocks in Block.blocks)
        {
           
                blocks.GetComponent<BoxCollider2D>().isTrigger = true;
            
        }

        yield return new WaitForSeconds(30);

        LoadRegularBallSprite();

        foreach (Block blocks in Block.blocks)
        {
            blocks.GetComponent<BoxCollider2D>().isTrigger = false;
        }
    }

Hello again :slight_smile:

A little harder to diagnose this one with the details provided.

Which script is generating the error message and on which line, and obviously post the full script so that it is a bit more in context.

The error message is telling you what the problem is, you are trying to reference something that doesn’t exist anymore, so it could be related to the removal from the list, but that would depend on what the other methods which are called after that do. Equally, it could be something else but with out the specific details its hard to tell from this end.

If the error is reproduce-able (easily) I would be happy to take a look at your project if you want to share it, needs to be the entire project files - not a build and not just an export.

The forum will allow uploads of up to 10MB, if your project files (zipped) are larger than that you would need to use a service such as Google Drive or Dropbox, and then share the URL.

Hello, I’ve uploaded my project to WeTransfer (29.3MB): https://we.tl/t-PabgbxqKpa

You can active Power-Ball with Yellow Up sprites. You can simply re-produce error by playing. I am sorry if my code is messy :slight_smile: Thank you very much!

Hi Tugberk,

So, I ran your game and I see no error messages.

I dragged the yellow PowerUpPowerBall prefab into the scene, just above the paddle and ran the game, again it ran without any errors.

Can you perhaps provide more detailed steps of how to produce the actual error?

Hello,

Sorry, my mistake. You need to play the game and active Powerball powerup. I get the error when trying to activate Powerball. But, sometimes I don’t get the error and Powerball gets activated. I guess it is about timing. I believe you’ll get the error if you play 3-4 times.

When you say “activate the Powerball” - what does that actually mean? Do you mean I need to collect the yellow power-up which falls from a block?

Yes exactly, it is when I get the error

Ok, so for testing purposes, how do you limit your power-ups so that only the PowerBall power-up is available in your game? Is this what the array of “Ball Types” is on the Player GameObject?


Updated Thu Jan 31 2019 20:04

Don’t worry, I see it, its just in code, a bit of randomness


Updated Thu Jan 31 2019 20:11

Tweaked a couple of things to make the problem occur more reliably.

First, I change PowerUpInstantiate within Block.cs to set whichPowerUp to 4 instead of a random value, we don’t care about the other power-ups at this point;

whichPowerUp = 4;

Next, I changed PowerBall within Paddle.cs so that the yield was for only 3 seconds instead of 30;

yield return new WaitForSeconds(3);

When I run the game now the only powerups I get are the PowerBall and the error is produced.

If I collect a power-up and then pause the game I can step through frame by frame. After the 3 seconds of power-up has run out, your error occurs - if - a block has been destroyed.

As the physics engine is being used to detect collisions, and this takes place more than once per frame, I suspect what is happening is that the collision occurs with the ball and the block, the block is destroyed, but it has not yet been removed from your list of blocks in Block.cs, and for a briefly moment you try to access an object that no longer exists.

To protect against this you can put a check around your code where you try to access the blocks in your list, specifically, in Paddle.cs in the PowerBall method;

IEnumerator PowerBall()
{

    LoadPowerBallSprite();

    foreach (Block blocks in Block.blocks)
    {
        if(blocks)
        {
            blocks.GetComponent<BoxCollider2D>().isTrigger = true;
        }     
    }

    yield return new WaitForSeconds(3);

    LoadRegularBallSprite();

    foreach (Block blocks in Block.blocks)
    {
        if (blocks)
        {
            blocks.GetComponent<BoxCollider2D>().isTrigger = false;
        }
            
    }
}

In the above you can see the two if statements I have added, which basically say, whilst iterating through the list of blocks, only if the block exists at this moment in time, change the isTrigger property.

Running the game after these changes I was unable to reproduce the error.

Hope this helps :slight_smile:

p.s. for readability I’d change your foreach statements to read like this;

foreach(Block block in Block.blocks)
{
    // ...
}

e.g for each singular in multiple :slight_smile:

No, I use Ball Types for changing ball sprite. When a block destroyed I call PowerUpInstantiate method where you can arrange power-ups:

  private void PowerUpInstantiate()
    {
        whichPowerUp = Random.Range(1, 6);
        if (whichPowerUp == 1)
        {
            Instantiate(powerUpPaddleIncrease, transform.position, powerUpPaddleIncrease.rotation);
        }
        if (whichPowerUp == 2)
        {
            Instantiate(powerUpExtraBall, transform.position, powerUpExtraBall.rotation);
        }
        if (whichPowerUp == 3)
        {
            Instantiate(powerUpPaddleDecrease, transform.position, powerUpPaddleDecrease.rotation);
        }
        if (whichPowerUp == 4)
        {
            Instantiate(powerUpPowerBall, transform.position, powerUpPowerBall.rotation);
        }
        if (whichPowerUp == 5)
        {
            Instantiate(powerUpPaddleLock, transform.position, powerUpPaddleLock.rotation);
        }
    }

Oh, I see you found it :slight_smile:

See above, you replied as I was updating my previous post and the above has a solution for you.

Incidentally, on a game play related note, have you considered what happens when the player runs out of white blocks where they can get a power-up and the one red block still remains - they will be unable to destroy it.

Thank you, I understand the problem. Actually, I’ve suspected of this and tried to change Script Execution Order to Ball > Paddle. What do you think about this approach?

Yes, I’m aware of this. Current level design is just for test purposes.

1 Like

The execution order is another possibility, it crossed my mind earlier today, however that does tuck away any potential fix (if it works), e.g. you have to remember that you’ve made that change in the execution order.

Yes, if statement looks like a better solution. I’ve added if statements to my code. I didn’t get the error, but sometimes Powerball doesn’t get activated.

1 Like

So, this would be when you collect the power-up but then nothing happens? Again, is that re-producible?

Yes, exactly. I think it is the same with the error. Instead of giving the error, nothing happens.

I would start by adding some Debug.Log statements where you detect the collision. Make sure you don’t out them in any if statements initially, just check that the console outputs a message when you have collected the power- up.

From there, start adding the Debug.Log statements in the methods which are called subsequently and both in and around any logic so you can see if the code gets called so far but then because of something else doesn’t happen.

The null check we talked about yesterday is only affecting the isTrigger property of a block, there’s no reason this would hide and issue collecting a power-up.

Hello,

I can collect the power-up and it actually loads the ball sprite for power ball I assigned. The problem is it doesn’t call isTrigger = true. I’ve tested with Debug.Log statements, this is where I have a problem:

  IEnumerator PowerBall()
    {
       
        LoadPowerBallSprite();

        foreach (Block blocks in Block.blocks)
        {
            if (blocks)
            {
                Debug.Log("Bug");
                blocks.GetComponent<BoxCollider2D>().isTrigger = true;
            }
        }

It doesn’t call Debug.Log statement here. However, I think I’ll pass this bug and go on with the course. Thank you for your help!

Hi,

That Debug.Log statement doesn’t really imply a bug, it just mea s you iterated through your collection of blocks and on each iteration, where there was a block you output a message.

A better use might be;

if(blocks) 
{
    blocks.GetComponent<BoxCollider2D>().isTrigger = true;

    if(blocks.GetComponent<BoxCollider>().isTrigger != true) 
    {
        Debug.Log(blocks.name + ' isTrigger = false') ;
    } 
} 

Unless you have something else changing the isTrigger property of the blocks you should see this message at all when the power up is enable, which would be a good thing.

Hi @Tugberk_Dilbaz, is this resolved now?


See also;

Privacy & Terms