Unity Coroutines - What? Why? How?

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments

The video looks at what coroutines are, why you'd use them, and how to use them. For me, these videos are a great way to firm up my knowledge and more often than not learn a few extra bits here and there.

Hopefully, it's useful for someone!

For those who want something shorter, there's a 55-second version here: https://www.youtube.com/watch?v=JQ_JRINxaqo

👍︎︎ 1 👤︎︎ u/OneWheelStudio 📅︎︎ Mar 24 2021 🗫︎ replies
Captions
but you might be asking what is a co-routine co-routines in many ways can be thought of as a regular function but with the return type of ionumerator while coroutines can be called just like a normal function to get the most out of them we need to use start co routine to invoke them but what is really different and new with a co routine when what allows us to leverage the power of code routines is that they require at least one yield statement in the body of the co routine these yield statements are what give us control of timing and allow the code to be run asynchronously it's worth noting that co-routines are unique to unity and are not available outside of the game engine the keyword yield the i enumerable interface and the i numerator type are however native to c-sharp but before we dig in too deep let's get one misconception out of the way code routines are not multi-threaded they are asynchronous multitasking but not multi-threaded c-sharp does offer async functions which can be multi-threaded but those are more complex and i'm hopeful that it'll be the topic of a future video in a blog post if async functions aren't enough you can go to full-fledged multi-threading but unity is not thread safe and this gets even more complex to implement so let's start with a simple example of changing a numerical value over time to make it easier to see the results let's display the value in a ui text element we can of course do this with a standard update function and some sort of timer but the implementation isn't particularly pretty it's got three fields an if statement and an update that is going to run every frame that this object is turned on now of course while this works there is a better and cleaner way of course our code routines so let's look at a co-routine that has the same result as the update function we can see that the return type of the co-routine is innumerator notice also that we can include input parameters and default values for those parameters just like a regular function then inside the co routine we can define the count which will be displayed in the text this variable will live as long as the co routine is running so we don't need a classified variable making things a little bit cleaner now despite being personally scared of using wild statements this is a pretty good use of one and inside the while loop we encounter our first yield statement here we are simply asking the computer to yield and wait for a given number of seconds this means that the computer will return to the code block that started the code routine as if the code routine had been completed and continue running the rest of the program this is an important detail as some users may expect the calling function to also pause and wait so then when the wait time is up the thread will return to the co routine and run until it terminates or in this case loops through and encounters another yield statement the result of this co-routine i would argue while not shorter is much cleaner than an update function plus the co-routine only runs once per second versus once per frame and as a result will be more performant in my personal projects i've replaced update functions with co-routines for functionality that needed to run consistently but not every frame and it made a dramatic improvement of the performance of that game as mentioned earlier to invoke the co-routine we need to use the command start co-routine this function has two main overloads one that takes in a string and the second which takes in the co-routine itself the string based method cannot take in input parameters and i generally avoid the use of strings if at all possible so i'd recommend the strongly typed overload now if you have a co-routine especially one that doesn't automatically terminate you might also want to stop that co routine when it's no longer needed or if some other event occurs and you want to stop the process of that co-routine unlike an update function if the component is turned off the co routine will not automatically stop but if the game object with the co routine is turned off or destroyed then the co routine will stop so that's one way and can certainly work for some applications but what if you want more control you can bring down the hammer and use stop all co-routines which stops all co-routines associated with the given component personally i've often found this sufficient but you can also stop individual co-routines with the function stop co routine and give it a reference to the particular co-routine that you want to stop this is done by telling it explicitly which co-routine by name or as i recently learned you can cache a reference to a co-routine and use that reference in the stop code routine function now this method is useful if there's more than one code routine running at a time and we'll look at an example of that later if you want to ensure that a code routine stops when a component is disabled you can either call stop co-routine or stop all co-routines from a non-disabled function it's also worth noting that you can get more than one instance of a co-routine running at a time this could happen if a coordinate is started in an update function or in a while loop this can cause problems especially if the co routine like the one that changes our value never terminates and could quickly kill performance other uses of co-routines could be simple animations such as tweens and for example laying down the tiles of a game board using a co-routine may be easier to implement and quicker to adjust than a traditional animation laying down the game board pieces here actually makes use of two co-routines the first instantiates a tile in a grid and waits a small amount of time before instantiating the next tile the second co routine is run from a component on each tile this component caches the start location then moves the object a set amount directly upward and then over several frames lerps the object's position back to the original or intended position and the result is a floating down-like effect another advantage of using a co-routine over a traditional animation is the reusability of the code their co-routine can be easily added to any other game object with the parameter of the effect easily modified by adjusting the values in the inspector do notice that in the float down code it doesn't wait for the position to get back to the original location since a lerp will never get to the final value so if the code routine ran the while loop until it got to the exact original position the co-routine would never terminate if the exact position is important the position can be set after exiting the while loop code routines can also be used to create smooth movement such as a game piece moving around the board but there's a potential snag with this approach in my case i'm once again using a lerp function to calculate where the game piece should be for the next frame the problem comes from the fact that the lerp function operates over several frames this creates a smooth motion but in that time the player could click on a different location which would start another instance of the co-reading and then both coverities would be trying to move the game piece to different locations and neither would be successful or ever terminate now while this is a waste of resources it's even worse the player will lose control and not be able to move the game piece a simple way to avoid this issue is to cache a reference to the co-routine and this is made easy as the start coroutine function returns a reference to the started co-routine then all we need to do before starting the movement co-routine is to check if the co-routine variable is null and if it's not we stop the previous co-routine before starting the next movement co-routine as you might imagine it's easy to lose control or lose track of co-routines and caching references is a great way to maintain that control so that's the big picture of how co-routines work how they're structured and a couple examples of how to use them but the secret sauce is the yield instructions which are the key addition to co-routines versus regular functions and there are several options built into unity and just as a side note it is possible to create your own custom yield instructions and unity provides some documentation on how to do that if your project needs a custom implementation maybe the most common yield instruction is the wait for seconds which pauses the co routine for a set number of seconds before returning to execute the code if you're concerned about garbage collection in your game and are using wait for seconds frequently and with the same amount of time you can create an instance of it in your class this is useful in particular if you've replaced some of your update functions with co-retains and that co-routine will be called frequently while the game is running another common yield statement is to return null this causes unity to wait until the next frame to continue the co-routine which is particularly useful if you want an action to take place over several frames such as a simple animation i've also used this for computationally heavy tasks that would cause a lag spike if done in one frame in those cases i simply convert the function to a co-routine then sprinkle in a few yield return at null statements to break it up over several frames an equally useful but i think often forgotten yield statement is break which simply ends at the execution of a co-routine much like the return command does in a traditional function the wait and till and await while yields instructions are similar in function and they will pause the co routine until a delegate evaluates as true or while a delegate is true this could be used to wait a given number of frames wait until a player's score equals a particular value or maybe show some dialogue when a player has died three times the instruction wait for end of frame is a great way to ensure that the rest of the game code for that frame has completed as well as cameras and ui having been rendered since it's often hard or impossible to control what code executes before other code this can be very useful if you need specific code to run after other code is complete wait for fixed update well that's pretty self-explanatory and waits for fixed update to be called by unity now the unity documentation doesn't specify if this is triggered before after or somewhere in between when fixed update functions are getting called the last yield instruction is wait for seconds real time this is very similar to wait for seconds but as the name suggests it is done in real time and is not affected by the scaling of time whereas wait for seconds is affected by scaled time so before we finish this up a few last thoughts and comments on co routines now again many people when they get started with unity and co-routines think that co-routines are multi-threaded but they aren't clear routines are a simple way to multi-task but all the work is still done on the main thread multi-threading in unity is possible with acing functions or manually managing threads but those are complex approaches multitasking with code routines means the thread can bounce back and forth between tasks before those tasks are complete but it can't truly do more than one task at any given time to illustrate this idea i've stolen this diagram from the video best practices co-routines versus async functions this is a great visual of real multi-threading on the left and what multi-tasking co-routines actually does on the right while admittedly pretty dry the video does offer some very good information and some more detailed specifics on code routines also worth noting the code teams do not support return values if you need to get a value out of a co-routine you'll need to have a class-wide variable or some other data structure to save and access those values so there you go co-routines are pretty easy and pretty useful in a lot of different situations i hope that was interesting and better yet useful for you in your project if you have questions make sure to leave them down in the comments or come on over to the discord until next time happy game designing you
Info
Channel: One Wheel Studio
Views: 7,558
Rating: undefined out of 5
Keywords: Unity3d, Indie Game, Game Development, coroutines, corountines
Id: t4m8pmahbzY
Channel Id: undefined
Length: 11min 3sec (663 seconds)
Published: Wed Mar 24 2021
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.