How to make Ball respawn if it is destroyed?

Hi,

I made the Ball prefab object get destroyed if it goes off screen, but I am unable to figure out the respawn after that.

in the ball handler script update I add the following:

        if (GameObject.FindGameObjectWithTag("Player") == null)
        {
            Invoke(nameof(SpawnNewBall), respawnDelay);
        }

And I selected the “Player” tag for the ball prefab, but it’s not working.

Can anyone help?

Thanks,
Majestic

Hi, if the object is destroyed does your findgameobject return anything? I have a sneaking suspicion it finds nothing and so cannot compare it to “null”.

Could you turn it into a boolean search?

edit - quick search of the documentation brought up a boolean flag on the tryfind function to search inactive objects. Rather than destroy your ball can you make it inactive instead?
Sorry, I am on my phone and can’t help more right now.

Hi Grey,

Thanks a lot for your help, it is my own fault that I did not past the whole script.

It turns out that the FindGameObjectWithTag(“Player”) does work if the game object is destroyed, but I had that bit of the code at the end of the update section, which means it was not ever running because at the top of the update there is the line “if (currentBallRigidbody == null) { return; }”.

using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.InputSystem;

public class BallHandler : MonoBehaviour
{

    [SerializeField] GameObject ballprefab;
    [SerializeField] Rigidbody2D pivot;
    [SerializeField] float detatchDelay;
    [SerializeField] float respawnDelay;

    Rigidbody2D currentBallRigidbody;
    SpringJoint2D currentBallSpringJoint;

    public GameObject player;

    private Camera mainCamera;
    private bool isDragging;


    void Start()
    {
        SpawnNewBall();
        mainCamera = Camera.main;
    }

    void Update()
    {
        if (currentBallRigidbody == null) { return; }

        if(!Touchscreen.current.primaryTouch.press.isPressed)
        {
            if (isDragging)
            {
                LaunchBall();
            }

            isDragging = false;

            return;
        }

        isDragging = true;

        currentBallRigidbody.isKinematic = true;

        Vector2 touchPosition = Touchscreen.current.primaryTouch.position.ReadValue();

        Vector3 worldPosition = mainCamera.ScreenToWorldPoint(touchPosition);

        currentBallRigidbody.position = worldPosition;

        currentBallRigidbody.isKinematic = true;

        if (GameObject.FindGameObjectWithTag("Player") == null)
        {
            Invoke(nameof(SpawnNewBall), respawnDelay);
            Debug.Log("spawning new ball");
        }
    }

    private void LaunchBall()
    {
        currentBallRigidbody.isKinematic = false;
        currentBallRigidbody = null;

        Invoke(nameof(DetatchBall), detatchDelay);
    }

    private void DetatchBall()
    {
        currentBallSpringJoint.enabled = false;
        currentBallSpringJoint = null;
    }

    private void SpawnNewBall()
    {
            GameObject ballInstance = Instantiate(ballprefab, pivot.position, Quaternion.identity);

            currentBallRigidbody = ballInstance.GetComponent<Rigidbody2D>();
            currentBallSpringJoint = ballInstance.GetComponent<SpringJoint2D>();

            currentBallSpringJoint.connectedBody = pivot;
    }

}

But I still couldn’t quite figure it out… I tried to put the

        if (GameObject.FindGameObjectWithTag("Player") == null)
        {
            Invoke(nameof(SpawnNewBall), respawnDelay);
            Debug.Log("spawning new ball");
        }

at the top in the update, above the

if (currentBallRigidbody == null) { return; }

but then it keeps spawning unlimited balls after the first one is destroyed. Still not sure how to make it work to spawn just one new ball.

Hey Majestic,

Take everything I say with a grain of salt as I still feel pretty new to all of this.

My first thought is that the continuous spawning of new balls indicates that there is an issue with either the tag string or the prefab with the tag?

I have a question though, you are checking if the currentBallRigidBody is null right at the start… why are you also checking for the “Player” tag object separately? Could you not use the currentBallRigidBody test to spawn in a new ball?

if (currentBallRigidbody == null)
        {
            Invoke(nameof(SpawnNewBall), respawnDelay);
            Debug.Log("spawning new ball");
        }

Sorry, I have not done this course yet.

If you can’t find the issue, you could possibly circumvent it all together. Rather than doing all the test searching at run time, what about if you created a new Boolean to indicate if the ball is alive?

