BlockBraker, Duplicating ball

Hi there,

I just finished the Block breaker part of the course and I am trying to add some power-ups to the game. One of the power-ups, I thought, could be duplicating the ball. My logic is that some of the blocks would yield an object and if you could catch it you will get a random power-up. I created a prefab and a script for the power-up and managed to make the blocks yield this prefab with a rigidbody(so it will slowly come down).
In the script, I created a switch case loop with a randomized index and added methods to the loop so that every time the player catches a power-up, it will have a random effect. I also managed to duplicate the ball and add some velocity and a new layer to it.
Yet whenever that method runs and instantiates the new ball, the lines on the ball.cs(paddleToBallDist or gs.autoPlayOn == true) are giving Null reference exception error.
This is the power-up script

public class PowerUp : MonoBehaviour
{

    Paddle paddle;
    Ball ball;
    [SerializeField]GameObject myBall;

    int powerUpArray;
    // Start is called before the first frame update
    void Start()
    {
        ball = FindObjectOfType<Ball>();
        paddle = FindObjectOfType<Paddle>();
        powerUpArray = UnityEngine.Random.Range(1, 5);
    }

    // Update is called once per frame
    void Update()
    {
        
    }

    private void OnCollisionEnter2D(Collision2D collision)
    {
        Destroy(gameObject);
        ExecuteMethodByIndex();
    }

    private void ShrinkPaddle()
    {
        paddle.transform.localScale = new Vector2(0.5f, 1);
    }

    private void EnlargePaddle()
    {
        paddle.transform.localScale = new Vector2(1.5f, 1);
    }
        
    private void NormalisePaddle()
    {
        paddle.transform.localScale = new Vector2(1, 1);
    }
    
    private void DuplicateBall()
    {
        GameObject dupBall = Instantiate(myBall, transform.position, transform.rotation);
        dupBall.GetComponent<Rigidbody2D>().velocity = new Vector2(8, 2);
        dupBall.layer = 12;
        
    }


    void ExecuteMethodByIndex()
    {
        switch (powerUpArray)
        {
            case 1: EnlargePaddle();
                break;
            case 2: ShrinkPaddle();
                break;
            case 3: NormalisePaddle();
                break;
            case 4: DuplicateBall();
                break;
            default:
                break;
        }
    }
}

and this is the ball.cs

public class Ball : MonoBehaviour
{
    //config params
    [SerializeField] Paddle paddle1;
    [SerializeField] float velX = 2f, velY = 15f;
    [SerializeField] AudioClip[] sounds;
    [SerializeField] float randomBounce = 0.1f;
  
    
    //state
    Vector2 paddleToBallDist;
    bool hasStarted = false;

    //cached references
    GameStatus gs;
    GameStatus myGameStatus;
    AudioSource myAudioSource;
    Rigidbody2D myRigidbody2D;

    // Start is called before the first frame update
    void Start()
    {
        paddleToBallDist = transform.position - paddle1.transform.position;
        myAudioSource = GetComponent<AudioSource>();
        myRigidbody2D = GetComponent<Rigidbody2D>();
        myGameStatus = GetComponent<GameStatus>();
        gs = FindObjectOfType<GameStatus>();
    }

    // Update is called once per frame
    void Update()
    {
        if (!hasStarted)
        {
            if (gs.autoPlayOn==true)
            {

                LockBallToPaddle();
                Launch();
            }
        else
        {
                LockBallToPaddle();
                LaunchOnMouseClick();
        }
   
        }
    }

    private void LaunchOnMouseClick()
    {
        
      if(Input.GetMouseButtonDown(0))
        {
           hasStarted = true;
           myRigidbody2D.velocity = new Vector2(velX, velY);
          
        }
    }

    private void Launch()
    {
            hasStarted = true;
            myRigidbody2D.velocity = new Vector2(velX, velY);
    }

    private void LockBallToPaddle()
    {
        Vector2 paddlePos = new Vector2(paddle1.transform.position.x, paddle1.transform.position.y); 
        transform.position = paddlePos + paddleToBallDist;
    }

    private void OnCollisionEnter2D(Collision2D collision)
    {

        Vector2 velocityTweak = new Vector2(UnityEngine.Random.Range(0, randomBounce), UnityEngine.Random.Range(0, randomBounce));
        
        if(hasStarted)
        {
            AudioClip clip = sounds[UnityEngine.Random.Range(0,sounds.Length)] ;
            myAudioSource .PlayOneShot(clip);
            myRigidbody2D.velocity += velocityTweak;
        }
    }
}

I really liked the idea of duplicating the ball so I wanted to fiddle a bit more to solve it before abandoning it and ask you people for help in the meantime…=)
Thanx in advance.Cheers.

edit 1: The error is occurring on the LockBallToPaddle() line which is in the Update()…But I don’t understand why since that method runs inside if(!hasStarted) and the instantiate that I’m trying is when hasStarted==true.

Hi,

NullReferenceException means that a reference (“link”) to an instance is missing. Double click on the error message to see to which line in your code it is referring. If you exposed a field in the Inspector, make sure that it’s not empty.

If you instantiate a game object during runtime and it is supposed to access an object in the scene, you’ll have to look for a reference to the required object. Depending on the case, this could be achieved with the FindObjectOfType method.

Hi again,

The error sends me to the first line in the Update() method, if(gs.autoPlayOn == true)…And I created
gs = FindObjectOfType<GameStatus>(); as the reference.

But I kinda thought of a workaround. I created another ball prefab with a different script. I instantiate that at the original balls transform.position…=)

What different script do you mean?

And did you log the value of gs into your console after the FindObjectOfType method call? Maybe no GameStatus object was found.

I thought the problem was with LockBallToPaddle in ball.cs or the autoPlay boolean referenced from gs.cs in ball.cs . So I duplicated and renamed and recolored the ball prefab and attached a script without the starting conditions(autoPlay and LockToPaddle). It worked also in favor of gameplay because I needed the instantiated balls not to end the level when they trigger the LoseCollider.

I didn’t think of logging it. But I paused and checked several times when I was trying to make it work and it was there under Don’tDestroyOnLoad.

But since I figured a workaround(I don’t know if it is an elegant one) I moved on to the other parts of the game.So thanx a lot for the answers=)

You found a solution/workaround? That’s great. :slight_smile:


See also:

I think so. Now, some blocks yield a rigidbody(which falls on the y axis) when they got destroyed and if the player can catch it, they would earn some points and a modification to the game(paddle enlarges or shrinks or the paddle sprite changes). One of the modifications was this duplicating ball and with the new ball prefab to instantiate from, it kinda works fine. At least for now=)
I’ll mark it as solved.
Thanx again=)

This topic was automatically closed 24 hours after the last reply. New replies are no longer allowed.

Privacy & Terms