Search Unity

  1. Megacity Metro Demo now available. Download now.
    Dismiss Notice
  2. Unity support for visionOS is now available. Learn more in our blog post.
    Dismiss Notice

A more flexible coroutine interface

Discussion in 'Scripting' started by chomp, Jun 23, 2011.

Thread Status:
Not open for further replies.
  1. chomp

    chomp

    Joined:
    Dec 6, 2010
    Posts:
    22
    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:

    https://raw.github.com/krockot/Unity-TaskManager/master/TaskManager.cs

    The comments document usage.

    In a nutshell, this allows you to - with no initialization and from anywhere in any code - do things like:

    Code (csharp):
    1.  
    2. // Equivalent to StartCoroutine(SomeCoroutine())
    3. new Task(SomeCoroutine());
    4.  
    5. // Equivalent to the above, but keeps a handle to the running coroutine
    6. Task t = new Task(SomeCoroutine());
    7.  
    8. // Pause the coroutine at next yield
    9. t.Pause();
    10.  
    11. // Resume it
    12. t.Unpause();
    13.  
    14. // Terminate it
    15. t.Stop();
    16.  
    17. // Test if it's still running.
    18. if(t.Running) {}
    19.  
    20. // Test if it's paused.
    21. if(t.Paused) {}
    22.  
    23. // Receive notification when it terminates.
    24. t.Finished += delegate(bool manual) {
    25.     if(manual)
    26.         Debug.Log("t was stopped manually.");
    27.     else
    28.         Debug.Log("t completed execution normally.");
    29. };
    30.  
    I hope someone finds this to be useful. Cheers!
     
  2. runner

    runner

    Joined:
    Jul 10, 2010
    Posts:
    865
    Thank You

    looks handy :cool:
     
  3. Sam at FPS

    Sam at FPS

    Joined:
    Sep 1, 2009
    Posts:
    80
    Nice work. I've been frustrated with the lack of flexibility in coroutines before. This looks like a nice addition to my library. Cheers.
     
  4. faultymoose

    faultymoose

    Joined:
    Oct 1, 2010
    Posts:
    246
    I was only just crying into my coffee last night about being unable to pause/resume coroutines! I thankyou sir, and so does the taste of my coffee! <3
     
    HypedStudios and Harinezumi like this.
  5. Dreamora

    Dreamora

    Joined:
    Apr 5, 2008
    Posts:
    26,601
    Very nice share, thanks :)

    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
     
  6. Julien-Lynge

    Julien-Lynge

    Joined:
    Nov 5, 2010
    Posts:
    142
    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())?
     
  7. Dreamora

    Dreamora

    Joined:
    Apr 5, 2008
    Posts:
    26,601
    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)
     
  8. Felix-Schlitter

    Felix-Schlitter

    Joined:
    Sep 30, 2013
    Posts:
    8
    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
     
  9. softrare

    softrare

    Joined:
    Jun 12, 2011
    Posts:
    444
    Hi! Well done! May I use this in my Asset Store package?
     
  10. WillNode

    WillNode

    Joined:
    Nov 28, 2013
    Posts:
    423
    Thank you for sharing that, I always use that for tweening things (because it's look handy).

    FYI, i have some better improvement from your code (i did a fork to your code ;) ), take a look on here:
    https://github.com/willlandmubarok/Unity-TaskManager

    it's about a coroutine system which uses delegates (more simple and powerful), so you can ...

    Code (CSharp):
    1.  
    2. void Start() {
    3.     //Tweens from (0,0,0) to (1,0,0) for 20 seconds
    4.     Task.Get(delegate(float t) { transform.position = new Vector3(t,0,0); }, 20);
    5. }
    6.  
     
  11. AdamRamberg

    AdamRamberg

    Joined:
    Dec 8, 2016
    Posts:
    22
    Great! Thanks for sharing :)
     
  12. kwiplimited

    kwiplimited

    Joined:
    Oct 23, 2017
    Posts:
    1
    How would you tell when the coroutine is finished?
     
  13. You put a command at the end of the coroutine which tells you that it has run and ended. Like you call a function or something.
     
  14. Supergeek

    Supergeek

    Joined:
    Aug 13, 2010
    Posts:
    103
    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.
     
  15. JGroxz

    JGroxz

    Joined:
    Dec 12, 2017
    Posts:
    12
    Put simply, this is awesome. Gives much more flexibility with Coroutines, especially when using singletons. Kudos for it!
     
  16. anisimovdev

    anisimovdev

    Joined:
    Mar 4, 2013
    Posts:
    22
    Thank you
     
  17. lennardbeers

    lennardbeers

    Joined:
    Apr 28, 2017
    Posts:
    10
    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?

    Here is what I have so far.
     
  18. Bunny83

    Bunny83

    Joined:
    Oct 18, 2010
    Posts:
    3,912
    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.
     
  19. lennardbeers

    lennardbeers

    Joined:
    Apr 28, 2017
    Posts:
    10
    @Bunny83 Thanks for the answer. I got it to work now by passing the IEnumerator method as a Func<IEnumerator> 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.

    https://github.com/EuleMitKeule/mono-routine
     
Thread Status:
Not open for further replies.