27:16: "Finally, coroutines are familiar to most Unity developers. Async isn't." Counterpoint: Async is familiar to most C# developers starting out with Unity, but show them coroutines and they're like "what in the world is this crap?!? Why not just use async?"
Coming from a Java background, multithreading/asynchronous coding in C#/Unity has been one of the biggest challenges to wrap my head around. I've gotten really used to Futures, with custom-configured ExecutorServices to handle thread management and UI syncing/callbacks. But async/await (and its kissing-cousin, Promises) definitely provide an easier mental transition - if you can keep straight what can happen in a worker/non-main context and don't mind relinquishing control over where/how your code will ultimately run*. I still feel hobbled, but at least I can move around a little bit faster :) * There was a GDC '17 AI summit talk with a technical deep-dive into managing core-usage while using asynchronous code that folks should check out - Beyond Framerate: Taming Your Timeslice Through Asynchrony
When async was put into Unity, I didn't have much use for it because I already knew more than one way to create a background thread. For me, the big issue with background threads is that I don't always know what will and won't run from a background thread in Unity. For instance, the error logging that he mentioned in the "Exception Hiding" Section of his talk; that's one of the things that doesn't work in background threads. Unity has a lot of things that doesn't work in background threads; it's an issue I've come across quite a bit when coding in Unity. Even calling the ToString() method on an Exception from an background thread will give you a warning because that can throw an Exception on certain Unity API items when called from a background thread. One of the hopes I have for DOTS, is that it might enable things that didn't work background threads (before now) to run in background threads in the near future. Coroutines run on the main thread, therefore, they have greater access to the Unity API. You can run things in a coroutine that can't run in async because the coroutine runs on the main thread. For instance, if you are doing a lot of interactions with the Graphics class or running Compute Shaders, you will be stuck in the main thread. I'm not trying to say that async is bad. It's a nice, short, and quick way to create a piece of code that runs in a background thread that returns a result without blocking the parent thread (which may or may not be the main thread). While coroutines are great for breaking up a long running (or multi-state) process that can't leave the main thread. Shoot, I've even launched plenty of non-blocking, background threads from coroutines, just so that I can offload work from the main thread while maintaining current state in long running processes. Both items have their uses.
I think you miss some information about about the way async works, first of all async uses tasks, not threads. Yes most tasks will run on background threads, but not all (just google tasks vs threads if you want to know more details about the difference). Next the tasks don't have to run on a background thread, the async "callbacks" use the SynchronizationContext. The "default" SynchronizationContext will (in most cases) run the async tasks on a background thread. But like you said a background thread can't access the Unity API, that's why Unity has a custom SynchronizationContext which synchronizes all async "callbacks" back to the main thread. So you can access the Unity API with async mehods. If you want to use async methods which run on the background and don't have to access the Unity API you can still do this by calling ConfigureAwait(false) then the SynchronizationContext will be "ignored" and the async "callback" will (in most cases) run on a background thread.
@@r1pfake521 Um.. . I hate to be the first to tell you this but tasks are just promise objects for interacting with work executed on other threads. They don't really exist, except as a logical model for understanding threading.
@@whoisj After reading both people's comments I have to say that your comment is very unhelpful. R1PFake's reply to Chavez about how async/await in .NET/C# is not implictly multithreading your code, and describing the purpose and meaning of the synchronization context which does control continuations, is a very appropriate answer to Chavez complaint.
@@r1pfake521 Not to mention you can actually write Async methods that work via Unity's coroutines (via custom Awaiters returned from extension methods on, for example, YieldInstruction) - so you not only know your async methods are 100% work on main thread, you can actually control the context they work in (which is important for games with complex updates like ECS-based architectures with interdependent systems etc.)
Bad thing is WebGL still not support for async right now. I have trying to use async to load Addressable Asset but editor shows that it doesn't support right now...
I'm pretty confused about the popup example, why does he load a whole scene just for the popup? And why would he need to select the buttons? Sorry, I'm just pretty confused...
It's easier to manage the UI lifetime by loading and unloading a scene (additive or not) than instantiating UI gameobjects directly, keeping references to them somewhere and destroying them at the end. It find it particularly usefull when switching between non-additive UI scenes. Like going from the multiplayer server list UI to the game loading UI for example. The server list UI only needs to know that when you click the Join button it loads the GameLoading scene non-additively so that ServerList UI gets destroyed.
I can imagine they might keep all their dialogs in a dedicated scene, with all accompanying logic, and then just call up that scene from anywhere with dialog parameters set as appropriate.
i had recently needed to convert some coroutine logic to async entirely because i couldn't make it await a result. so in my case i had a message that would move across the screen using ui builder and transitions. i didn't want the next action to happen until the message for the current action had shown and gone (so for example i have a "program" called "Hellhound" in the game and it attacks my player "DEADFEED" it first has to spot him, if it does a message shows saying "Hellhound spotted DEADFEED" the hellhound then need to wait for that message to finish before moving on to its next action of attacking DEADFEED, and it should wait til that message is gone before showing the message for "HIT" or "MISS" and when that is done then it applies the damage and shows a message for that. the code also simulates a dice roll, but an option is available to get a dice roll from the user, a diceware option, so in that case when the hellhound looks for DEADFEED then a prompt would need to show up asking the player to roll 1D10 and put in the number, the REF would also be asked, if the game is being played in online mode, to roll 1D10 so my enemies (programs) have logic that depends on the result of 1 or many asynchronous inputs or which has to wait for certain actions to continue. async made this much, much simpler, i had to make sure all my functions were async and returned a task, in the case of void, or task in the case of one that returns and i had to move the state machine out of a coroutine and into an async so it could await these results, but it's worked pretty nicely.
Super informative, but specifically for Unity and specifically for this use-case, why would using async/await as demonstrated be more efficient than instantiating a popup prefab, instantiating the desired number of buttons (using layout elements), and doing Button[i].OnClick().AddListener( () => some function(arguments) ) It seems to me that, for pop-up boxes, it is more efficient and allows greater flexibility to assign the desired Actions directly to the buttons...
consider the following: if(!await GameController.RollToHit(target, this)) { GameController.EndTurn(this); return 0; } (from gameController) public async Task RollToHit(NetActor target,NetActor Attacker) { ret=false; int D10= await MenuController.ShowMessageBox("Please Roll a D10"); if((ProgramDefense + d10 + InterfaceTotal + Int) > target.defense) { ret=true; } return ret; } (inside MenuController a visualElement using uiElements) public async Int ShowMessageBox(string Message) { while (MessageBoxOpen) { await Task.Delay(250); } var newMessage = Message; MessageLabel.text = newMessage; MessageBoxOpen= true; MessageLabel.AddToClassList("Shown"); while(MessageBoxOpen) { await Task.Delay(250); } return MessageBoxNum.Value;
} i'm glossing over a few things and kinda refitting some code i have written here to demonstrate the biggest benefit here is that the code continues right after the input, and the ui is kept pretty separate from the main code (so the menucontroller handles the popup, and likely other ui elements, it handles the click on the ok and cancel to set that field "MessageBoxOpen" to false to allow the async call there to continue. this could also be getting its results from a socket to another program, doesn't matter to the calling program, it just waits to get a result back from...wherever...however the program wants to do it.)
@@tuseroni6085 Came back to say that your comment started me down the async road a few months ago, and I cannot imagine coding pop-up items without it anymore. Thank you!
@@tuseroni6085 Is there a name to this pattern? Inhouse, I have made a very successful UI framework fully based on async like the speaker here hints at. It is incredible what the pattern has done for stability, cleanliness of code and iteration speed. So much that it ought to have a name and some recognition.
At 26:17 How do we fix it? (async throwing null reference). Why not just for the async write: `while (transform)` and it would stop if transform gets null and zero complexity added...
*while(this.transform)* will throw because the *this* reference will be null (or null-like) and the transform getter will throw, but indeed you can use *while(this)* and *if(this)* after awaiting to avoid this issue.
It's common practice to rename async methods by adding the suffix "Async".
Utils.LoadScene -> Utils.LoadSceneAsync
Good talk!
My #1 point for using Async is that you can use it inside scriptable objects and standard C# classes
This. Scriptables that have functionalities inside must use async.
27:16: "Finally, coroutines are familiar to most Unity developers. Async isn't."
Counterpoint: Async is familiar to most C# developers starting out with Unity, but show them coroutines and they're like "what in the world is this crap?!? Why not just use async?"
This was me a few days ago!
this is exactly what i was thinking!!!
Jep!
Lol me.
There’s definitely more documentation/tutorials/information out there about coroutines for Unity then there is for using async with Unity
whenever im having trouble sleeping i jsut turn up this talk. its so damn good
Coming from a Java background, multithreading/asynchronous coding in C#/Unity has been one of the biggest challenges to wrap my head around. I've gotten really used to Futures, with custom-configured ExecutorServices to handle thread management and UI syncing/callbacks. But async/await (and its kissing-cousin, Promises) definitely provide an easier mental transition - if you can keep straight what can happen in a worker/non-main context and don't mind relinquishing control over where/how your code will ultimately run*. I still feel hobbled, but at least I can move around a little bit faster :)
* There was a GDC '17 AI summit talk with a technical deep-dive into managing core-usage while using asynchronous code that folks should check out - Beyond Framerate: Taming Your Timeslice Through Asynchrony
I think he was super good, I could actually follow! Thx for this mr. Suomi! :)
Using this for UI solves so many problems!
Great presentation- thank you.
When async was put into Unity, I didn't have much use for it because I already knew more than one way to create a background thread. For me, the big issue with background threads is that I don't always know what will and won't run from a background thread in Unity. For instance, the error logging that he mentioned in the "Exception Hiding" Section of his talk; that's one of the things that doesn't work in background threads. Unity has a lot of things that doesn't work in background threads; it's an issue I've come across quite a bit when coding in Unity. Even calling the ToString() method on an Exception from an background thread will give you a warning because that can throw an Exception on certain Unity API items when called from a background thread. One of the hopes I have for DOTS, is that it might enable things that didn't work background threads (before now) to run in background threads in the near future.
Coroutines run on the main thread, therefore, they have greater access to the Unity API. You can run things in a coroutine that can't run in async because the coroutine runs on the main thread. For instance, if you are doing a lot of interactions with the Graphics class or running Compute Shaders, you will be stuck in the main thread.
I'm not trying to say that async is bad. It's a nice, short, and quick way to create a piece of code that runs in a background thread that returns a result without blocking the parent thread (which may or may not be the main thread). While coroutines are great for breaking up a long running (or multi-state) process that can't leave the main thread. Shoot, I've even launched plenty of non-blocking, background threads from coroutines, just so that I can offload work from the main thread while maintaining current state in long running processes. Both items have their uses.
I think you miss some information about about the way async works, first of all async uses tasks, not threads. Yes most tasks will run on background threads, but not all (just google tasks vs threads if you want to know more details about the difference). Next the tasks don't have to run on a background thread, the async "callbacks" use the SynchronizationContext. The "default" SynchronizationContext will (in most cases) run the async tasks on a background thread. But like you said a background thread can't access the Unity API, that's why Unity has a custom SynchronizationContext which synchronizes all async "callbacks" back to the main thread. So you can access the Unity API with async mehods. If you want to use async methods which run on the background and don't have to access the Unity API you can still do this by calling ConfigureAwait(false) then the SynchronizationContext will be "ignored" and the async "callback" will (in most cases) run on a background thread.
@@r1pfake521 Um.. . I hate to be the first to tell you this but tasks are just promise objects for interacting with work executed on other threads. They don't really exist, except as a logical model for understanding threading.
@@whoisj After reading both people's comments I have to say that your comment is very unhelpful. R1PFake's reply to Chavez about how async/await in .NET/C# is not implictly multithreading your code, and describing the purpose and meaning of the synchronization context which does control continuations, is a very appropriate answer to Chavez complaint.
@@r1pfake521 Not to mention you can actually write Async methods that work via Unity's coroutines (via custom Awaiters returned from extension methods on, for example, YieldInstruction) - so you not only know your async methods are 100% work on main thread, you can actually control the context they work in (which is important for games with complex updates like ECS-based architectures with interdependent systems etc.)
Bad thing is WebGL still not support for async right now. I have trying to use async to load Addressable Asset but editor shows that it doesn't support right now...
Perfect timing, thank you!
Good god almost fell asleep in the train back home good thing there is only two concepts to explain
I'm pretty confused about the popup example, why does he load a whole scene just for the popup? And why would he need to select the buttons? Sorry, I'm just pretty confused...
If you play the game he refers to it makes sense then.
It's easier to manage the UI lifetime by loading and unloading a scene (additive or not) than instantiating UI gameobjects directly, keeping references to them somewhere and destroying them at the end. It find it particularly usefull when switching between non-additive UI scenes. Like going from the multiplayer server list UI to the game loading UI for example. The server list UI only needs to know that when you click the Join button it loads the GameLoading scene non-additively so that ServerList UI gets destroyed.
I can imagine they might keep all their dialogs in a dedicated scene, with all accompanying logic, and then just call up that scene from anywhere with dialog parameters set as appropriate.
i had recently needed to convert some coroutine logic to async entirely because i couldn't make it await a result.
so in my case i had a message that would move across the screen using ui builder and transitions. i didn't want the next action to happen until the message for the current action had shown and gone (so for example i have a "program" called "Hellhound" in the game and it attacks my player "DEADFEED" it first has to spot him, if it does a message shows saying "Hellhound spotted DEADFEED" the hellhound then need to wait for that message to finish before moving on to its next action of attacking DEADFEED, and it should wait til that message is gone before showing the message for "HIT" or "MISS" and when that is done then it applies the damage and shows a message for that. the code also simulates a dice roll, but an option is available to get a dice roll from the user, a diceware option, so in that case when the hellhound looks for DEADFEED then a prompt would need to show up asking the player to roll 1D10 and put in the number, the REF would also be asked, if the game is being played in online mode, to roll 1D10
so my enemies (programs) have logic that depends on the result of 1 or many asynchronous inputs or which has to wait for certain actions to continue.
async made this much, much simpler, i had to make sure all my functions were async and returned a task, in the case of void, or task in the case of one that returns and i had to move the state machine out of a coroutine and into an async so it could await these results, but it's worked pretty nicely.
Every time i see on Unity docs and their videos the use of "public" i get chills...
Why not both?
Unitask exists...
Just stick to Async.
Enjoyable Talk! Clear explanations. Thanks very much
Is there any official Unity documentation about Async?
Super informative, but specifically for Unity and specifically for this use-case, why would using async/await as demonstrated be more efficient than
instantiating a popup prefab, instantiating the desired number of buttons (using layout elements), and doing Button[i].OnClick().AddListener( () => some function(arguments) )
It seems to me that, for pop-up boxes, it is more efficient and allows greater flexibility to assign the desired Actions directly to the buttons...
consider the following:
if(!await GameController.RollToHit(target, this))
{
GameController.EndTurn(this);
return 0;
}
(from gameController)
public async Task RollToHit(NetActor target,NetActor Attacker)
{
ret=false;
int D10= await MenuController.ShowMessageBox("Please Roll a D10");
if((ProgramDefense + d10 + InterfaceTotal + Int) > target.defense)
{
ret=true;
}
return ret;
}
(inside MenuController a visualElement using uiElements)
public async Int ShowMessageBox(string Message)
{
while (MessageBoxOpen)
{
await Task.Delay(250);
}
var newMessage = Message;
MessageLabel.text = newMessage;
MessageBoxOpen= true;
MessageLabel.AddToClassList("Shown");
while(MessageBoxOpen)
{
await Task.Delay(250);
}
return MessageBoxNum.Value;
}
i'm glossing over a few things and kinda refitting some code i have written here to demonstrate
the biggest benefit here is that the code continues right after the input, and the ui is kept pretty separate from the main code (so the menucontroller handles the popup, and likely other ui elements, it handles the click on the ok and cancel to set that field "MessageBoxOpen" to false to allow the async call there to continue. this could also be getting its results from a socket to another program, doesn't matter to the calling program, it just waits to get a result back from...wherever...however the program wants to do it.)
@@tuseroni6085 Came back to say that your comment started me down the async road a few months ago, and I cannot imagine coding pop-up items without it anymore. Thank you!
@@tuseroni6085 Is there a name to this pattern? Inhouse, I have made a very successful UI framework fully based on async like the speaker here hints at. It is incredible what the pattern has done for stability, cleanliness of code and iteration speed. So much that it ought to have a name and some recognition.
@@gecko6872 I had a similar journey, but from being at this speech live :D
MORE DOTS
ok stop dots
..............
Sorry, had to.
@@whoisj Funny .. I'm still waiting for a polish product ..
KAAAY, DPS, SLOWLY
@@user-gl1ls1jx3h And when I say slowly...I mean -
My first experience with async .obj file loading, it is very very slow!
At 26:17 How do we fix it? (async throwing null reference). Why not just for the async write: `while (transform)` and it would stop if transform gets null and zero complexity added...
*while(this.transform)* will throw because the *this* reference will be null (or null-like) and the transform getter will throw, but indeed you can use *while(this)* and *if(this)* after awaiting to avoid this issue.
@@tryfinally Yep true...
and here i am using async methods in coroutine bruh ...
I still hate 'var' -- var is like a cancer.
Me too, I dunno why people like it. Our team doesn't allow it.
you used the Drake meme wrong, mate.
This guy dose have some funny lines for audience but the problem is the way he delivers it...
I, for one, quite enjoined his dry and somewhat awkward delivery. Suits his character it seems.
как. же. он. медленно. говорит. делая. такие. долгие. паузы. что. даже. на 1.75 скорости. его. не возможно. слушать.
Может это не его родной язык, поэтому ему тяжело?
Nice memory leak in that PressButton async example *facepalm*
Jeeze. Can we get a little less boring. At least Mike makes it entertaining.
PLEase GOD Change speaker........