Monster shadow

VIDEO

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

Description:
The C# async / await workflow can simplify your code and give you more granular control over your game in Unity. Learn how to convert your current coroutine workflow, what a task is, how to run sequential and synchronous code and how to return data from an asynchronous function. UniTask: https://github.com/Cysharp/UniTask (Provides an efficient allocation free async/await integration for Unity) ❤️ Become a Tarobro on Patreon: https://www.patreon.com/tarodev ========= 🔔 SUBSCRIBE: https://bit.ly/3eqG1Z6 🗨️ DISCORD: https://discord.gg/GqeHHnhHpz ✅ MORE TUTORIALS: https://www.youtube.com/tarodev 0:00 Super professional intro 0:30 Standard coroutine workflow 1:19 Converting to async / await 2:44 Use-cases of async / await 3:10 Running functions sequentially 6:00 Waiting for synchronous tasks to complete 10:20 Mixing sequential and synchronous tasks 11:00 Returning data from an async function 14:20 Calling an async function from a non-async function 15:10 WebGL caveat 15:45 Other advanced async techniques
ADD A COMMENT

JimPlaysGames
Holy crap this is amazing. I had no idea this was a thing and I am definitely putting this to use. Thank you!
Roberto De Montecarlo
Dude, thank you so much. I mean, really! You made a great masterclass of how to use properly Tasks, Async/Await. Now, I truly understand it. No more crappy code anymore.
Redwoods
What a great tutorial that I didn't even know I needed :3
Tarodev
Go live your best async life
Deivid Torres - GameDev
Great explanation! Keep the great work up!
CaHydra
Thanks for enlightening me about async & await, this will come to great use.
DynastySheep
This is a nice tutorial, I have been using coroutines all the time and will surely start using the async/await in my future projects. Thanks :)
Immortality
oh god why I didn't knew this method earlier. nice video btw! keep up the good work
VEOdev
It is much better but the question is who is more performance heavy ?
zondaSOULS
So you said that async isn't continuing each frame. Doesn't that make frame dependant stuff inconsistent?
Chrisbrei
I really want to eat the shapes
Chrisbrei
@Tarodev ☹️
Tarodev
No sir, don't do it
Balasubramani Mudaliar
great tut😀
Khold
I loved await & async until I tried to restart the level or quit the game (because it continues to run). Tried to read about Cancellation tokens, but I didn't understand it at first glance, so I just made a simple thing. Example: Check if anything inside the async has lost reference or when OnApplicationQuit is fired. If any of these are true -> set "bool canceled = true;" & return. Once it's back to the await, it does a check "if (x.canceled) return;" so it doesn't continue. would love to learn a proper way of doing it :) Edit: Figured out how to use CancellationTokenSource / CancellationToken with Try/Catch etc. Works fine now :)
pagoda_dimensionale
How he commented so fast the line of code at 1:32?
APaleDot
I think I ought to mention that most of the advantages listed here in favor of async/await are also present in Coroutines, but he doesn't write the Coroutine version in a way to take advantage of them. For instance, when running Coroutines sequentially, you do not have to make a bunch of different functions and then a separate function to yield on each function sequentially. You can simply do what he does for the async function. You can have that function return IEnumerator (just like how he changes it to async) and run it using StartCoroutine. Then you simply yield return for each shape just like he does with the async version. So in code it looks like this: public IEnumerator BeginTest() { for (var i = 0; i < _shapes.Length; i++) { yield return _shapes[i].RotateForSeconds(1 + i); } } Compare to the async version. It's identical. Similarly, when talking about ways to wait for all the Coroutines to finish he doesn't even mention WaitUntil, or the fact that you can simply yield on Coroutines after starting them. The later method leads to code which is very similar to what he writes for the async function: public IEnumerator BeginTest() { _finishedText.SetActive(false); Coroutine[] tasks = new Coroutine[_shapes.Length]; for (var i = 0; i < _shapes.Length; i++) { tasks[i] = StartCoroutine( _shapes[i].RotateForSeconds(1 + i) ); } foreach (Coroutine task in tasks) yield return task; _finishedText.SetActive(true); } Which again is almost identical to his async version. The major advantage of async functions is the fact that you can return values from them like a Promise in Javascript. But Coroutines are native to Unity and are therefore better integrated into that system: for instance Coroutines line up with the frames of the engine as he mentions, async functions often continue even after you hit stop in the editor which he doesn't mention. Also, some default Unity methods (such as Start) can be converted directly into Coroutines simply by making them return IEnumerator.
Souplé
So helpful before my exam. Thank you so much.
Nitin Dhami
Yes a more advanced version of the same would be great btw I just discovered this recently must say great content Cheers :)
Google-ALC-0315
Coronautine
Tarodev
So relevant
KeyboardKrieger
Man I will rework my whole code with this. I worked all day with async as webdev but somehow thought in unity coroutines are the way to go, while haiting these fuckers xD
Utku ERDEN
Great video totally and look forward to advanced tutorial
Gaston Claret
Great tutorial, thanks for this
SEE ALL COMMENTS


Transcript:

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