Problem Destroying Gameobject

I recently figured out how to get my slow motion powerup working, but now I am having trouble destroying the object if it is not triggered. I can destroy by setting the destroy time a few seconds higher than the coroutine time, but that leaves the powerup on screen for too long. I tried creating a bool and setting it equal to false. This is not working. The object won’t destroy and my coroutine won’t finish even if a trigger occurs (without the bool or update method the coroutine finishes fine). I may have it set up wrong. I want the powerup to stay on screen for 3-5 seconds so the player must be quick to pick it up. I would really appreciate some help.

Heres my code:

using UnityEngine;
using System.Collections;

public class TimeChangingPowerUp : MonoBehaviour {

	public AudioClip powerUpSound;
	public float time;
	public float speed;
	private bool Active=false;
	//private bool playerEntered;

	/*public void ActivatePowerUpSlowMotion(float time, float speed)
	{
		StartCoroutine (SlowMoRoutine (time, speed));
	}*/

	void Update(){

		if (Active) {
			
			Destroy (gameObject, 3);

		}
		
	}

	IEnumerator SlowMoRoutine(float time, float speed)
	{
		Active = true;
		//time = Time.unscaledTime;
		//Destroy(gameObject );
		float duration = time * speed;
		Time.timeScale = speed;
		yield return new WaitForSeconds (duration);
		Time.timeScale = 1f;
		Destroy (gameObject);

	}

	void OnTriggerEnter2D(Collider2D other){

		if (other.tag == "Player") {
			
			Active = true;
			GameObject sound = new GameObject ();
			sound.transform.position = transform.position;
			AudioSource audioSource = sound.AddComponent<AudioSource> ();
			audioSource.clip = powerUpSound;
			audioSource.Play ();
			Destroy (sound, powerUpSound.length);
			gameObject.GetComponent <SpriteRenderer > ().enabled = false;
			StartCoroutine (SlowMoRoutine (time, speed));

		} 
		 
	}

}

Hi!

you hav the error in this sentence

void update()
{
    Destroy(gameObject, 3);
}

you must remember that this function work one time for frame, so it’s in a blucle, and always set the same seconds for destroy = 3, destroy = 3, destroy = 3, destroy = 3, destroy = 3, xD you must call this function only one time.

understand me?

sorry for my english! I am from colombia.

I figured it out! I added another coroutine that disables the spriterender after 3 seconds and then the object destroys after 17. Powerup still works like normal if triggered.

Here’s the end result:

using UnityEngine;
using System.Collections;

public class TimeChangingPowerUp : MonoBehaviour {

	public GameObject particle;
	public AudioClip powerUpSound;
	public float time;
	public float speed;
	bool Active=false;
	//private bool playerEntered;

	/*public void ActivatePowerUpSlowMotion(float time, float speed)
	{
		StartCoroutine (SlowMoRoutine (time, speed));
	}

	void Start(){
		Active =false;
	}*/

	void Update(){

		if (Active==false) {
			StartCoroutine (DestroyGameObject());
		}
		
	}

/*void OnDisable(){
	StartCoroutine (SlowMoRoutine (time, speed));

	}*/

	IEnumerator SlowMoRoutine(float time, float speed)
	{
		Active = true;
		//time = Time.unscaledTime;
		//Destroy(gameObject );
	//float duration = time / speed;
		float duration = time * speed;
		Time.timeScale = speed;
		yield return new WaitForSeconds (duration);
		Time.timeScale = 1f;
		Destroy (gameObject);

	}

	IEnumerator DestroyGameObject(){

		yield return new WaitForSeconds (3);
		gameObject.GetComponent <SpriteRenderer > ().enabled = false;
		yield return new WaitForSeconds (17);
		Destroy (gameObject);

	}
		

	void OnTriggerEnter2D(Collider2D other){

		if (other.tag == "Player") {
			
			Active = true;
			GameObject clonedParticle;
			clonedParticle =Instantiate (particle, transform.position, transform.rotation) as GameObject ;
			GameObject sound = new GameObject ();
			sound.transform.position = transform.position;
			AudioSource audioSource = sound.AddComponent<AudioSource> ();
			audioSource.clip = powerUpSound;
			audioSource.Play ();
			Destroy (clonedParticle, 1.2f);
			Destroy (sound, powerUpSound.length);
			gameObject.GetComponent <SpriteRenderer > ().enabled = false;
		StartCoroutine (SlowMoRoutine (time, speed));
			//Destroy (gameObject);


		} 
		 
	}

}

You have a conceptional problem here. When calling Destroy with a delay that call is final. Once invoked you can’t abort that call. It should only be used when you want to specify a max life time for the object.

So in your case you shouldn’t call Destroy unconditionally. There are usually two ways how to approach this problem:

  • Seperate the powerup pickup logic from the actual powerup effect. So two seperate scripts / objects. The powerup pickup script can be the same for all powerups. This object has the trigger collider and all it does is creating the referenced “powerup prefab” and parent it to the player. The pickup script can have it’s own max lifetime seperate from the actual powerup.
  • The second way is to keep everything in one script / component as you have it now, but include the pickup timeout in the coroutine.

For the first solution you would do something like this:

public class PickUp : MonoBehaviour
{
    public GameObject powerUp;
    public float lifeTime = 3f;
    void Start()
    {
        Destroy(gameObject, lifeTime);
    }
    void OnTriggerEnter2D(Collider2D other)
    {
        if (other.tag == "Player") {
            GameObject obj = (GameObject)Instantiate(powerUp);
            obj.transform.parent = other.transform;
        }
    }
}

Your “TimeChangingPowerUp” would be attached to the prefab that you reference in your PickUp script. Inside your “TimeChangingPowerUp” you would simply start the coroutine inside Start when it’s attached to the player. Of course the Update method isn’t necessary anymore.

The second solution would be something like that:

public float lifeTime = 3f;

void Start()
{
    StartCoroutine(SlowMoRoutine( time, speed));
}

IEnumerator SlowMoRoutine(float time, float speed)
{
    float timeOut = Time.time + lifeTime;
    while(!Active && Time.time < timeOut)
        yield return null;
    if (!Active){
        Destroy(gameObject);
        yield break;
    }
     float duration = time * speed;
     Time.timeScale = speed;
     yield return new WaitForSeconds (duration);
     Time.timeScale = 1f;
     Destroy (gameObject);
 }
 void OnTriggerEnter2D(Collider2D other)
{
     if (other.tag == "Player")
     {
         Active = true;
         GameObject sound = new GameObject ();
         sound.transform.position = transform.position;
         AudioSource audioSource = sound.AddComponent<AudioSource> ();
         audioSource.clip = powerUpSound;
         audioSource.Play ();
         Destroy (sound, powerUpSound.length);
         gameObject.GetComponent <SpriteRenderer > ().enabled = false;
     } 
 }

Instead of creating that temp GO for the audiosource you might want to use AudioSource.PlayOneShot. That method will create an internal temp GameObject which is destroyed automatically once the clip is finished.