Is there a better way for a coroutine to wait for an event?

So I’ve set up the following code:

public class TravelManager : MonoBehaviour
    {       
        [SerializeField] Transform havenRespawn;
        [SerializeField] SceneSO persistentScene;
        [SerializeField] PlayerController playerController;
        [SerializeField] GameSceneManager gameSceneManager;
        [SerializeField] LoadingScreenManager loadingScreenManager;

        bool sceneDone = false;
        IEnumerator respawnCoroutine;

        private void Start()
        {
            GameEventsManager.current.onSceneDoneLoadingEvent += SceneDoneTrigger;
        }

        public void RespawnAtHaven()
        {
            if (respawnCoroutine != null)
            {
                StopCoroutine(respawnCoroutine);
            }
            respawnCoroutine = RespawnCoroutine();
            StartCoroutine(respawnCoroutine);
        }

        private IEnumerator RespawnCoroutine()
        {
            gameSceneManager.LoadScene(persistentScene, true, null);
            while (!sceneDone)
            {               
                yield return null;
            }
            sceneDone = false;
            playerController.TeleportPlayer(havenRespawn.position, havenRespawn.rotation);
            loadingScreenManager.CloseLoadingScreen();
        }
        private void SceneDoneTrigger()
        {
            sceneDone = true;
        }
    }

Basically whenever GameSceneManager is done loading a scene it triggers an event through GameEventsManager. What I want to do is wait for that event to trigger after the gameSceneManager.LoadScene line and once it triggers do the rest of the coroutine.

I had to set up a new boolean for this and it works fine, but I was wondering if there is a more direct way of listening to that event trigger in the coroutine.

You could make an IEnumerator version of LoadScene and then use
yield return gameSceneManager.LoadScene(...);
Otherwise, you could remove the coroutine altogether and move the code into the event:

public class TravelManager : MonoBehaviour
    {     
        [SerializeField] Transform havenRespawn;
        [SerializeField] SceneSO persistentScene;
        [SerializeField] PlayerController playerController;
        [SerializeField] GameSceneManager gameSceneManager;
        [SerializeField] LoadingScreenManager loadingScreenManager;

        private bool respawning = false;

        private void Start()
        {
            GameEventsManager.current.onSceneDoneLoadingEvent += SceneDoneTrigger;
        }

        public void RespawnAtHaven()
        {
            if(respawning) return;
            respawning = true;
            gameSceneManager.LoadScene(persistentScene, true, null);
        }

        private void SceneDoneTrigger()
        {
            playerController.TeleportPlayer(havenRespawn.position, havenRespawn.rotation);
            loadingScreenManager.CloseLoadingScreen();
            respawning = false;
        }
}
2 Likes

The easy answer is probably to just refactor your code. It might be as simple as moving the LoadScene call to the end of RespawnAtHaven and then have the SceneDoneTrigger run your coroutine instead, but then you probably don’t need the coroutine as it appears it’s just being used for that while loop.

Looks like @Trafulgoth_1 got the same idea.

1 Like

Thanks for the ideas!

I do think that @Trafulgoth_1 has a cleaner solution than what I was doing so I think I’ll go with that, though I would add in the SceneDoneTrigger method of your solution a check to see if respawning is true so as to avoid teleporting when onSceneDoneLoadingEvent goes off from other sources.

Thanks!