Wzrd
Wzrd

Reputation: 258

SetActive(true) not working inside a coroutine

I am using coroutines to manage cooldowns for powerups in my game. When the player collides with a powerup they will get the effect and a cooldown will start so they can't use it again for a certain amount of time.

I am using coroutines to manage these cooldowns - setting the gameObject to false, waiting for the cooldown, then setting the gameObject back to true.

My code:

IEnumerator RespawnTime ()
{
    gameObject.SetActive(false);
    Debug.Log("disabled");

    yield return new WaitForSeconds(3f);

    gameObject.SetActive(true);
    Debug.Log("enabled");
}

private void OnCollisionEnter2D (Collision2D collision)
{
    // Health stuff goes in here.
    Debug.Log("Health regen!");

    // Start cooldown
    StartCoroutine(RespawnTime());
}

However, the gameObject is never activated again once disabled. I don't think the problem is with the yield as commenting out the SetActive statements results in both debug messages being displayed at the correct times.

Edit:

Sorry, I didn't clarify. I know coroutines don't run on disabled GameObjects. I made a PowerUpManager script and it didn't work. Code:

In the PowerUp script, in the OnCollisionEnter2D method

StartCoroutines(PowerUpManager.RespawnTime(gameObject))

In the PowerUpManager script

public static IEnumerator RespawnTime(GameObject powerUp)
{
    powerUp.SetActive(false);

    yield return new WaitForSeconds(3f);

    powerUp.SetActive(true);
}

Upvotes: 1

Views: 2485

Answers (3)

kefren
kefren

Reputation: 1112

If you deactivate the game object, all its components get disabled as well, so the Coroutine stops being invoked.

What you can do, is to separate the Coroutine for the cool down and the script that checks the collision, in 2 separate scripts.

Let’s call the Cooldown.cs and Powerup.cs. Then, have them cross referenced in each other. In powerup.cs you have

private void OnCollisionEnter2D (Collision2D collision)
{
    // Health stuff goes in here.
    Debug.Log("Health regen!");
    // Start cooldown
    Cooldown cd = gameObject.GetComponent<cooldown>();
    cd.CooldownCoroutine();
}

In Cooldown.cs:

IEnumerator RespawnTime ()
{
     Powerup pu = gameObject.GetComponent<Powerup>()
     pu.SetActive(false);
     Debug.Log("disabled");
     yield return new WaitForSeconds(3f);
     pu.SetActive(true);
     Debug.Log("enabled");
}

public void CooldownCoroutine()
{
    // Health stuff goes in here.
    Debug.Log("Health regen!");
    // Start cooldown
    StartCoroutine(RespawnTime());
}

This way you don’t disable the whole object, but only the script that checks for collisions.

Upvotes: 1

derHugo
derHugo

Reputation: 90679

The moment you call

gameObject.SetActive(false);

this object does not receive the Update message anymore and thus also doesn't execute the Coroutine further.

Coroutines are also stopped when the MonoBehaviour is destroyed or when the GameObject it is attached to is disabled.


The reason your second attempt doesn't work is: You still are calling StartCoroutine on the same GameObject so the Coroutine of the PowerUpManager class is now executed on your GameObject => same effect.

If you really want to go with the PowerUpManager you would need a Singleton instance and call StartCoroutine on that instance like e.g.

In PowerUpManager have

public static PowerUpManager Instance;

private void Awake()
{
    Instance = this;
}

then you can call from your script

PowerUpManager.Instance.StartCoroutine(PowerUpManager.RespawnTime(gameObject));

In your simple case where there is no smooth moving etc required but only a simple delay I would suggest rather using Invoke with your desired delay. This will also be executed on a disabled or inactive GameObject.

private void EnableAfterCooldown()
{
    gameObject.SetActive(true);
    Debug.Log("enabled");
}

private void OnCollisionEnter2D (Collision2D collision)
{
    // Health stuff goes in here.
    Debug.Log("Health regen!");

    gameObject.SetActive(false);
    Invoke("EnableAfterCooldown", 3f);
}

Upvotes: 2

Iggy
Iggy

Reputation: 4888

Coroutines do not run on disabled game objects!

You should run this on another game object.

Upvotes: 0

Related Questions