We’ve had a long week strugling with “yields” in a “system” programmed by the other developer. This code breaks all OPP practices and it’s hard to get control on it (meaning for an senior developer to perfectly understand what is going on).
Note: We do understand the use of “yield return” in enumerators (IEnumerator interface etc.)
But in Unity coding style, yields are used everywhere for “stopping” the code. If called from MonoBehaviour, they are used with StartCoroutine(), and in ordinary classes they used to be wrapped inside “while” or “do…while” loops. Anyway, they look like ugly hacks.
So, we cleaned up the code; there are no yields anymore, but event driven code (custom event dispatchers). It works great and we finally have a control over the code.
I use Yields quite a bit. Particularly when working on weaker devices.
Currently Unity calls it’s MonoBehaviour actions through reflection, which is incredibly slow. Using an event system, and then having those events spawn Coroutines replaces the need for Update calls, and is immensely faster.
(that is stripped down code, I do other things with target, and handle Registration better)
This breaks no OO practices that I can think of, and allows the camera to handle it’s own update loop in an incredibly effecient manner while never calling any unecessary calls.
It takes a while to get comfortable with yields, however when used properly can create incredibly effecient, well designed code for Unity that bypasses one of Unity’s weak spots. Back when I swapped Update functions for temporary Coroutines, it took my iPhone framerate from 10 to 40+ (with the framerate limiter off).
Think of it as Temporary update loops and Threading-light. It is also good for functions that need to perform an action every second, or can update at a slower rate (and avoid uneccesary repeating calls just to check a boolean or timeElapsed).
I don’t like the coroutine/yield pattern either. But yeah, I can see how it might be worthwhile to squeeze out performance (if necessary) or for novice users.
For me, regarding Ntero’s comment about slow “update” calls, I ended up bypassing this (mostly) by only having one object listen for the Update event and then internally issuing out registered events through my own custom event handling delegate.
I haven’t had the need yet to do something along the lines of “do this, then this, then this” between frames though, so I don’t know how cumbersome it would be and if I would just give up and use the coroutines/yields. I would hope that it wouldn’t come to that though.
Ntero, I didn’t see StartCoroutine internals, but I think it’s simple, gotta be something with storing an enumerator in an array (member of of MonoBehaviour) and calling GetNext() on each enumerator in that array on each Update.
So, there you have it - your yield coroutine runs on update. Am I right or not?
Yap, that’s the way I do it, too. You got one “event entry point” or “heartbeat” in the application, which is basically called on update and dispatches an event, for which other parts of the application can be subscribed/unsubscribed. That’s the basics of an event-driven system in Unity.
Coroutines, are a tool. Like any other programming tool, they can be abused to make a mess of things.
They can also be used to great effect. The beauty of yield, is that it allows you to write code that uses asynchronous operations in a sequential fashion. This can greatly improve readability:
WWW request = new WWW("http....");
yield return request;
Debug.Log(request.data);
In my opinion, this is a far clearer way of programming an asynchronous web request operation than, for instance, the callback method, where you provide a separate function that will be called upon arrival of the request.
Another way of using coroutines, is for implementing operations that span over time:
If I were implement do this in an Update function, my script would need use to use several member variables, lists of them if I wanted to run multiple fades at a time. This coroutine is completely self-contained and doesn’t need any extra work to run in multiple instances concurrently.
You are likely right about the way it is implemented, but not that it’s like calling Update. Calling Update requires reflection, calling GetNext on the IEnumerator does not. I do not believe performance is an argument for using Coroutines, however.
Yeah, I completely understend your examples, and I know compiler is building a state machine for each “yield return”, thus making your code shorter. But my opinion is that this “stopping the code” logic breaks OOP, because an OOP developer expects that a method executes FROM START to the return statement, EACH TIME.
In Javascript it looks clean, but with C# you must write IEnumerator Start() and StartCoroutine() stuff…
Personally I would like Unity to have event-driven code like:
What is your definition of OOP developer? It differs from mine :P. My view on OOP is much like the Wikipedia entry on it, with key concepts such as encapsulation, abstraction, decoupling. For me, whether or not functions return immediately is not part of that package. By your definition, more well-known language functionality like multi-threading would also break OOP.
Yeah, it’d be nice if the WWW class had an “OnComplete” event so you could employ lambda functions easily, or at least wire it up to a method rather than checking on Update/IsDone:
Slightly off topic, but may I ask what you guys mean by calling the code by reflection? I have never heard that term before and would appreciate extra info.
Reflection lets you inspect a script’s various methods and properties while the program is running (and even invoke the methods, assign values, create new instances, etc.)
Since the Unity source code has no sense of what our custom scripts are, it uses reflection to inspect our custom code and invoke the pre-determined methods (such as Update, Awake, Start, etc.) at the proper times. This isn’t necessarily the only way of doing it, but one of the more common and established methods.