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;
}
}