Lately I’ve been hearing a lot of arguments against Coroutines, which is fair. But at the same time Coroutines are great, they make things super easy and intuitive.
So I was wondering. How do you do Timers in your projects? Whats the best way to wait for a few seconds and then do something?
I tend to use Coroutines and in some cases even the infamous Invoke. I can’t really bother to do Update() logic to handle this sort of thing, it’s not very flexible and it’s extremely verbose.
Is there any “best alternative” the people that complain against Coroutines use?
I would ask them. If they don’t have one you like, their complaints are not worth taking seriously. And, really, just what are these complaints? I love coroutines for the exact reasons you mention: easy, intuitive, flexible, and compact.
I personally use Update/Coroutines a bunch. I have yet to have a single issue with either.
I guess you could alternatively use ‘async/await’. The newer 4.x .net targeting has enabled it. Though honestly when it comes to the synchronization context and all that, I’ve yet to actually USE it in Unity. I primarily use async/await in my day job (business software). So as it comes to it “just working out of the box” I don’t know the status of it. Early on you had to implement your own syncrhonizationcontext, then I know some asset store things popped up… word was Unity would add direct support “out of the box” but I don’t know the status on that. I’m still back on an early Unity 2018 build without the 4.x .net support (just a couple more weeks and we’ll be moving to projects with newer versions of unity).
Anyways, maybe someone could give you more specifics on the status of async/await working in Unity without issues (note there can be invisible issues in the background). But once you got it working (may you have to import some asset or be on a specific version of unity) it’d be as simple as:
public async Task DoSomething()
{
await Task.Delay(1000); //wait 1 second
this.transform.position = Vector3.zero; //do something, in this case move to origin
}
Though I mean… performance wise I’m unsure about it compared to Coroutines. I’m going to guess it’s more efficient (again haven’t used async/await in unity specifically). But it’s going to have its own overheads as well for sure.
…
Alternatively, you could also write some wrapper logic that uses Update, but makes it straightforward/easy to use.
I think it might actually be slower than a coroutine, due to the fact that async await is designed to automatically sync thread contexts and unwrap tasks. I think there is a cost that you pay there for that convenience even if you aren’t using it to return results from other threads. I might be wrong though.
Coroutines apparently take unity timescale into account. Async Await won’t. Definitely something to keep in mind.
I often use Update for simple timers. It might be a couple extra lines of code, but it is really simple to keep track of exactly what is going on each frame. If the number of timers themselves is variable, or the timer is rather complex, then I’ll default to coroutines.
Some really interesting perspectives here! I think Kurt-Dekker’s example shows how integrating cooldown into Update can be easy and sensible, particularly when “TriesToShoot()” is (I assume) a polling method that necessarily needs to be called on each Update.
Now, if code that TriesToShoot conditions were replaced by an event-driven call to a method (which might well be the case if one were using the Input System instead of the Input class), moving the cooldown out of Update and into its own coroutine looks appealing to me. The coroutine could run until gunheat <= 0, then exit. That avoids constantly checking gunheat in Update, over and over, when it’s going to be zero a lot of the time. If one is counting CPU cycles, this is all small potatoes, and one method is as good as another, really. If one has an Update method that begins to accumulate a lot of conditionals, that might be another story. I like coroutines as an alternative there, but it makes just as much sense to break the “God” class containing that Update into multiple small classes, each of which can have its own (much cleaner, smaller) Update method.
I tell my students that Update is probably the way to go when something will need to run repeatedly, often, and for the lifetime of the object it’s in (like TriesToShoot, for example), and that coroutines might be the better bet when something happens from time to time, and for only a limited time.
I rarely use async/await, but I have done a lot of multithreaded coding. Something else I find I have to tell my students is that coroutines are not multithreaded. (The students who have done any concurrent coding tend to see the similarities between that and coroutines, and some of those students get really anxious about synchronization and cache-coherence issues that just don’t exist with single-threaded coroutines.) If you can do anything to get your mutli-threaded code to run on another core than the main thread, you get some real gains there. If the async/await stuff is running on the same core, I expect it would slightly degrade performance as, albeit light, there is some overhead in switching thread contexts.
Keep it coming! I love this kind of shop talk (when you work at home, alone, it’s all there is).
I also tend to favor code with extremely clear flow paths… I just ASSUME code will fail and have bugs, so when I’m writing it I am always thinking,
“Where can I put the breakpoints?”
… always… It’s like a mantra. I know that I am GOING to need breakpoints, that’s not even negotiable. Make it easy now, air the code out vertically, one thing at a time (which btw is all the CPU is going to be doing!), and step, step, step.
I have used Linq but trust me, there are no chained().on().things().in().my(codebase);
They allocate garbage and aren’t very fast. Which to be honest is ok by me. I am just really curious and willing to learn about some alternatives. Thanks for your response btw
Man, if you ever do, please post it. I used to be a politician, which was when I learned that Einstein was wrong when he said nothing moves faster than light. All politicians know that nothing moves faster than bullshit. Gamedevs probably know this too.
I’m a little frustrated that our OP hasn’t really addressed this. Just what are these complaints against coroutines?