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

Co-routines years after they were introduced, opinions?

Discussion in 'Scripting' started by eelstork, Sep 27, 2019.

?

Are you using co-routines

  1. Often

    41.7%
  2. Only to solve specific problems

    50.0%
  3. I avoid them

    8.3%
  4. I preach against them

    0 vote(s)
    0.0%
  5. I don't really know how to use them

    0 vote(s)
    0.0%
  1. eelstork

    eelstork

    Joined:
    Jun 15, 2014
    Posts:
    221
    It's been a few years now that co-routines have been supported in Unity. Personally I loved the JS/UnityScript syntax for these. Since then I did warm up to C# a lot, but not to its equivalent syntax.

    Having said that, I moved away from co-routines for reasons which have less to do with the syntax, more with how they solve the problems they were intended to solve (and the other problems that this tends to introduce, like accidentally spawning the same co-routine several times over).

    But I also know that some programmers appear to find these very useful.

    So I'd like to ask, who nowadays uses coroutines in their game logic, and how much?

    Poll!
     
  2. mgear

    mgear

    Joined:
    Aug 3, 2010
    Posts:
    9,350
    always using them,
    its easy solution for fades/transitions/slide effects, delays/waits and slow custom update loops..
    if necessary, i add some bool isThisRunning, so it cannot be called twice..

    are there some other good/easy options?
     
    Stardog, StarManta and SparrowGS like this.
  3. eelstork

    eelstork

    Joined:
    Jun 15, 2014
    Posts:
    221
    What exactly is a "slow custom update loop"?

    For what you describe there's tweening APIs but are they easier than coroutines... theoretically they should be.
    If you want to post a little code illustrating your use, I'd rewrite it a couple of ways, including with my own framework, as I'd like to compare.

    To me co-routines are a thing we (people at work me included) were excited about years back. Incidentally we were also stuck with a solution where everything had to be event based (not so fun).

    But I also think the cases you describe are relatively safe and appropriate for co-routines.
     
  4. mgear

    mgear

    Joined:
    Aug 3, 2010
    Posts:
    9,350
    slow update, would be something that needs to execute only every x seconds for example.
    (so to save time needlessly running inside Update() and checking time, coroutine can do it easily)

    here's some typical stuff (which could be cleaned up, but works for now..)
    color fade https://gist.github.com/unitycoder/dd464da0f0ffe5041e46
    ui slide in-out https://gist.github.com/unitycoder/285198e5414729792808081827520b12
    camera shake https://answers.unity.com/questions/212189/camera-shake.html?childToView=992509#answer-992509

    i've tried those free tween-plugins, but maybe havent properly read the docs..
    often they dont work as expected, are too complicated or missing feature, and then just end up using simple coroutine..
     
    SparrowGS likes this.
  5. eelstork

    eelstork

    Joined:
    Jun 15, 2014
    Posts:
    221
    i've tried those free tween-plugins...

    Why does this sound so familiar!
    Having a look...
     
  6. Munchy2007

    Munchy2007

    Joined:
    Jun 16, 2013
    Posts:
    1,732
    Despite the bad press they seem to get, I use them quite a lot. As long as the potential pitfalls are understood and avoided I've found they can be very handy.
     
    eelstork likes this.
  7. exiguous

    exiguous

    Joined:
    Nov 21, 2010
    Posts:
    1,749
    My gripes with coroutines is, that they are are black box, especially when dealing with serializing state. A few years back I wanted to use them but I could not figure out how to persist the state they are currently in. There was a "savegame" plugin which promised that functionality but it was too much bloat for me. I contacted the author if it is possible to strip it down to this functionality. But he meant that it is rooted deep inside the asset and almost impossible to strip out.

    If the thread is also meant for discussion: How do you deal with that? User runs a coroutine, saves the game and exits. After loading it should proceed where it was. Maybe I just miss something. Maybe they are not meant for that. Maybe there is a workaround I don't know?

    Summary: If a coroutine would give me a byte arrray for serializing its state I would use them more often. As is I avoid them for beeing a black box.
     
    eelstork likes this.
  8. lordofduct

    lordofduct

    Joined:
    Oct 3, 2011
    Posts:
    8,514
    Coroutines just weren't designed with that in mind. Furthemore coroutines exploit a feature of C#/.net called iterator functions. So get a byte array of its state isn't exactly a simple thing (though not impossible actually... though hard to reinstantiate and use).
    https://docs.microsoft.com/en-us/dotnet/csharp/iterators

    I could think of ways to create coroutines that restart from a specific point in them that doesn't involve getting a byte array of the iterator. But it's very much non-trivial, and would likely gives lots of stipulations on how your coroutines would have to be set up. It'd probably be easier to just do it on a case by case basis.

    I would argue though that Tasks, or any other thing similar to coroutines and what they're intended for, would also have this limitation as well.
     
    Last edited: Sep 27, 2019
    Ryiah likes this.
  9. eelstork

    eelstork

    Joined:
    Jun 15, 2014
    Posts:
    221
    The blackbox feel is with me. Using a co-routine feels like starting a train, which then can't be controlled except via shared state.
    I was hoping using this to write cleaner game logic (in the time-based aspects, surely!) back in the days, but these trains need captains at the helm.

    @Munchy2007 I'm not sure they're getting so much bad press. I mean, my good guess is that they're here to stay : )

    @mgear8808 been having a look at your code. For a tween, if I packaged it in a separate component (which is totally fine, probably unless tweening tons of things at once), mmh... I feel like, wait, the Update() loop is already here and does the sam. But, it becomes an implementation detail, so I think it's down to taste. Problem with tweening is elsewhere (there's gotta be a problem with tweening - tweening a thing is always a minor chore. Trying to fix it once and for all seems to be a recipe for frustration)
     
  10. StarManta

    StarManta

    Joined:
    Oct 23, 2006
    Posts:
    8,773
    There's a panel about this from Unite here.
     
    eelstork likes this.
  11. Suddoha

    Suddoha

    Joined:
    Nov 9, 2013
    Posts:
    2,824
    Still using them alot. There are lots of things for which they suffice. And they're the ultimate choice when you potentially need to preserve backwards compatibility.

    Personally I never expose them though. In my code they're always an implementation detail.

    The most common pitfalls can easily be worked around, for example the one with spawning multiple coroutines. One could create a reusable and lightweight wrapper that encapsulates the limitations for execution.
    Also, this is not really a coroutine issue, even though it is often one thing that is mentioned when people discuss about them. The same thing is relevant with an Update-equivalent, or an async-equivalent. There has to be some kind of controlling when code should not be executed multiple times.

    Perhaps I'm not quite seeing the usefulness of it, or rather I'd say it might be an issue of handling/keeping state in the wrong place/scope.

    If the issue does not exist when not using coroutines, than it should be just as fine to achieve the same with coroutines. It's just a matter of separating state properly, i.e. seperating the state that needs to be persisted from the state that is only temporary and can be recovered from a snapshopt of persisted state.

    When you look at coroutines from the perspective of state machines, you should be able to get back to where it stopped using the a snapshot of the state as an "input". Most coroutines are not written with that in mind, but that's okay as long as they don't deal with specific requirements, such as recovering from a given state.

    If i misunderstood, an example would be nice to get a better understanding.
     
    lordofduct likes this.
  12. Joe-Censored

    Joe-Censored

    Joined:
    Mar 26, 2013
    Posts:
    11,847
    I use them when a specific problem calls for them. I favor using Update if the problem can be implemented just as easily there, only because it is a bit easier to see what is running every frame when looking at the code when it is called from Update instead of a coroutine. Just personal preference really.
     
    eelstork, Suddoha and Ryiah like this.
  13. Antistone

    Antistone

    Joined:
    Feb 22, 2014
    Posts:
    2,836
    For any non-trivial application, I wouldn't expect "saving" to serialize the entire state of the running software, I would expect it to strategically pick only those bits of information that are really important. For instance, if there's a door that swings open or closed when the player uses it, I would normally expect a saved game to just remember that it is "open" or "closed", not its exact current rotation value and momentum; if you save while the door is still animating, it just forces it to one position or the other.

    Therefore, "serializing the entire state of a running coroutine" isn't something I would expect a typical application to ever want to do. I could imagine edge cases where it might be useful, but I've never encountered one.
     
    eelstork, Joe-Censored and lordofduct like this.
  14. StarManta

    StarManta

    Joined:
    Oct 23, 2006
    Posts:
    8,773
    While I agree, I think that's largely because I've developed a habit of not using coroutines in situations where this would be a problem. For example, a coroutine-based system could easily govern the progress of a level, I know that coroutines can't be saved nor started up in the middle; and as a result, I make it a point not to write them that way. So it's not that this isn't a problem for coroutines; it's that this is such a problem for coroutines that we've all learned to not used coroutines for that.
     
  15. Antistone

    Antistone

    Joined:
    Feb 22, 2014
    Posts:
    2,836
    Not me. I can't think of any time when I have been remotely tempted to use a coroutine for something and then said "wait, but saving will be a problem."

    I just think that state information about a game is usually more convenient to encode in variables and traditional data structures than in which line of your source code is currently executing.

    Even for a completely linear progression with no player input like "enemy spawn waves" (e.g. spawn 10 orcs at point A at time B, then spawn 5 goblins at point C at time D, etc.) I'd generally rather store that spawn pattern in an explicit data structure and my progress through it in timer or counter variables, rather than writing the whole pattern out as a coroutine with wait statements in it. The data structure is easier to visualize, easier to modify, easier to make the information available to other parts of the program (e.g. so you can have a spell that changes the next wave, or add a new UI panel that gives a preview of what's coming up). So even if I knew 100% for sure I would never need to save my progress, I still wouldn't use a coroutine for that.

    For "levels" that might involve branches or backtracking, it gets even worse.

    I've used coroutines for things like:
    • Waiting for an asynchronous operation to finish (WWW, LoadResourcesAsync)
    • Choreographing complex animations with no gameplay effect (end-of-game results screen)
    • Pseudo-Update that runs less often than once per frame
     
    eelstork likes this.
  16. eelstork

    eelstork

    Joined:
    Jun 15, 2014
    Posts:
    221
    Though most games simply aren't designed around catch-all persistence (I serialize as little as I can), games which appear to serialize as much as possible, and appear to restore almost exactly to the point we left, are not rare.

    When I read about C# co-routines anywhere not Unity, I just feel that they simply weren't designed with games in mind. I may be wrong here.

    On the other hand, even if their usefulness seems to exclude logic resolution, and they are mostly useful to display non-critical (I mean, 'inconsequential' but not in a bad way) side effects, that is still pretty good; after all, polished games are all bells, whistles and firecrackers.
     
  17. Munchy2007

    Munchy2007

    Joined:
    Jun 16, 2013
    Posts:
    1,732
    This pretty much sums up my usage of coroutines too.
     
    eelstork likes this.