Unity async / await: Coroutine's Hot Sister [C# & Unity]

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments

Replace Task with UniTask. UniTask is a struct based Task that has next to no allocations and a bunch of phenomenal utilities.

https://github.com/Cysharp/UniTask

πŸ‘οΈŽ︎ 20 πŸ‘€οΈŽ︎ u/KAJed πŸ“…οΈŽ︎ Oct 09 2021 πŸ—«︎ replies

Nice topic, wasn't aware of it!

Next: That just motivated me to finally take a few minutes to learn/understand how to also use jobs and burst (to go beyond the main thread), without necessarily using ECS, i.e. the whole DOTS (w/ Entities.ForEach, etc).

πŸ‘οΈŽ︎ 3 πŸ‘€οΈŽ︎ u/PiLLe1974 πŸ“…οΈŽ︎ Oct 09 2021 πŸ—«︎ replies

Great video and really funny because I just yesterday implemented the "start a bunch of coroutines and count them and subtract from the counter in the callback and check the counter until it's 0" pattern and he's right.. it's a crappy way to work!

Will consider this a strong message to finally stop using coroutines.

πŸ‘οΈŽ︎ 4 πŸ‘€οΈŽ︎ u/Daxon πŸ“…οΈŽ︎ Oct 09 2021 πŸ—«︎ replies

I’m glad I learned about these coding discord bots before I learned about the mess that is unity coroutines, saved me some headaches

πŸ‘οΈŽ︎ 3 πŸ‘€οΈŽ︎ u/ethanglide πŸ“…οΈŽ︎ Oct 09 2021 πŸ—«︎ replies

Yeah it’s great, though I wish it could just take care of killing itself when it’s object dies. Would make it way simpler to code and closer to Coroutines in that aspect

