Anya
Anya

Reputation: 395

Start coroutine on an inactive/de-activated GameObject

I have the following code:

void Start()
{
    gameObject.SetActive(false);
    StartCoroutine(Load());
}

IEnumerator Load()
{
    yield return new WaitForSeconds(waitTime);
    gameObject.SetActive(true);
}

This gives me an error that says:

Coroutine couldn't be started because the the game object 'NameOfObj' is inactive!

This makes sense, since the game object has been set to deactivate before running the script. Even still, what is it that I'm supposed to do then? I tried moving gameObject.SetActive(false) to the coroutine, before WaitForSeconds(). Doing this stopped the game object from loading at all.

From my understanding, when the line gameObject.SetActive(false) is executed, the script stops running until the game object is reactivated. However, if this is the case, would it not be impossible to then reactivate the game object (as the script is disabled)?

Regardless, how would I delay my game object from loading until 2-3 (or any arbitrary length of time) after the game has started?

Upvotes: 1

Views: 14038

Answers (2)

Programmer
Programmer

Reputation: 125305

You cannot start a coroutine function from a script that has its GameObject de-activated.

The StartCoroutine function is a function under the MonoBehaviour class. When you have to start a coroutine on a deactivated GameObject, you need a reference to a MonoBehaviour object that has an active GameObject.

Two ways to do this:

1. Use an already existing GameObject that's unlikely to be deactivated. In my case, I usually use the camera. I access the camera's MonoBehaviour since it's likely to be activated then use it to start the coroutine function.

I suggest you use this method.

Replace the code in your Start function with the one below:

//De-activate this GameObject
gameObject.SetActive(false);
//Get camera's MonoBehaviour
MonoBehaviour camMono = Camera.main.GetComponent<MonoBehaviour>();
//Use it to start your coroutine function
camMono.StartCoroutine(Load());

2. Attach the script to an empty GameObject and the script on the empty GameObject will control or be able to activate/de-activate the other GameObject.

The script with the coroutine function you expect to run on a de-activated GameObject (Attach it to the GameObject you wish to de-activate):

public class YourDeactivatableScript: MonoBehaviour
{
    public IEnumerator Load()
    {
        yield return new WaitForSeconds(waitTime);
        gameObject.SetActive(true);
    }
}

Now, let's say that you want to deactivate a GameObject named "Cube" that has the YourDeactivatableScript script attached to it but still be able to start its Load coroutine function, create an empty GameObject with a new script, then start the Load function from it.

Create an empty GameObject then attach this script to it:

public class LoadFuncCallerScript: MonoBehaviour
{
    GameObject targetObject;

    public void Start()
    {
         //Find the GameObject you want to de-activate
        targetObject = GameObject.Find("Cube");
        //De-activate it
        targetObject.SetActive(false);
        //Get it's component/script
        YourDeactivatableScript script = targetObject.GetComponent<YourDeactivatableScript>();
        //Start coroutine on the other script with this MonoBehaviour
        StartCoroutine(script.Load());
    }
}

The coroutine is now started from another script named LoadFuncCallerScript.

Upvotes: 6

Tricko
Tricko

Reputation: 551

What I do to avoid stopping coroutine is have a game object that does not get deactivated.

public class CoroutineHandler : MonoBehaviour
{
    private static CoroutineHandler instance = null;
    public static CoroutineHandler Instance
    {
        get
        {
            if(instance == null)
            {
                GameObject inst = new GameObject("CoroutineHandler");
                DontDestroyOnLoad(inst);
                instance = inst.AddComponent<CoroutineHandler>();
            }
            return instance;
        }
    }
}

Then use this for such coroutines by using CoroutineHandler.Instance.StartCoroutine(RoutineMethodHere());.

If you do not want something like this, since it can go wrong or cause leaks if not handled correctly, you can try using Invoke("MethodName", delay);

Upvotes: 5

Related Questions