0
\$\begingroup\$

I am trying to add a delay to the respawn of my player. But when I add the yield return new line nothing runs after it. I have tried addiing debug.log after everyline and they stop out putting once they get to the yield return new line. This respawn is on the player on its own script and there is no other script that would effect the player respawn. The game is also 2D. Here is the code

public GameObject spawnPoint;
public float respawnDelay = 2;

public IEnumerator RespawnPlayer()
{
    gameObject.SetActive(false);
    gameObject.transform.position = spawnPoint.transform.position;
    yield return new WaitForSeconds(respawnDelay);
    gameObject.SetActive(true);

}

void OnCollisionEnter2D(Collision2D other)
{
    if (other.gameObject.tag == "Enemy")
    {
        StartCoroutine(RespawnPlayer());
    }
}

Thanks!

\$\endgroup\$
1

2 Answers 2

4
\$\begingroup\$

Coroutines run only on active GameObjects. Turning off the script's own GameObject with SetActive(false) means none of its component messages will be called, and all of its coroutines will be terminated. So your coroutine never has a chance to wake it back up.

You'll need to put the timer on something else. Worst-case, you could delegate this to an external timekeeper.

public class Sandman : MonoBehaviour {

    static Sandman _instance;

    public static Coroutine SleepFor(GameObject sleeper, float duration) {
        if(_instance == null)
            _instance = new GameObject("Sandman"). AddComponent<Sandman>();

        return _instance.StartCoroutine(SleepCoroutine(sleeper, duration));
    }

    static IEnumerator SleepCoroutine(GameObject sleeper, float duration) {
        sleeper.SetActive(false);
        yield return new WaitForSeconds(duration);
        sleeper.SetActive(true);
    }
}

Now when your script needs a nap, it can ask the Sandman,

Sandman.SleepFor(gameObject, 2f);

And the Sandman will be responsible for watching the hourglass while your GameObject sleeps.

\$\endgroup\$
1
\$\begingroup\$

Because for a Coroutine to run, it needs the GameObject having the MonoBehaviour responsible for calling the coroutine to be active. At the start of the IEnumerator, you deactivate the GameObject, from there the coroutine doesn't run anymore.

As you can see on this post, it just needs the GameObject to be active, not the script, so the most you can do is to disable the script, and use a transparent shader.

Or... you could call the coroutine from a persisten GameObject you'll know you won't ever disable. Example:

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

public class CoroutineController : MonoBehaviour
{
    public static CoroutineController Instance;

    void Awake()
    {
        if(Instance == null) Instance = this;
        else if(Instance != this) Destroy(gameObject);
    }

    public void Wait(float _seconds, Action onWaitEnds)
    {
        StartCoroutine(WaitCoroutine(_seconds, onWaitEnds));
    }

    private IEnumerator WaitCoroutine(float _seconds, Action onWaitEnds)
    {
        yield return new WaitForSeconds(_seconds);
        if(onWaitEnds != null) onWaitEnds();
    }
}

public class YourClass : MonoBehaviour
{
    public float waitTime;

    public void WaitAndReappear()
    {
        gameObject.SetActive(false);
        CoroutineController.Instance.Wait(waitTime, 
        => { gameObject.SetActive(true); });
    }
}

Here you should make use of Lambda Expressions to make something after the wait. I hope it helps.

\$\endgroup\$
1
  • \$\begingroup\$ I tried to move the code to a my game master object that never gets disabled. But now the player is not detecting that it is hittting a enemy. I added both of the functions to the gamemaster. \$\endgroup\$
    – C. Smith
    Commented Mar 21, 2018 at 2:24

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .