I created a class and a subclass for calling coroutines with a probability and a level threshold.
class SC
{
public int probability;
public int scoreThreshold;
}
class SCEvent : SC
{
public IEnumerator spawnEvent;
}
And I created a list of SCEvent objects in my script, then i inserted the variables in the list in Start() method. List<SCEvent> SCEvents = new List<SCEvent> {};
Now my problem is, when i call my coroutine like that for the first time StartCoroutine(SCEvents[1].spawnEvent);
it works just perfectly but at the end of the coroutine when i call it again with the same line it just doesn’t work.
I checked the list of SCEvents for every other element and they are fine. And I tried calling the coroutine for the second time directly (like StartCoroutine(coroutineName()) this), it works too. Oddly I can’t call my object’s coroutine element twice. What should i do?
IEnumerators are kind of “one-shot” objects. Once you iterate through them, they’re done. They cannot be enumerated twice, and therefore cannot be used as a coroutine twice or more.
In contrast, IEnumerable is an interface that provides a GetEnumerator() method which provides a fresh IEnumerator each time. if you want to be able to reuse your SCEvents, perhaps make them provide an IEnumerable instead of IEnumerator and call GetEnumerator() on them each time you want to start a coroutine.
Thank you very much. I changed the element IEnumerator in my SCEvent object into IENumerable and used .GetEnumerator() on my new spawnEvent element when i want to start the coroutine. It solved my problem. But i still don’t get it. Why this works
IEnumerator functionName()
{
yield return new WaitForSeconds(1);
StartCoroutine(functionName())
}```
and this doesn't
```StartCoroutine(SCEvents[0].spawnEvent);
IEnumerator functionName()
{
yield return new WaitForSeconds(1);
StartCoroutine(SCEvents[0].spawnEvent);
}```
Because what you are doing whenever you call functionName() is creating a new IEnumerator object. In the first example you’re calling the function every time and creating a new one. In the second example you’re calling the function just once and storing the result in an array. Therefore when you read the value from the array you’re trying to reuse the same IEnumerator object instead of creating a new one.
Oh ok, I get it now. If I want to use them more than once, I shouldn’t assign them to a variable or store them in an array, or else I should use IEnumerable instead of IEnumerator. I’m just discovering coroutines and I was clueless before you came. So thanks a lot again 0:)
I’ve written a coroutine crash course recently which may give some more insight in what the yield keyword actually does, how it’s related to the interfaces IEnumerator / IEnumerable and how Unity uses them to implement coroutines.
As PraetorBlue said. IEnumerators are reseteable at Coroutines. Each time you call IEnumerator.MoveNext() (i.e. each time unity execute some code from the coroutine), it’s been “disordered” and unity can’t come back from.
You can instead of create the method as an IEnumerator, create it as an IEnumerable in order to “save” the 1st state.
And you start your Coroutine like this StartCoroutine(YourIEnumerableCoroutine().GetEnumerator());
Anywy, I’ve written an special Coroutine asset which allows you to pause and restart/reuse coroutines based on the example above but still it’s not uploaded to the Asset Store.
Btw at this GitHub repo you’ve the source code and a package to install the full asset with a documentation an examples.
And here you have the documentation and some tutorials… I’ve tried to make it intuitive since uses the same yield return syntax than standard Coroutines. I hope your feedback if you use it.