Search Unity

  1. Unity 6 Preview is now available. To find out what's new, have a look at our Unity 6 Preview blog post.
    Dismiss Notice
  2. Unity is excited to announce that we will be collaborating with TheXPlace for a summer game jam from June 13 - June 19. Learn more.
    Dismiss Notice

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

Discussion in 'Scripting' started by farukaygun, Apr 27, 2022.

  1. farukaygun

    farukaygun

    Joined:
    Nov 23, 2020
    Posts:
    19
    Hi everyone,

    I have a problem about instantiated object's instance reference.
    I have 2 script which names are EnemyController.cs and EnemyAttack.cs.

    I am instantiating objects every x seconds and calling method from EnemyAttack.cs with instance reference in EnemyController.cs



    Red object instantiated with my code. Error says the yellow object is not active even though I instantiated the red object.

    Error is:
    Coroutine couldn't be started because the the game object 'Zombie_0(Clone)' is inactive!
    UnityEngine.MonoBehaviour:StartCoroutine (System.Collections.IEnumerator)


    Sample code here:

    EnemyController.cs
    Code (CSharp):
    1. public class EnemyController : MonoBehaviour
    2. {
    3.     private EnemyAttack enemyAttack;
    4.  
    5.     private void Start()
    6.     {
    7.         enemyAttack = EnemyAttack.instance;
    8.     }
    9.  
    10.     private void StateExecute()
    11.     {
    12.         enemyAttack.Attack(); // not working
    13.         // GetComponent<EnemyAttack>().Attack(); --> this works.
    14.     }
    15. }
    EnemyAttack.cs
    Code (CSharp):
    1. public class EnemyAttack : MonoBehaviour
    2. {
    3.     public static EnemyAttack instance;
    4.  
    5.     private void Awake()
    6.     {
    7.         instance = this;  
    8.     }
    9.  
    10.     public void Attack()
    11.     {
    12.         // do some works
    13.         StartCoroutine(AttackRoutine());
    14.     }
    15. }
     
  2. Kurt-Dekker

    Kurt-Dekker

    Joined:
    Mar 16, 2013
    Posts:
    39,005
    Did you Instantiate<T>() the prefab properly? If not, then it is just lying as a disk asset and cannot run coroutines. It has to be either Instantiate-d into the scene, or already exist in scene to run coroutines.
     
  3. farukaygun

    farukaygun

    Joined:
    Nov 23, 2020
    Posts:
    19
    I'm instantiating to pool with this method.

    Code (CSharp):
    1.     public void CreateObjectToPool()
    2.     {
    3.         pooledObjects = new    List<GameObject>();
    4.         GameObject tmp;
    5.  
    6.         for (int i = 0; i < amountToPool; i++)
    7.         {
    8.             tmp = Instantiate(objectToPool);
    9.             tmp.SetActive(false);
    10.             pooledObjects.Add(tmp);
    11.         }
    12.     }
    // Getting from pool
    Code (CSharp):
    1. private IEnumerator SpawnRoutine()
    2.     {
    3.         while (isSpawning == true)
    4.         {
    5.             yield return new WaitForSeconds(spawnCountDown);
    6.  
    7.             GameObject enemy = EnemyPool.instance.GetPooledObject();
    8.             if (enemy != null)
    9.             {
    10.                 int selectedSpawner = Random.Range(0, 2);
    11.                 Transform spawner   = selectedSpawner == 0 ? leftSpawner : rightSpawner;
    12.  
    13.                 enemy.transform.position = spawner.position;
    14.                 enemy.transform.rotation = spawner.rotation;
    15.  
    16.                 enemy.SetActive(true);
    17.             }
    18.         }
    19.     }
     
  4. Kurt-Dekker

    Kurt-Dekker

    Joined:
    Mar 16, 2013
    Posts:
    39,005
    Are you doing the coroutine before doing .SetActive(true);?

    Find out with Debug.Log()

    You could also give active and inactive objects different names when you turn them on and off, then print their names.

    Code (csharp):
    1. tmp.SetActive(false);
    2. tmp.name = "I'm Asleep";
    and

    Code (csharp):
    1. tmp.SetActive(true);
    2. tmp.name = "I LIVE!";
    which can help you print out names.
     
  5. farukaygun

    farukaygun

    Joined:
    Nov 23, 2020
    Posts:
    19
    I change the names to "zombie 0", "zombie 1" ... "zombie 49" when object actived. Seems like script referring inactive object. Zombie 0 activated but it says "Coroutine couldn't be started because the the game object 'zombie 49' is inactive!".

    I activated to zombie 49 from inspector, code starts work without error.
     
  6. Kurt-Dekker

    Kurt-Dekker

    Joined:
    Mar 16, 2013
    Posts:
    39,005
    The screen shot you show is an inactive object.

    What exactly are you expecting to change until you activate it?

    Activate it in code with .SetActive(true) before you try starting any coroutines on it.

    I'm not sure how much clearer I can state it.
     
    Joe-Censored likes this.
  7. farukaygun

    farukaygun

    Joined:
    Nov 23, 2020
    Posts:
    19
    Here is a short video :


    My script should work for zombie 0 but it works for zombie 49.
     
  8. Peeling

    Peeling

    Joined:
    Nov 10, 2013
    Posts:
    443
    Heh, okay - a few things falling into a language gap here :)

    When you say it "should work for zombie 0 but it works for zombie 49" , what you mean is "It should be starting the coroutine on zombie 0 (the active one) but instead it is trying to start it on zombie 49 (the inactive one)"

    Here is why that is happening:

    You are attempting to pass the EnemyAttack component of the most recently spawned zombie to its own EnemyController component by storing it in a static variable on Awake and reading it in Start. This is a terrible, terrible idea for a number of reasons, this bug included. In particular, it is guaranteed to go wrong if you ever spawn two zombies at once.

    But anyway, back to this bug.

    When you instantiate an active GameObject, or a prefab that contains an active gameobject (so it's ticked as active when you examine the prefab), both Awake() and OnEnable() will be called before you get a chance to set it inactive.

    I tested that myself with a simple script that logs out Awake, OnEnable and Start. Here's the code that spawns it:

    Code (CSharp):
    1.   GameObject go = GameObject.Instantiate(thing);
    2.   go.SetActive(false);
    And here's the debug output from 'thing':

    upload_2022-4-28_0-28-7.png

    This means that all 50 zombies set the static instance variable to their personal EnemyAttack component in turn, and then it stays that way until you wake a zombie up.

    At that point, Zombie 0 grabs the static instance (now containing Zombie 49's EnemyAttack component) as its own and a few seconds later tries to use it.

    The fix is simple: never, ever use this way of passing components around other than for singletons. Either add a public 'attack' field to EnemyController and drag the EnemyAttack into it (best) or use GetComponent<EnemyAttack>() (slower, but still okay).

    Also: If your intention is to have things spawn in inactive, make sure that they are inactive in the prefab you're spawning from, otherwise Awake and OnEnable will be called prematurely.
     
    Last edited: Apr 28, 2022
    farukaygun likes this.
  9. farukaygun

    farukaygun

    Joined:
    Nov 23, 2020
    Posts:
    19
    Poor english problems. :(

    Oh I see thanks for explantion. it helped a lot. :)
     
  10. Deleted User

    Deleted User

    Guest

    hi.. i was wondering if you could help me with this and im using the prefab as the game object btw and its active nut its gives me the same error!
    and also the coroutine is from another script but im calling it from the first script
     

    Attached Files: