Search Unity

Why do people hate Coroutines?

Discussion in 'General Discussion' started by yifu, Aug 1, 2014.

  1. yifu

    yifu

    Joined:
    Dec 23, 2012
    Posts:
    17
    Today, I was arguing with my CTO if we could implement a "Coroutine manager" in our project.
    The end result was that he "won".
    He does have a point : The only person who uses Coroutines is me in the studio. He doesn't want to expose too much stuff with coroutines so it may confuse other people. He said he is OK with me using coroutines as long as I encapsulate them so other people can't use them.
    I understand that he is just doing his job and I think he was just trying to make sure things are smoother.

    But I do get a lil sense of hate toward coroutines in unity.
    Let me start with that I personally don't abuse Coroutines, and I use them for an action that only last certain amount of time, or use them to replace simple state machine. It makes the code much shorter, "readable" and very close to human logic.

    However, I found that many more senior co-workers really hate coroutines, including my Leader programmer here( they are good programmers and I respect that, they also worked on some big console game projects ).

    Most senior people I have worked with came from C/C++ background, and I understand Enumerator is not part of standard C/C++. But come on, Enumerator is one of the key features in C#.

    So, why do you guys think many people hate coroutines in Unity?
     
    AdmiralThrawn and astracat111 like this.
  2. Demigiant

    Demigiant

    Joined:
    Jan 27, 2011
    Posts:
    3,242
    I think Coroutines are awesome, but as a project manager I would hate them too, unless I really trusted my team (and all of my team felt comfortable with them). Too prone to mess things up if not used correctly (like generating useless GC allocations or people forgetting to correctly clear them).
     
  3. StarManta

    StarManta

    Joined:
    Oct 23, 2006
    Posts:
    8,775
    One point against Coroutines is that if they are interrupted, they cannot be resumed. If you implement a state machine by using conditions in Update(), you can save the conditions and return to that state at any time - not possible to resume any particular spot with Coroutines. Similarly, if a GameObject is disabled, the coroutine is interrupted and cannot be resumed.

    Some issues scale upward with project complexity. If, for example, I create a script that uses coroutines and at one point must call StopAllCoroutines, it'll work fine. But in the future I basically am unable to ever use a coroutine on that component again. (Sometimes StopAllCoroutines is the only option; using StopCoroutine(string) is only possible if the coroutine was called with StartCoroutine(string), which does not allow you to send the coroutine any parameters.)

    Despite these, I do use coroutines in small, simple, controlled doses - usually for simple fleeting graphic flourishes and the like, or occasionally for splitting initialization processing across multiple frames, that sort of thing. But it's really easy to slowly slide into overusing them, and I totally understand outright banning them in a team setting.
     
    AdmiralThrawn and astracat111 like this.
  4. BrainMelter

    BrainMelter

    Joined:
    Nov 20, 2012
    Posts:
    572
    HSMs solve the same problem, and they're arguably simpler to work with. But I guess it comes down to personal preference. I will say the coroutine syntax can be awkward sometimes, such as when you want to call a coroutine from another coroutine.
     
  5. yifu

    yifu

    Joined:
    Dec 23, 2012
    Posts:
    17
    You guys do have a point.

    I totally agree that coroutines can be annoying after interrupted.
    The reason why I wanted a coroutine manager in the project was that coroutines can still execute while the related script is disabled . ( sort of like running coroutines outside of specific monobehavior. )

    As far as complexity, :), I have seen people doing everything in Update loops and it was nightmare to debug that update loop behavior especially with hundred of boolean checks( bad code , I know ). And I know doing all the continuous behavior in a update loop isn't a good idea.

    In contrast, many short lived behavior in update loop can be a coroutine and it would make things a lot easier to read and debug, such as lerping, wait a second/ a couple frame, Wait until an action is done, etc.

    @Izitmee I have never benchmarked the performance of coroutine, but I have read from Unity manual that "Coroutines have virtually no performance overhead.". I know that coroutines in unity is based on enumeration, so it should probably allocate a enumerator when we start a coroutine. I don't know If I should trust Unity or not in this case.
     
  6. BFGames

    BFGames

    Joined:
    Oct 2, 2012
    Posts:
    1,543
    I love Coroutines, and uses them a lot in my project - however we are only two programmers and we trust each other 100% :D

    I do however understand your CTO's point of view, but having everything in Update can also be a big mess and Coroutines can in different cases come out on top performance wise compared to checking for things in Update, it really depends on the project and situation.

    But watch them and watch your GC if your are going the Coroutine way :D
     
  7. sootie8

    sootie8

    Joined:
    Mar 25, 2014
    Posts:
    233
    Those people who hate coroutines are the same ones who advocate countless "magic" global(public) static variables, because its easier. Wake up people, C# is not C++ !
     
  8. Dantus

    Dantus

    Joined:
    Oct 21, 2009
    Posts:
    5,667
    Things have changed:
    http://docs.unity3d.com/ScriptReference/MonoBehaviour.StopCoroutine.html
     
    zoran404 likes this.
  9. Kondor0

    Kondor0

    Joined:
    Feb 20, 2010
    Posts:
    601
    People are afraid of change. I didn't like them much when I first heard of them, I had to saw more and more examples to finally realize their potential.

    It has to do with pride too ("my way works, I don't need them").
     
    astracat111 likes this.
  10. gallenwolf

    gallenwolf

    Joined:
    Mar 11, 2012
    Posts:
    118
    Disclaimer: I'm not a programmer, but I really enjoy coroutines. At the moment, I only use them for timing control. E.g. I want one click to call 3 methods with a short delay between each of them. Much easier than running/maintaining a running timer. Perhaps there is a way around that without coroutines, but I ain't no programmer ;)
     
  11. StarManta

    StarManta

    Joined:
    Oct 23, 2006
    Posts:
    8,775
    Still says: "Please note that only StartCoroutine using a string method name can be stopped using StopCoroutine."

    OTOH it does have a version of the method for the non-string version. So maybe they just never changed that line in the docs. In which case... does inaccurate documentation count as a strike against coroutines?
     
  12. npsf3000

    npsf3000

    Joined:
    Sep 19, 2010
    Posts:
    3,830
    People hate coroutines? Heck I reimplemented them so I could use 'em on a server!

    Is it *just* coroutines, or there a whole range of 'advanced' language constructs that they dislike? Do they have a reason (e.g. GC allocations?)
     
  13. yifu

    yifu

    Joined:
    Dec 23, 2012
    Posts:
    17
    Well... Most of people I know who hate them was because 1) things aren't done in a traditional way( ie. not done in a update loop ) 2). syntax takes time to get used to.

    None of the people I know hate it because of memory usage.
     
  14. npsf3000

    npsf3000

    Joined:
    Sep 19, 2010
    Posts:
    3,830
    Then it's just the way they like doing things. I take the opposite approach where I understand a lot of the syntax and various concepts (and there's a lot) and in return write code that can be half or a quarter the size what other developers might write. For me that just makes sense.
     
  15. Marble

    Marble

    Joined:
    Aug 29, 2005
    Posts:
    1,268
    I think coroutines are great. It's wonderful to be able to simply yield to another coroutine to create a queue of actions. For example, I can spawn a tutorial tip that, on start, yields to a tweening animation, then when that is complete yields until its target is enabled, then yields to an exit tween, and when that is over spawns the next tutorial tip. I could maybe do something similar with events, but in Update it would be a crappy series of conditionals with return statements embedded.

    It's also nice to have a variety of looping methods named after the function they are performing. And, to be honest, being able to wait for a frame or for a set duration is invaluable, especially with animation. In Mecanim, if I set a parameter, I need to wait a frame until it affects the state machine; that's easy in a coroutine.
     
    der_r likes this.
  16. der_r

    der_r

    Joined:
    Mar 30, 2014
    Posts:
    259
    What's become of those coders? Didn't they become programmers because of exactly that? Learning new things, being excited about what a computer/technology can do, etc. etc.? Where's their pride as a programmer? :D
     
  17. Dantus

    Dantus

    Joined:
    Oct 21, 2009
    Posts:
    5,667
    As far as I know this was introduced with 4.5. I haven't used it yet. To me it looks as that should work:
    Code (csharp):
    1. Corutoine coroutine = StartCoroutine (CoroutineMethod (someArguments));
    2. StopCoroutine (coroutine);
    But I don't submit bug reports for things I haven't tried. I haven't because I made my own implementation where this is possible.

    Regarding the topic, I indeed hate(d) coroutines, because they are extremely restrictive. They are great, but at the same time it didn't feel like one has the complete control. It wasn't even possible to stop a certain coroutine. When I finished my own implementation, I got the control I needed and was very happy again even with more advanced use cases.
     
  18. Cygon4

    Cygon4

    Joined:
    Sep 17, 2012
    Posts:
    382
    There also is a thing called "technical masturbation" - trying to use the most advanced methods and showing off your leet skills just because you can. I don't think that applies to coroutines, though.

    The problem I have with coroutines is that I consider them a "code smell", an indication that something was solved in a lazy or inflexible way and could be done better. Whenver a Unity coroutine is used, for example, it's usually a hard-coded sequence of events that should rather be represented as a list of actions (for which you can then write visual editors, too). Coroutines also make unit testing that much harder. That it's impossible to save their state has been mentioned, too, I think.
     
    LogicFlow likes this.
  19. der_r

    der_r

    Joined:
    Mar 30, 2014
    Posts:
    259
    Should? Why?
     
  20. Dantus

    Dantus

    Joined:
    Oct 21, 2009
    Posts:
    5,667
    I couldn't disagree more. If I am e.g. having a simple UI animation where a button needs to be moved from one position to another, it is extremely handy to use a coroutine. You may e.g. call myButton.MoveTo (newPosition, 0.2f), the button itself starts a coroutine an moves to that position. At any point, even if the button is moving, we may call myButton.ChangeColor (newColor, 1.0f) which starts another coroutine and changes the color within one second. Even if it is an artificial case, it is one that would be pretty complex to implement without coroutines.

    You have to use everything appropriately. If the coroutines can't be tested, but it is needed in your use case, don't use them for the unit tests. If you can use integration tests instead, perfect. If it is essential to save the state of a coroutine, there are mostly quite elegant ways to do it. If you want a configurable sequence of actions within the coroutine, set it up in a way it works.
     
    mandisaw likes this.
  21. Cygon4

    Cygon4

    Joined:
    Sep 17, 2012
    Posts:
    382
    Because hardcoding a sequence of events is inflexible. Because you cannot write visual editors for it. Because the state a coroutine is in is inaccessible to the rest of the code. Because you can't unit test them easily. Because if you use them for quests etc. you can't save your game's state.

    I would recommend watching through Uncle Bob's "Clean Coder" series (or buying the books). Two important statements that apply here:

    https://sites.google.com/site/unclebobconsultingllc/one-thing-extract-till-you-drop
    http://mikeebert.tumblr.com/post/25998669005/large-functions-are-where-classes-go-to-hide
     
    LogicFlow and wccrawford like this.
  22. Cygon4

    Cygon4

    Joined:
    Sep 17, 2012
    Posts:
    382
    Show me those :)

    Swiping those considerations away with "quite elegant ways to do it" and "set it up the way it works" is a tad too easy. If you have found good solutions to them, however, I'm willing to learn!
     
    wccrawford likes this.
  23. Dantus

    Dantus

    Joined:
    Oct 21, 2009
    Posts:
    5,667
    Before I write anything, you should explain your actual use case. There are use cases where coroutines just make no sense. I am not someone who is using coroutines for the sake of coroutines. But there are situations in which they are extremely helpful.
    What kind of actions do you need to execute? When is the order of them defined?
     
  24. der_r

    der_r

    Joined:
    Mar 30, 2014
    Posts:
    259
    Thanks for the links. :)

    I can write a visual editor for a co-routine based system, if I happen to need one. I just make each action that gets called a co-routine.

    The other problems are certainly there, but then again, it's a tool and there are use-cases where paying that price makes sense. I wouldn't want to go your proposed route for small things like tweens, sequenced mini-animations, short cutscenes and such.

    Let's keep in mind that we're "only" scripting here. It's important to keep your code clean, but it's equally important to know your techniques, their strengths and weaknesses, and where they can shine.

    Generally saying lists of action sequences without co-routines looks a lot like the technical masturbation you mentioned earlier, using more complicated techniques just because you can, if a simpler, humbler thing would suffice.
     
  25. der_r

    der_r

    Joined:
    Mar 30, 2014
    Posts:
    259
    Exactly right. It's all about your use case and not about getting praised by Uncle Bob.
     
  26. superpig

    superpig

    Drink more water! Unity Technologies

    Joined:
    Jan 16, 2011
    Posts:
    4,657
    Note that any IEnumerator-implementing object can be handed to StartCoroutine:

    Code (csharp):
    1.  
    2. public class AIBrain : IEnumerator
    3. {
    4.     public enum States { Idle, GoToTarget, PlayAnimation }
    5.     public States State { get; private set; }
    6.  
    7.     private YieldInstruction _yield;
    8.     public AIAgent Self;
    9.  
    10.     public bool MoveNext()
    11.     {
    12.         switch(State)
    13.         {
    14.             case GoToTarget:
    15.             if(Self.nav.remainingDistance < 2f)
    16.             {
    17.                 Self.animation.Play("DoThing");
    18.                 State = States.PlayAnimation;
    19.                 _yield = new WaitForSeconds(Self.animation["DoThing"].clip.length);
    20.             }
    21.             break;
    22.             case PlayAnimation:
    23.             Self.nav.SetDestination(GetRandomDestination());
    24.             State = States.GoToTarget;
    25.             _yield = new WaitForSeconds(1f);
    26.             break;
    27.         }
    28.         return true;
    29.     }
    30.     public object Current { get { return _yield; } }
    31. }
    32.  
    33. AIBrain brain = new AIBrain();
    34. brain.Self = GetSelf();
    35. StartCoroutine(brain);
    36.  
     
    der_r likes this.
  27. uk

    uk

    Joined:
    Apr 8, 2013
    Posts:
    14
    For those who hate coroutines. How do you postpone changes?, do you check every frame too see if some specific "time goal" have been reached?
     
    sootie8 likes this.
  28. deram_scholzara

    deram_scholzara

    Joined:
    Aug 26, 2005
    Posts:
    1,043
    I don't get why they aren't just considered a normal programming technique - they've been around for half a century and have improved dramatically since then. Saying they shouldn't be used because it's easy to make mistakes with them and break things could apply to any coding technique, so that's not really a valid argument against coroutines.
     
    mandisaw and PeterB like this.
  29. lmbarns

    lmbarns

    Joined:
    Jul 14, 2011
    Posts:
    1,628
    Never knew there was so much hate towards coroutines.
    Code (CSharp):
    1.  
    2. float delay = 0.2f;
    3. IEnumerator LoopExpensiveAction( float delay){
    4.     while(true){
    5.                  ExpensiveAction();
    6.                  yield return new WaitForSeconds(delay);
    7.     }
    8.  
    9. void Update(){
    10.        if(doAction){
    11.               ExpensiveAction();
    12.        }
    13. }
    14. }
    Is there a reason Update is better in this case?

    I use them when I want something to refresh at a slower interval than update, or when I want do perform a set of actions in sequence that require small delays between. Some scripts I don't even have an Update, just a coroutine with while(true) inside.
     
  30. willemsenzo

    willemsenzo

    Joined:
    Nov 15, 2012
    Posts:
    585
    You could declare the WaitForSeconds outside the coroutine and just initialize it once instead of returning a new WaitForSeconds every update (for dem statslovers).
     
    RavenOfCode likes this.
  31. lmbarns

    lmbarns

    Joined:
    Jul 14, 2011
    Posts:
    1,628
    Ah, I do use a time variable to delay stuff in an update, like attackTime = Time.time + 1;

    if(attackTime <Time.time){
    attackTime = Time.time +1;
    DoAction();
    }

    Is there a better way than grabbing Time.time every time I want to increment the timer, and every frame of the update loop where it checks attack time vs Time.time? Or is it's impact on performance not worth worrying about?


    Another place I find myself using Coroutines is for toggling GUI elements on touch. So hide the touched button for .66f then turn it back on....so I'm better to do the same as I do with the time variable than coroutine?
     
  32. Philip-Rowlands

    Philip-Rowlands

    Joined:
    May 13, 2013
    Posts:
    353
    I think that part of it is because Invoke is easier for a beginner. However, I've started using them for things like attacking, especially with weapons with a fixed firing rate like a gun. I've also used them in my current project to create a basic intro cutscene, pausing things while the player gives their name and then chooses a difficulty level. For that sort of thing, a piece of code that can be started on an event and then paused makes more sense than an Update loop.
     
  33. BFGames

    BFGames

    Joined:
    Oct 2, 2012
    Posts:
    1,543
    For stuff like this, coroutines is actually better for performance.
     
    lmbarns likes this.
  34. superpig

    superpig

    Drink more water! Unity Technologies

    Joined:
    Jan 16, 2011
    Posts:
    4,657
    Does that actually work? I know you can do that with WaitForFixedUpdate and WaitForEndOfFrame but I wasn't sure whether WaitForSeconds objects were safe to reuse or not.
     
  35. superpig

    superpig

    Drink more water! Unity Technologies

    Joined:
    Jan 16, 2011
    Posts:
    4,657
    Yes. There's also a question of 'expression of intent' to consider - if I want to run something at a regular interval, I can write it as:

    Code (csharp):
    1.  
    2. InvokeRepeating("DoSomething", 0, Interval);
    3.  
    or I can write it as:

    Code (csharp):
    1.  
    2. private float _nextTimeToDoSomething;
    3. void Update()
    4. {
    5.     if(Time.time >= _nextTimeToDoSomething)
    6.     {
    7.         DoSomething();
    8.         _nextTimeToDoSomething = Time.time + Interval;
    9.     }
    10. }
    11.  
    Of the two, which one is faster for you to read and say "Ah, he's causing DoSomething() to happen at a regular interval?" How about in situations where there's a lot more code present, such that I can't easily see whether _nextTimeToDoSomething is being modified elsewhere?
     
  36. yoonitee

    yoonitee

    Joined:
    Jun 27, 2013
    Posts:
    2,363
    What? How come I've been programming for 10 years but I've no idea what you're talking about?

    What's a coroutine? Is it like a subroutine?
     
    Teremo likes this.
  37. der_r

    der_r

    Joined:
    Mar 30, 2014
    Posts:
    259
  38. Whippets

    Whippets

    Joined:
    Feb 28, 2013
    Posts:
    1,775
  39. makeshiftwings

    makeshiftwings

    Joined:
    May 28, 2011
    Posts:
    3,350
    I don't think "everyone" hates coroutines. In fact this is the first thread I've seen where anyone expressed any dislike for them. I think they're great; I use them all the time. I also don't think they're that complicated, but maybe I'm just a crazy super genius. Probably not though.
     
  40. yoonitee

    yoonitee

    Joined:
    Jun 27, 2013
    Posts:
    2,363
    Well you learn something new every day. I always wondered what yield actually did.

    I guess that would be useful for loading a world and yielding while you redraw the loading bar.

    I suppose there's other ways of doing it, but it could be useful in a big for loop to yield after a certain amount of time to redraw the scene.

    But then I guess you have to make sure the rest of the program doesn't think the scene is loaded.
     
  41. Silly_Rollo

    Silly_Rollo

    Joined:
    Dec 21, 2012
    Posts:
    501
    If people hate coroutines maybe that's why I see so much Update abuse which to me makes for some crazy messy code. For simplicity's sake it seems better to only put in Update code that absolutely has to run every frame like input or maybe certain animation or rendering code but that's about it. Anything else that needs to run only sometimes seems more sensible as a coroutine. I'm also a fan of event driven so that instead of something sitting in Update looking for EventHappened have it subscribe to a sender for it.
     
  42. yoonitee

    yoonitee

    Joined:
    Jun 27, 2013
    Posts:
    2,363
    Isn't it all equivalent in the end? It's just that the code that's handling coroutines every frame happens under the hood?
     
  43. superpig

    superpig

    Drink more water! Unity Technologies

    Joined:
    Jan 16, 2011
    Posts:
    4,657
    Yes, ultimately all realtime systems boil down to there being something somewhere which is continually checking whether it's time to do something - be that polling Update style implementations, event-driven, coroutines, whatever. This is why I think stuff like expression of intent is important; if the details of exactly how that continual checking is done isn't important to your program, then you shouldn't be implementing it yourself, if possible.
     
  44. Teremo

    Teremo

    Joined:
    Jul 8, 2014
    Posts:
    82

    Going over the Intermediate scripting tutorials and came across Quaternions and Coroutines... wait a minute... hmm ... "co"-"routines." Ooooooooooh. For some reason it looked like "croutons." Any one up for a C# salad?
     
  45. User340

    User340

    Joined:
    Feb 28, 2007
    Posts:
    3,001
    Mmm, it depends. What kind of dressing are you going to use?
     
  46. Silly_Rollo

    Silly_Rollo

    Joined:
    Dec 21, 2012
    Posts:
    501
    Its about readability. If an Update function is many lines of code like
    Code (csharp):
    1.  if (_blah && Time.time > _timeNextBlah && !_blahElse) {  
    to check for a variety of conditions that's a lot less readable than a coroutine that yields for the appropriate time and kills itself when the condition is met rather than flipping some bools or changing a state enum. It's also easier debugging.

    It's also pretty rare you need to check for some condition every frame all the time. More likely is that a specific condition happens which should then start the coroutine checking for the next condition. That's the sort of thing that is handy to tie into an event system.
     
  47. Teremo

    Teremo

    Joined:
    Jul 8, 2014
    Posts:
    82
    What kind of dressing would you like?
     
  48. Teremo

    Teremo

    Joined:
    Jul 8, 2014
    Posts:
    82
    So after doing some research, I've came to realize coroutines are mainly used for iterations?
     
  49. hippocoder

    hippocoder

    Digital Ape

    Joined:
    Apr 11, 2010
    Posts:
    29,723
    Current project doesn't use them. They're strictly for lazy purposes and soak up the same amount of performance as though you were using an if statement and a timer. It uses that internally, and this cost is pretty much background noise in performance. So while coroutines and managing your own variant of time slicing is of equal performance, the real choice comes down purely to being a helpful thing rather than a truly useful thing.

    There is no performance benefit to using them over your own time sliced function, and pretty much the same effort to manage both. Obviously managing it yourself will lead to more flexibility.
     
  50. zDemonhunter99

    zDemonhunter99

    Joined:
    Apr 23, 2014
    Posts:
    478
    I ain't got nothing against Coroutines. I use em all the time wherever necessary.