Just a quick question about the internals of stopping a coroutine. Does Stop/StopAll stop the coroutine(s) mid iteration or does the current iteration finish and then it stops.
I would make the assumption that the current iteration finishes and then it stops, but I’m curious if anyone knows for certain.
Edit: Just to clarify, by iteration I mean that current frame.
Thank you for the plethora of information Kurt. Helpful as always haha.
I am curious for your advice then, and I also have a question about one of the posts you linked.
As for the coroutine portion I’m curious as to what you’d recommend in place of them. I’m currently building what I call (for lack of a better term atm) a meta world simulation. This is where NPCs that aren’t in the current scene are having their movement and to some extent their behavior simulated. So, there is going to be a lot of calculations and a lot of nested looping going on with 100s of virtual NPCs.
It made sense for me to break it up into a coroutine (at least in my novice brain it makes sense). So, the coroutine, or a single simulation step, will run through all the NPCs once. Then once it’s run through the current simulation step, the coroutine will be started again. The only time I should have to call StopCoroutine() is when I’m switching scenes.
Any advice on a better approach? I’m very open to hearing it!
Also, my question about one of the posts you shared. In one you mentioned coroutines being a bad idea for changing one value to another. Can you elaborate a little more. I’m currently using a coroutine to fade in/out a vignette that appears when the player crouches. It didn’t make sense (in my novice brain) to put checks for this in an Update() method for something that will happen somewhat infrequently.
I understand that they run in the same thread. However, coroutines allow the large number of calculations and nested loops I’ll need to run across several frames to reduce the amount of hitching and stutters. So, I very much disagree that it matters nothing where the calculations are placed. If there is a better alternative to coroutines in this specific situation, I’m open to hearing it.
ANYTHING you can do in a coroutine can also be done simply in Update().
If there is lots of local bookkeeping related to an iterative task, it MAY be easier to do it in a coroutine.
If you contemplate stopping / starting that, the benefits begin to weaken.
That just means you don’t grasp how coroutines work. Refer to the above links until you understand why a unit of work done in Update() causes precisely as much CPU load as in a coroutine.
Coroutines are JUST a language construct, nothing more.
Repeat after me:
“Coroutines are simply a cute little tidy state machine, nothing more.”
Put another way and paraphrasing Mike Acton,
“Coroutines do not run in a magic fairy aether powered by the fevered dreams of CS PhDs.”
Yes, because for every yield an Update() call (frame) passes.
The first example does all 1000 iterations in one frame. The result is available immediately.
The coroutine spreads the same calculation over 333 frames. The result is available only after significant time passed.
If you call StopCoroutine() in and for the currently running coroutine, the running coroutine will finish executing until the next yield return statement or if it reaches the end of the method.
If you call StopCoroutine() from elsewhere or stop a different coroutine, there is no currently running coroutine (same thread, see above) and so that coroutine simple does not resume execution.
Which for what I’m currently building is acceptable. As mentioned previously this is to simulate NPCs that aren’t even in the current scene, so I won’t need immediate access to the data I’m simulating.
I see, I thought that was the case. Thank you very much for the info!
I wouldn’t underestimate the benefits that coroutines can have on readability. A lot of boilerplate code gets generated behind the scenes for us when we use them.
I do recommend adding an extension method like void RestartCoroutine(this MonoBehaviour monoBehaviour, IEnumerator coroutineToStart, ref Coroutine startedCoroutine) to the project to avoid unintentionally starting multiple instances of the same coroutine running at the same time.
But that is not the usual form of coroutines and CERTAINLY not one for this use:
ESPECIALLY if it is under arbitrary player control and can be multi-started.
Spiffy! I still won’t use a coroutine if I contemplate ad-hoc stoppage.
I will use a coroutine that can be stopped, but I will ALWAYS do that by passing in a continuation-check delegate, eg, the way the Unity WaitUntil() method works, accepting an “until” delegate.
To actually answer your technical question instead of discussing the merits of coroutines…
If you call it from anywhere “outside” the coroutine, then any code following the last yield which it already ran will not be executed. If you call it from anywhere “inside” the coroutine, then any code up to the next yield will still be run, then no more.