Set a new private bool _isAlive.
In your spawn function, set it to true.
In your destroy function, set it to false.
You can now just check if the bool is true or false at the start of your update function?

Hey,
Well I’ve tried everything you suggested. It either doesn’t spawn a new ball, or it does but continues spawning balls nonstop.

I’m just trying to make it so:

  1. The current ball prefab gets destroyed if it goes off screen,
  2. Then spawn a new single ball after the previous one is destroyed.

The first part is easily achieved with the below script attached to the ball prefab.

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class Ball : MonoBehaviour
{
    private void OnTriggerExit2D(Collider2D collision)
    {
        if (collision.gameObject.CompareTag("Camera confiner"))
        {
            Destroy(gameObject);
            Debug.Log("Ball went off screen");
        }
    }
}

But the 2nd part I cant figure out how to get working in accordance with the BallHandler script provided in the course.

If you or anyone else has any more suggestions it would be much appreciated.

Thanks a lot anyways.

Ok so I took a very quick glance at the course, the instructor has the respawn function nested inside the DetachBall function rather than checking if “Player” exists.

So my thought process based on what you have included so far is that it is a tag issue. Assuming you have set the tag to “Player” in the ball prefab.

Have you tried putting a breakpoint on this line and stepping through what it is doing?
if (GameObject.FindGameObjectWithTag(“Player”) == null)

I can’t try this code at work, but please let me know how it goes. I am sorry I can’t be more helpful with this.
I “think” the respawn delay is causing your (if statement) to fire every frame before the ball respawns… so if your respawn is one second and your update runs thirty times a second, your invoke is fired every frame until the delay is finished. This is just a beginner guess of course so take it with a grain of salt.

    Rigidbody2D currentBallRigidbody;
    SpringJoint2D currentBallSpringJoint;

    public GameObject player;

    private Camera mainCamera;
    private bool isDragging;
   
    private bool _isAlive = new bool();

    void Start()
    {
        _isAlive = false;
        SpawnNewBall();
        mainCamera = Camera.main;
    }

    void Update()
    {

        if(!Touchscreen.current.primaryTouch.press.isPressed)
        {
            if (isDragging)
            {
                LaunchBall();
            }

            isDragging = false;

            return;
        }

        isDragging = true;

        currentBallRigidbody.isKinematic = true;

        Vector2 touchPosition = Touchscreen.current.primaryTouch.position.ReadValue();

        Vector3 worldPosition = mainCamera.ScreenToWorldPoint(touchPosition);

        currentBallRigidbody.position = worldPosition;

        currentBallRigidbody.isKinematic = true;

// Set Ball to dead if it was alive.
        if (currentBallRigidbody == null && _isAlive)
        {
           _isAlive = false;
        }
// If Ball is dead, spawn new ball.
        if(!_isAlive)
        {
        Invoke(nameof(SpawnNewBall), respawnDelay);
        Debug.Log("spawning new ball");
        }
    }

    private void LaunchBall()
    {
        currentBallRigidbody.isKinematic = false;
        currentBallRigidbody = null;

        Invoke(nameof(DetatchBall), detatchDelay);
    }

    private void DetatchBall()
    {
        currentBallSpringJoint.enabled = false;
        currentBallSpringJoint = null;
    }

    private void SpawnNewBall()
    {
//Set Ball to Alive.
            _isAlive = true;
            GameObject ballInstance = Instantiate(ballprefab, pivot.position, Quaternion.identity);

            currentBallRigidbody = ballInstance.GetComponent<Rigidbody2D>();
            currentBallSpringJoint = ballInstance.GetComponent<SpringJoint2D>();

            currentBallSpringJoint.connectedBody = pivot;
    }

}
1 Like

Here’s my approach:
Add this method to BallHandler.cs:

public void InitiateRespawn()
{
    Invoke(nameof(SpawnNewBall), RespawnDelay);
}

Then in a Ball script, add this OnDestroy() method

void OnDestroy()
{
    FindObjectOfType<BallHandler>().InitiateRespawn();
}

Remove all the code to do this in Update()
This event driven model means that the timer to respawn the ball will happen exactly once.

2 Likes

Look at that, someone who knows what they are doing! :laughing:

Yeah that works. Thank you so much Brian :smiley:

Grey thank you so much for trying to help, I really appreciate it. your Boolean idea script worked to make it respawn a single ball after the last one is destroyed, but when trying to drag to launch the new ball the console throws an error (NullReferenceException: Object reference not set to an instance of an object)

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

Privacy & Terms