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) ========= 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

YulRun
I don't like comparing content creators, as everyone has a unique approach. But you're basically the Brakeys 2.0 in the void of Brakeys. GIving us more indepth, higher level tutorials that is exactly what the space needs. You definitely deserve much more Subscribers.
Tarodev
This is truly the highest praise. Thanks Matt.
Nguyễn Hữu Phú
But what would be perfectly frame-dependent in case of using async await but not use yield return new WaitForFixedUpdate()?
Georgi Mihalkov
I think most of us are already familiar with the async/await workflow. What I expected to see in the video is what it does under the hood and is it the same as the Coroutines. Because as we know, coroutines don't mess with threads, they're just a way to achieve asynchronous code in Unity. They execute in the game loop and that's, that. (Correct me if I'm wrong ofc) Async/Await on the other hand manages threads in some cases and generates a state machine over your asynchronous methods. Maybe you can address that in a future video?
Maxx Golbraykh
Fantastic video. I've always loathed using coroutines. Thank you!!
Italo Felipe Capasso Ballesteros
Although this is an excellent pattern to use, here's a warning on overusing async-await on Unity (there is a reason why coroutines still exist). Async will generate a lot of IL code, and if you use IL2CPP, it will also create a lot more C++ code than usual, which can translate to performance issues. Async-await will also generate more unmanaged heap allocations, and if not treated correctly, this can cause memory leaks if you overuse this pattern. Mind you that I'm not saying, "don't use async-await." I'm just saying do "overuse it" by solving every single problem with this pattern. If performance and memory consumption is a priority, I would advise to not overuse async-await. Performance slowdowns, unintended managed heap allocations, potential memory leaks, and potentially spaghetti code are hard to profile if you overuse async/await.
Swaggy Professor
Hey there , I just found your channel. I work full time as a C# backend dev and I always wondered how to apply all of these tools I use all the time like Tasks in Unity. I appreciate you doing these kinds of explanations nobody else on youtube is willing to do. Good work!
qzswar zeeshan
Love it.
Invalid_Studio
Wow! I didn't know I needed this tutorial. Job well done, thank you.
JAWEED
Task and coroutines are completely different things. Async/await is not there to replace coroutines. So the title of this video is just wrong.
Dan Bowes
Almost spooky how well timed this is, having some trouble with Coroutine Sequentials and cancelling a Coroutine, so this is perfect! Great explanation and well spoken, definite subscribe! :D
Joakin
This is it man. Quality content!
Dan Pos - Game Dev Tutorials!
Awesome video Tarodev - will definitely be using async more often now!
Dan Pos - Game Dev Tutorials!
@Tarodev Thanks a lot!
Tarodev
You look to have some interesting tutorials on your channel! I'll sub and take a peak shortly
Cook Padre
hmm.. Whenever I try to use async/await, my unity crashes when it gets to the await Task part..? Any idea why? I tried returning void, returning something, used GetAwaiter-GetResult, nothing seems to work it always freezes and crashes my Unity Editor.. Great video btw and Thanks!
Tarodev
@Cook Padre Nope, Task.Yield is a very VERY short wait, similar to yield return null in coroutines.
Cook Padre
@Tarodev Isn't Task.Yield() supposed to finish the task tho? An okay thanks for the other tips and thanks for fast response too!
Tarodev
I've actually encounter this problem before. First, ensure your task is actually finishing (ie:not infinite loop). Second, build the game and see if it happens in the production copy, if it works than its your editor... Upgrade to the next version (or down if you have to). I know it's a dumb way to fix it, but it worked for me... I couldn't find any other way.
Tom P
Finally some quality content for unity devs. I can't take any more "let's make a game from scratch" videos that teach you the very basics. When you get past that, you can't find anything! As an example, I dare you to find a video themed "save/load system in Unity" that will NOT teach you how to read to/write from playerprefs. Seriously? -_- Edit: yes please advanced usages! For instance, how to properly handle canceling async tasks (like when you have a coroutine and the player starts it again but you want to stop it first, I usually save it to a variable when starting, check if null then stop it before starting again; guess you could do the same?)
Tarodev
It seems a lot of people want the advanced version. I better get busy! Glad you like my content, Tom 😊
Michael S
Instead of a full for loop, you can just do it with LINQ by mapping them to the Task and awaiting the result right away: await Task.WhenAll( _shapes.Select((shape, index) => shape.RotateForSeconds(1 + 1 * index) );
Tarodev
Beautiful! But I try to keep as many external concepts out of each video. It's an inner-battle every-time I make a tutorial to write things a very specific way 😊
Fredrik Andersson
In my current project we use the async workflow as a solid solution for ghost states in UI. Since it's mobile games all views run in a fullscreen fashion, so only one view is accepting input at a time. All of the interactions basically boils down to a "int winningTaskIndex = await UniTask.WhenAny(UniTask[] interactionTasks);" but with a much neater and low boilerplate interface. We no longer have issues of performing actions during animations, double clicking of buttons or ghost-states that break the game.
FallenAvatar
Day late $ short. 2:50 and you already messed up
Dikran Ohannesian
Absolutely awesme video, consise and clear with great examples. You've got a new subscriber! ^^
Tarodev
Thanks Dikran!
Caleb
Everything you do, you do well! I am so excited to see your page gaining traction. It is well deserved
Tarodev
Thanks Caleb, I'm excited too! I'm so glad my teachings are helping so many people now 😊
Filoret
Thx, right in time. Just added state machine to mine project for character controller, and i used corutines at state classes... Well changed to tasks , i thing it became better performed Ps : side question, have You already used input controller? I cant figured out how to use accelerometer with it.. want to use some parallax effect with camera
Tarodev
Unfortunately I have never used an accelerometer... But I'm very interested so I may study up and make a video about it!
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