Reputation: 258
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
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
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
Reputation: 4888
Coroutines do not run on disabled game objects!
You should run this on another game object.
Upvotes: 0