πŸ‘οΈŽ︎ 3 πŸ‘€οΈŽ︎ u/OscarCookeAbbott πŸ“…οΈŽ︎ Oct 09 2021 πŸ—«︎ replies
Captions
and his name is john c as you can see by my presentation here the async workflow is significantly better than the co-routine workflow but let me show you why hey boys so i've got this little scene here we've got a shape manager and then we've got these three shapes here and there's just two scripts the shape and the shape manager so i'll show you the shape first so all it is is one co routine here that takes in a duration and for the duration it will just rotate uh the shape and we're using yield return null here as we're just wanting to wait to the next frame and then repeat and then wait to the next frame until we're all done next i've got the shape manager which just has a reference to my shapes and then the button in the scene we'll click this and it will just loop over the shapes and start all my co-routines each loop will just add a little bit of extra time so the result of that will be this just the shape spinning as you can see each one lasts for a little bit of extra time and if we want we can just keep clicking it and it will speed up and speed up as they're all stacking cool bananas so the first thing we're going to do is convert this uh workflow to the async workflow as opposed to the co routine workflow so to do that is quite simple i'm going to just copy this and let's change this function first to asynchronous so we want to remove ienumerator and let's make this async and for now let's return void and then down here instead of using yield return null we will now await task and this is in the system threading tasks namespace yield which is almost the equivalent to yield return null i'll talk about this a little bit later and that is all we have to do to convert a co-routine into an asynchronous function so that's pretty easy so save that and then across here in our shape manager we now don't need to start code routine we can just directly call it just like that all right so now in our unity editor let's see and as you can see it does the exact same thing uh but what is the benefit here i mean all we've really done is uh made the code a little bit cleaner a little bit shorter right we don't have to call start co-routine here um this is about the same so to get into the benefits of this uh we need to start talking about use cases and before i continue right in this i'm just rotating your shape right but in your game you could be doing anything from uh waiting for an attack animation or a missile or moving a character you know any any kind of game logic uh just replace this rotation with with that okay so the first use case will be what if we would like this uh these to run sequentially right one after another making sure that the last one finishes uh before we begin so using co routines you might achieve that like this so you've got your three k routines here and then you've got another co-routine uh where you're yielding the results of the uh of the co-routines themselves so this will actually run sequentially this one will finish first and then this one and then this one um alternatively you might do something like this so you might have uh co routines and then you're you're chaining your co routines which can get hella messy a horrible way to work to be honest uh i used to do it though before i i changed many many years ago so that's how you would traditionally achieve sequential in co routines for us though it's a lot more simple so uh right now we're just returning void from this rotate for seconds uh so what we need to actually do is go back here and instead of returning void we need to return a task now a task if maybe you've dabbled with javascript a task is similar to a promise so what it is is we're saying all right give us the result of this function uh if it's asynchronous give us a task so that we can monitor if it's done how long has elapsed if we want to cancel it if it's a long-running task for example another thing you may have noticed is that we're actually returning something here yet we don't actually have to physically say return uh you know object or whatever uh in our function c sharp just uh already knows that you know this is this is an async function uh you're saying to return a task so i'm just going to return this asynchronous object uh for you there's there's no reason for us to actually have to do it every single time so now back on our shape manager you'll see here i'm getting this squiggly here because it's saying this is an asynchronous function but you're just calling it which means it's not going to wait it's just going to continue we can fix that by now making this one an async function and we can simply say i want to await this function call now so now it's going to go to the first iteration and this is going to complete before going to the second iteration so let's actually say that in action this one will go then that one will go and then that one will go so as you can see that is significantly cleaner than doing something with co routines like this especially if you were doing the uh the chaining ones where your start co routine and you're chaining to the next ones uh very very easy but it gets more powerful than that let's say we don't actually want to run these sequentially we want to run these synchronously so all at once but we still want to make sure that they're all done before we continue to our next logic so as an example i'm going to make a serialized field here game object and this is just going to be my finished text and if i go back into unity here i've actually made just this little finished text here just like that so i'm going to assign that into my shape manager and just to make it uh reusable i'm going to say at the start here finish text dot set active equals false and then down here at the bottom i'm going to say finish text set active true now let's actually make this uh synchronous so we obviously don't want to await this or else it will be sequential but we do want to keep track of this because this is a task remember this is returning a task we need to keep track of it to make sure that they're all finished so we can create a little collection here tasks equal new task and the size of it is going to be the same as what we're iterating here because we know that there's three shapes so we're gonna perfor we're gonna we're gonna hold three tasks now down here we can say tasks.i is equal to this task now we've got our three tasks so what we can do is we can await task dot when all and just send in our tasks array so this as soon as this is called these will start start rotating straight away uh so they're all going to start rotating together and but they're all going to be rotating at different time lengths we don't necessarily know these arbitrary timelines uh when it gets to here this will wait for all three of them to complete before continuing so let's see that in action play and as you can see they're all starting and there we go and now our finished text has shown beautiful and to give you a better use case uh say you're doing a tactics game where you've got like five enemies and five uh friendlies and they they all attack at the same time so all their enemies attack and then all the unit players attack you could loop through all of your enemies and attack some of them might be casting a missile some of them might be you know swinging a two-hand weapon uh who knows and they could be going for any number like any any amount of time so you could just call them all put them all into a task array and then wait for them all to finish with co-retained you can do like messy things like for example you could send in an action here this will be a call back and then when you when when the task finishes you could do call back right and then when you're actually calling these things you could be like start co routine whoops action one and then in here you could set your callback and now you could have like a private variable up here private inch enemies finished attacking and you could say like that plus plus right and then you could have a you could have some kind of while loop that's constantly checking have all the units finished attacking uh and then and then you could uh and then you could action it and then continue uh basically it's it's it's messy uh also you could if all these scripts were in the same script you could you know you could just do that without the callback but to be honest it's pretty horrible it's a it's a crappy way to work and it makes code super unclean in my opinion this is incredibly simple and i i'm setting this these uh rotate for second tasks to this but as long as it's a task it does not matter what it is this could be any number of functions you can just slam them all into the um into the tasks list and it will all work exactly the same if you don't like using arrays because you think they're a little bit uh rigid you can just make a a list here of task and then here tasks you can say tasks add and then just chuck that in like that and that will work just fine as well another thing you could do possibly is you could let's say that this starts at one and then our first one our first shape we can actually run sequentially so let's await this like that uh actually no that's our first one so zero so this one will be running sequentially and then these ones will be running synchronously so let's see what that does that one will finish and then those ones will go so very very flexible now i have not even told you about the most powerful thing of the async workflow over a co-routine workflow and that is that async functions can actually return data okay which you cannot do in code routines so let me show you so let's make an async function here now when you're returning something from an async function you need to wrap it in this task object and let's say just we're turning an int then this will be called get random number and then in here we'll save our random number is equal to random unity and gen range and let's make it between 100 and 300 and because we're in an async function let's make it a little bit interesting and actually await that amount of time so task dot delay send in our random number now one thing to note uh when using tasks over a co-routine is that co-routines use well unity as a whole uses floats as seconds so 1.2 is 1.2 seconds with c sharp with tasks in c sharp they use milliseconds so here you can see i'm i'm waiting anywhere between 100 to 300 milliseconds which would be 0.1 or 0.3 in unity standard okay so for example also if you're if you're using unity units uh and let's just call that delay to use that here in a task you could cast it to an end and then you could just do uh delay times 1000 and that will do the same thing it will convert 1.2 into 1200 and then you'll be good to go but we're we're not accepting anything here we're just saying just wait for the random number and then we will return random number so now we can say let's just copy this out and we can say random number is equal to get random number and then we will print random number so if we go back to unity now let's just not maximize it then press start aha so this is obviously not what we wanted to return so this is an asynchronous function but i did not await it here and if you do not await an asynchronous function it will return the task object as opposed to the value the result of the task so what you can do with this task is as you can see if i'm hovering over this you'll see that it is of task of type task int and with this i can just check is it cancelled is it completed and uh if i wanted to i could actually cancel the task so if you're doing like for example a uh a button click and it will do a long running task on some kind of ui thing you might want to give the user to cancel it if it's taking too long where so then now that you've got this reference to this task they click the button you cancel the task it stops running okay that's a little bit deeper than what i wanted to go for this uh for this video so for now i'm just going to await it and as you can see if i hover over it now it's it's actually the inch so if we press play we can press start and we'll get our random numbers after it's awaited it easy peasy and if you're trying to call an async function but not from an async function you obviously can't use the awake keyword here so an alternative is to say get awaiter dot get result and that will be the same thing it will give you the integer number another way to do it which is a little bit looks a little bit cleaner is to just say results but the problem with this is if you've got a uh try catch loop in here right and you've got some custom exception here and you throw it once it actually gets up to here it will just give you a really generic error and it won't give you your prop error so it is actually always better to use this ugly thing here to get the result if you're calling it from a synchronous function so that was all i wanted to show you in this tutorial it's just kind of like a surface dive of co-routines vs async if you start experimenting with them both i can pretty much guarantee that you're going to enjoy the async workflow over the co-routine workflow one caveat uh which i have noticed and it's not really documented or that i can't really find any workarounds for is that uh async workflow does not work in webgl so if you're working making a webgl game just use code routines and save yourself a lot of heartache of having to refactor all your code which happened to me uh other than that uh there are more advanced techniques to the async workflow for example you we've got access to parallel 4-h loop and for loop which means instead of sequentially going through your collection and doing something with it you can grab as many threads as you've got and do them all at once so if you want a more advanced workflow tutorial just let me know and i'll make one otherwise i hope this has helped if you liked it subscribe like and i will see you for the next video see ya
Info
Channel: Tarodev
Views: 38,645
Rating: undefined out of 5
Keywords: tarodev, unity, tutorial, c#, async await, await, multithreading, task, coroutine, sequential, synchronous, rotate object, return data from task, game development
Id: WY-mk-ZGAq8
Channel Id: undefined
Length: 16min 18sec (978 seconds)
Published: Sat Oct 09 2021
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.