I’ve often wanted to do things with coroutines that Unity’s API does not explicitly provide an interface for. Namely:
Get a handle to a specific coroutine, with which I can:
Pause and unpause the coroutine
Stop the coroutine
Ask if the coroutine has terminated
Start a coroutine from library code that is not directly aware of any MonoBehaviour instance
Receive notification whenever a coroutine terminates
Not write code to do these things every time I need them!
I’ve also recently had a discussion with someone else who was interested in these same sort of features, but who lacked the ability to implement them. So I present a simple TaskManager script that makes this all much simpler. The source is here:
In a nutshell, this allows you to - with no initialization and from anywhere in any code - do things like:
// Equivalent to StartCoroutine(SomeCoroutine())
new Task(SomeCoroutine());
// Equivalent to the above, but keeps a handle to the running coroutine
Task t = new Task(SomeCoroutine());
// Pause the coroutine at next yield
t.Pause();
// Resume it
t.Unpause();
// Terminate it
t.Stop();
// Test if it's still running.
if(t.Running) {}
// Test if it's paused.
if(t.Paused) {}
// Receive notification when it terminates.
t.Finished += delegate(bool manual) {
if(manual)
Debug.Log("t was stopped manually.");
else
Debug.Log("t completed execution normally.");
};
Though I’m unsure to what degree its really usefull cause coroutines still run in the main thread so why would i really want to pause them, its not that you would do longer term stuff in them if you ever want to debug your code again, get it working right and leave any cpu time to anything else
The real gain to me is the explicit stop from a handle thats the major pain we all had as unity coroutines lack on that end as not even string handles help, it required to use Invoke which is troublesome in its own way
Thank you for this! I’m running a data visualization using unity and am pulling data with WWW calls from multiple servers in parallel, and this really makes my job easier. If you put this in the asset store (and sent me an email reminder) I would gladly pay money for this.
As someone with a non-programming background, forgive the naivety, but as an academic exercise would it be possible to take this code a step further and write it as an extension for the (useless) Coroutine class? Extension is just a static class with carefully defined methods, right? Could you add static variables to that class to mimic things like Running that the extended Coroutine would have access to? Or write them as methods (bool isRunning())?
See no use in extending coroutine as coroutine is just a dummy container.
the “is it running or not” is a thing that the managing host class etc in the end defines (here task) as its responsible for making the coroutine run at all. a coroutine can’t manage itself, it can not even exist on its own (it only exists in context of MonoBehaviour so if I were to add such capabilities somewhere it would be an extension to monobehaviour)
Thanks for sharing, I learned a good deal from the source code. Thanks again for not charging 15 dollars on the Asset store for this as seems to be the trend
As a hobbyist, this little module is just what I was looking for. Simple enough to integrate into my repertoire; one class that’s easily integrated instead of a complex, sprawling suite with more functionality than I need.
I am currently trying to add a Restart() method to the task manager, but I ran into a problem. To reuse the Enumerator I have to Reset it by calling Enumerator.Reset(). When I try to do that, I get a NotSupportedException in console.
Any ideas how to get this to work?
This does not work since iterators which are created by the compiler due to the yield keyword do not have any Reset method since there is no sensible way how that could be pulled off.
So what you want to do is not possible with an IEnumerator. However you could use the IEnumerable interface instead so you can simply create a new IEnumerator when necessary.
If you want to know more about IEnumerators, IEnumerables, coroutines and the yield keyword, have a look at my coroutine crash course that should clear up most of the confusion people have about coroutines and iterators.
@Bunny83 Thanks for the answer. I got it to work now by passing the IEnumerator method as a Func to my wrapper class and recreate the IEnumerator object each time the coroutine starts.
If someone is interested I setup a repository for my approach.