Home > Enterprise >  Couroutine couldn't be started because the game object is inactive
Couroutine couldn't be started because the game object is inactive

Time:10-21

I'm trying to do a respawn, but as mentioned in the title, that error appears. But how could I do to make the object to be disabled, and after a seconds enable it again, without this error?

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class playerCollision : MonoBehaviour
{
    public GameObject Destroy;

    [SerializeField] private Transform spawnPoint;
    [SerializeField] private Transform diePoint;

    private void OnCollisionEnter(Collision other)
    {
        if (other.gameObject.tag == "Enemy")
        {
            GameObject d = Instantiate(Destroy) as GameObject;
            d.transform.position = transform.position;
            d.SetActive(true);
            gameObject.SetActive(false);
            StartCoroutine(waitForRespawn());
            StartCoroutine(DestroyOptimization());
            IEnumerator DestroyOptimization()
            {
                yield return new WaitForSeconds(2);
                d.SetActive(false);
            }
        }
    }

    IEnumerator waitForRespawn()
    {
        yield return new WaitForSeconds(3);
        gameObject.SetActive(true);
        gameObject.transform.position = spawnPoint.transform.position;
    }
}

CodePudding user response:

You set gameObject.SetActive(false); .. so as the error tells you correctly: You can't start a Coroutine on an object that is not active.

Since your Coroutines do nothing fancy only delay something instead you could just use Invoke which works also on inactive objects:

public class playerCollision : MonoBehaviour
{
    public GameObject Destroy;
    
    [SerializeField] private Transform spawnPoint;
    [SerializeField] private Transform diePoint;

    private void OnCollisionEnter(Collision other)
    {
        if (other.gameObject.tag == "Enemy")
        {
            GameObject d = Instantiate(Destroy) as GameObject;
            d.transform.position = transform.position;
            d.SetActive(true);
            gameObject.SetActive(false);

            // will invoke the method called "Respawn" after 3 seconds
            Invoke(nameof(Respawn), 3f);;

            // will invoke the methd called "DestroyOptimization" aftre 2 seconds
            Invoke(nameof(DestroyOptimization), 2f);
        }
    }

    private void DestroyOptimization()
    {
        d.SetActive(false);
    }

    private void Respawn()
    {
        gameObject.SetActive(true);
        gameObject.transform.position = spawnPoint.transform.position;
    }
}

The alternative would be to let the Coroutines run on a controller script which is not getting inactive itself. It can actually be any MonoBehaviour but you can as well have a central controller script like e.g.

// is attached to an empty object in your scene which always stays active
public class CoroutineRunner : MonoBehaviour
{ 
    // Singleton instance
    private static CoroutineRunner _instance;

    // Lazy initialization of the Singleton
    public static CoroutineRunner Instance
    {
        get
        {
            if(_instance) return _instance;

            _instance = FindObjectOfType<CoroutineRunner>();

            if(_instance) return _instance;

            _instance = new GameObject(nameof(CoroutineRunner)).AddComponent<CoroutineRunner>();
        }
    }

    private void Awake()
    {
        if(_instance != null && _instance != this)
        {
            Destroy(gameObject);
            return;
        }

        _instance = this;
        DontDestroyOnLoad(gameObject);
    }  
}

and then do

public class playerCollision : MonoBehaviour
{
    public GameObject Destroy;
    
    [SerializeField] private Transform spawnPoint;
    [SerializeField] private Transform diePoint;

    private void OnCollisionEnter(Collision other)
    {
        if (other.gameObject.tag == "Enemy")
        {
            GameObject d = Instantiate(Destroy) as GameObject;
            d.transform.position = transform.position;
            d.SetActive(true);
            gameObject.SetActive(false);
            CoroutineRunner.Instance.StartCoroutine(waitForRespawn());
            CoroutineRunner.Instance.StartCoroutine(DestroyOptimization());
        }
    }

    private IEnumerator DestroyOptimization()
    {
        yield return new WaitForSeconds(2);
        d.SetActive(false);
    }

    private IEnumerator waitForRespawn()
    {
        yield return new WaitForSeconds(3);
        gameObject.SetActive(true);
        transform.position = spawnPoint.transform.position;
    }
}
  • Related