So I am adding coroutines to a list and I want the coroutine to remove itself from the list when it ends, however I cant seem to get the coroutine to reference itself, here is the gist of it:
public List<Coroutine> crList = new List<Coroutine>();
public void Runner(){
Coroutine r = StartCoroutine(cr());
crList .Add(r);
}
public IEnumerator cr(){
// do stuff
crList.Remove( ??? );
yield return null;
}
How can I get the coroutine to remove itself from the coroutine list just before it ends? My problem is with this part:
crList.Remove( ??? );
I can’t seem find a way to get a reference to the coroutine from inside itself.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class CoroutineTest : MonoBehaviour
{
List<IEnumerator> runningCoroutines = new List<IEnumerator>();
// Start is called before the first frame update
void Start()
{
CoroutineBox box = new CoroutineBox();
IEnumerator myCoroutine = MyCoroutine(box);
box.coroutine = myCoroutine;
StartCoroutine(myCoroutine);
}
// Update is called once per frame
void Update()
{
print(runningCoroutines.Count);
}
IEnumerator MyCoroutine(CoroutineBox box) {
IEnumerator self = box.coroutine;
runningCoroutines.Add(self);
print("Got reference to self. Waiting...");
yield return new WaitForSeconds(2);
print("Removing self from the list");
runningCoroutines.Remove(self);
}
class CoroutineBox {
public IEnumerator coroutine;
}
}
Here’s another version that works with actual coroutine references instead of IEnumerator but it has to wait till the end of the current frame to start execution for real. The reason being that StartCoroutine immediately runs the coroutine to the first yield return statement when you call it. It would be tricky to avoid that:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class CoroutineTest : MonoBehaviour
{
List<Coroutine> runningCoroutines = new List<Coroutine>();
// Start is called before the first frame update
void Start()
{
CoroutineBox box = new CoroutineBox();
IEnumerator myCoroutine = MyCoroutine(box);
box.coroutine = StartCoroutine(myCoroutine);
}
// Update is called once per frame
void Update()
{
print(runningCoroutines.Count);
}
IEnumerator MyCoroutine(CoroutineBox box) {
yield return null;
Coroutine self = box.coroutine;
runningCoroutines.Add(self);
print("Got reference to self. Waiting...");
yield return new WaitForSeconds(2);
print("Removing self from the list");
runningCoroutines.Remove(self);
}
class CoroutineBox {
public Coroutine coroutine;
}
}
It doesn’t really help to use WaitForEndOfFrame in this particular case, as it’ll still wait until the end of the next frame if WaitForEndOfFrame is the very first yield instruction in your coroutine. Most of the built-in instruction will at least wait one frame when they’re the first instruction, that’s a small hidden detail.
So you could just put a yield return null there and it’ll effectively execute “earlier” (also in the next frame though).
So, I started looking into my own solution using a wrapper class for this, but after looking into the docs I do not really see a reason for it. Here is my line of thought:
Your list of coroutines can serve only two purposes:
To be able to stop all running coroutines that are included in the list.
To know how many coroutines are running (by observing the size of the list).
Point 1 Would be much easier addressed by instantiating a dedicated MonoBehaviour to start the coroutines on. They could then all be stopped using theMonoBehaviour.StopAllCoroutines().
Point 2 can just be handled with a counter and would not need any self-referencing.
Did I miss something? What do you need the list for?
Yes you are correct it is for both 1) and 2), but it is more practical for me to use these lists because I am not familiar with instantiating dedicated monobehaviours and thus I dont understand how to do it nor what it entails. Thanks
Well, for 1) I think all you’d need to do is have a class CoroutineRunner : MonoBehaviour {}and slap that on some game object. And then start your coroutines on that.theGameObject.getComponent<CoroutineRunner>().StartCoroutine(theCoroutine);. If you already have an existing script that is not running any other coroutines you can just use that instead. Integrating the counter for 2) would still need some additional work, though.
But go with your list if it the thing you feel comfortable with. You might want to be aware of potential memory leaks, though. If something else is killing your coroutines, for example their game object gettnig disabled or destroyed, they won’;t run to completion and, thus, also won’t remove themselves from the list.
I solve this kind of problem with a data structure called Queue.
public Queue<Coroutine> crList = new Queue<Coroutine>();
public void Runner(){
Coroutine r = StartCoroutine(cr());
crList .Enqueue(r);
}
public IEnumerator cr(){
// do stuff
crList.Dequeue();// or it could be after yield return depending on what you are trying to do.
yield return null;
}
This is useful if you want to maintain the data of which Coroutines are running and all Coroutine have same runtime.