Jump to content
  • Advertisement
ethancodes

error for trying to reference an object that has been destroyed

Recommended Posts

I'm getting an error that I can't seem to track down. I understand what the error is telling me, just not why I'm getting it. Here is the exact error:

MissingReferenceException: The object of type 'AssaultRiflePickUp' 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.
UnityEngine.MonoBehaviour.StartCoroutine (IEnumerator routine) (at C:/buildslave/unity/build/artifacts/generated/common/runtime/MonoBehaviourBindings.gen.cs:62)
AssaultRiflePickUp.StartCoroutine (Single delay) (at Assets/Scripts/AssaultRiflePickUp.cs:77)
AssaultRiflePickUp.AssaultRifleShot (UnityEngine.GameObject mainBall) (at Assets/Scripts/AssaultRiflePickUp.cs:89)
Ball.OnCollisionEnter2D (UnityEngine.Collision2D collider) (at Assets/Scripts/Ball.cs:63)
 

To give some more info, I have an arkanoid style clone, and I have a special pick up called AssaultRiflePickUp. I collect the pick up, and then the next time the ball hit's my paddle, it should iterate through the code to do what the pick up does. The problem is that I get this error instead once the ball hit's the paddle. The AssaultRifleShot method is called from an event on the Ball class. The event is called when the Ball hits the paddle and the pick up is active. I have not used events before but I believe I have it set up correctly. When the pick up first gets hit by the ball, the gameObject is destroyed, but a clone of the AssaultRiflePickUp class is instanced on a list to be accessed. For whatever reason, I'm not getting this instance but seem to be getting the instance that has been destroyed. I'm not sure why though. Maybe someone can spot where I'm messing up.

Ball class event:

	public delegate void BallHitPaddle(GameObject ball);
	public event BallHitPaddle onBallHitPaddle;

and then in the OnCollisionEnter2D method....


if (collider.gameObject.name == "Paddle" && this.mainBall) 
			{
				if (onBallHitPaddle != null) 
				{
					onBallHitPaddle(this.gameObject);
				}
			}

Here is what happens when the pick up collides with the ball or paddle:

public void OnCollisionEnter2D (Collision2D collision)
	{	//tweak is used to make sure the pick up does not get stuck moving in a vertical loop
		Vector2 tweak = new Vector2 (UnityEngine.Random.Range(0f, 0.2f),UnityEngine.Random.Range(0f, 0.2f));

			this.gameObject.GetComponent<Rigidbody2D>().velocity += tweak;

		//if the pickup makes contact with the paddle or ball....
		if (collision.gameObject.tag == "Paddle" || collision.gameObject.tag == "Ball") 
		{
			GameManager.pickUpManager.AddToQueue(this); //Add the pick up to the queue of pick ups.
			Destroy(gameObject); //...and finally destroy pick up object
		}
	}

And here is the AssaultRiflePickUp:

public class AssaultRiflePickUp : BasePickUp 
{
	private int AmmoRemaining = 35;
	private float timer = 0.3f;

	public GameObject newBall;

	void Start ()
	{	
		//assign paddle to variable
		paddle = GameObject.FindObjectOfType<Paddle> ();

		//find the ball and assign it to this variable
		foreach (Ball ball in GameManager.pickUpManager.allBalls) 
		{
			if (ball.mainBall) 
			{
				mainBall = ball;
			}
		}
		//subscribe to events
		mainBall.onBallHitPaddle += AssaultRifleShot;
		mainBall.onBallCollided += DestroyExtraBalls;
		Debug.Log("Start");
	}

	public override void ActivatePickUp (MonoBehaviour coroutineHost)
	{
		
	}

	public void SpecialShot ()
	{
		Debug.Log("SpecialShot");
		Vector3 mainBallVector = mainBall.GetComponent<Rigidbody2D>().velocity; //grab mainBall's direction and speed before making it a destroyable ball
		mainBall.GetComponent<Ball>().mainBall = false;

			Instantiate(newBall);
			GameManager.pickUpManager.allBalls.Add(newBall.GetComponent<Ball>());
			newBall.transform.position = paddle.transform.position + ballSpawnOffset; //use the original ball's vector3 to follow it directly
			newBall.GetComponent<Rigidbody2D>().velocity = mainBallVector;
	}

	public void DestroyExtraBalls (GameObject ball)
	{
		if (GameManager.pickUpManager.allBalls.Count > 1) 
		{
		}

		//TODO: Implement observer pattern to watch for balls colliding with something. If they do and they are not the main ball, destroy them.
	}

	public void DestroyPickUp ()
	{
			Debug.Log("DestroyPickUp");
			Destroy (GameManager.pickUpManager.pickUpQueue [0]);
			GameManager.pickUpManager.pickUpQueue.RemoveAt (0);
			GameManager.pickUpManager.pickUpActive = false;
	}

	//a timer used for timed pick ups
	public IEnumerator PowerUpTimer (float delay)
	{	
		Debug.Log("PowerUpTimer");
		yield return new WaitForSeconds (delay);
		SpecialShot();
	}

	//The coroutine is used to start a short timer so that the balls don't spwn on top of each other all at the same time.
	public void StartCoroutine (float delay)
	{
		StartCoroutine(PowerUpTimer(delay));
	}


	//Controls the pick up's cycle
	public void AssaultRifleShot (GameObject mainBall)
	{
		if (AmmoRemaining > 0) 
		{ 
			//if there is ammo remaining, set the main ball to just a normal ball so it can be destroyed on impact and then start coroutine
			mainBall.GetComponent<Ball> ().mainBall = false;
			mainBall.GetComponent<Ball> ().onBallHitPaddle -= AssaultRifleShot; //unsubscribe to event when mainBall is set to false.
			StartCoroutine(timer);
		
			AmmoRemaining -= 1;
		} 
		else 
		{
			DestroyPickUp();	
		}
	}
}

 

Share this post


Link to post
Share on other sites
Advertisement

I've narrowed the code down to this section here on the Ball class:

if (collider.gameObject.name == "Paddle" && this.mainBall) 
			{
				if (onBallHitPaddle != null) 
				{
					onBallHitPaddle(this.gameObject);
				}
			}

This is in the OnCollisionEnter2D method and is the notifier for the event. The AssaultRifleShot method in the AssaultRiflePickUp class is the listener. I'm confused why I'm getting a null reference though since I'm doing a null check. I believe what is happening is the pickup game object is being destroyed without unsubscribing. This game object's script gets cloned into a pick up queue list before it is destroyed, and then it is destroyed. i tried using OnDestroy() to unsubscribe it, but if I do that, then the event never gets hit, so I'm guessing the clone isn't subscribed? The OnDestroy() does however get rid of the error. Does the Start() function get called when you clone a script? i think that is really what this boils down to is the clone script is not subscribed to the event. 

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

  • Advertisement
×

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

We are the game development community.

Whether you are an indie, hobbyist, AAA developer, or just trying to learn, GameDev.net is the place for you to learn, share, and connect with the games industry. Learn more About Us or sign up!

Sign me up!