Coroutine starts but doesn't run?

I am trying to implement a powerup that ends after a certain time and if another powerup is grabbed then time adds to the timer before de-activation.

I put my coroutine for the powerup time onto a PowerupManager script and made it public and static, and start it when the pickup is grabbed, like so:

        if (PowerupManager.powerupActive == false)
        {
            PowerupManager.powerupActive = true;
            PowerupManager.timer = powerupTime;
            StartCoroutine(PowerupManager.PowerupTimer());
        }
        else
        {
            PowerupManager.timer += powerupTime;
        }

Pretty straightforward. Then, the PowerupManager’s coroutine should activate and do its thing:

public class PowerupManager : MonoBehaviour {

public static bool powerupActive = false;
public static float timer = 0f;

//Waits amount of time and then disables the powerup effects
public static IEnumerator PowerupTimer()
{
    //At start of coroutine, set seconds elapsed to zero
    float seconds = 0f;

    //While seconds elapsed is less than the time to run, keep running this timer
    //Check the condition every second in case the timeToKeepActive changes
    while (seconds < timer)
    {
        Debug.Log((timer - seconds) + "seconds left to run");
        yield return new WaitForSeconds(1f);
        seconds++;
    }

   //[Code to deactivate powerup goes here]

    powerupActive = false;
}

}


My pickup’s powerupTime is set to 8f, so I should get the Debug.Log((timer - seconds) + "seconds left to run"); to print the amount of time left to run, counting down from 8 to zero. However it only prints “8 seconds left to run”, and doesn’t countdown any further. Not sure what’s going on here so any help is appreciated!

This line:

StartCoroutine(PowerupManager.PowerupTimer());

is equivalent to

this.StartCoroutine(PowerupManager.PowerupTimer());

That means you run the coroutine on the MonoBehaviour instance you’re currently on. Since i guess this is a powerup object which you probably destroy or deactivate the coroutine will be terminated along with the object. Since you already have a PowerupManager you probably should start the coroutine on that instance.

Instead of using so many static variables, it’s better to make your PowerupManager a singleton like this:

public class PowerupManager : MonoBehaviour
{
    public static PowerupManager instance;
    public bool powerupActive = false;
    public float timer = 0f;
    void Awake()
    {
        instance = this;
    }
    private IEnumerator PowerupTimer(float aTime)
    {
        powerupActive = true;
        float seconds = 0f;
        timer = aTime;
        while (seconds < timer)
        {
            yield return new WaitForSeconds(1f);
            seconds++;
        }
        powerupActive = false;
    }
    
    public void PowerupPickup(float aTime)
    {
        if (powerupActive == false)
        {
            StartCoroutine(PowerupTimer(aTime));
        }
        else
        {
            timer += aTime;
        }
    }
}

Inside your Powerup object you just would do:

PowerupManager.instance.PowerupPickup(powerupTime);

Of course your PowerupManager need to be attached to an object in the scene.

You guys nailed it, my powerup deletes itself after starting the timer, I wasn’t aware that the coroutine runs on whichever script calls it. Thanks!