Watch this if you've never tried JOBS in Unity (Tutorial)

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
hey guys welcome my last few tutorials have been focused on performance so to keep that Trend going this video is going to show you how to use the jobs system in unity basically it lets you easily write multi-threaded code for your game and you can use the jobs system whether you have an ECS project or not which is awesome if you are new to the jobs system then this video is perfect for you ready let's go so in my scene I've got this enemy spawner that will spawn 15,000 enemies into the scene when we hit play and you can see they're looking at us every frame and they like to keep their distance from us as well and we're running this around 24 frames per second right now which is not great here's the enemy we're spawning in they have an enemy Behavior script on them meaning each individual enemy is controlling their own behavior there's really nothing fancy going on in here in update we're just calculating and applying their rotation and moving their position if it's below the threshold and we'll go through this code in detail in just a minute now the thing is we can improve this even without jobs right now since model behaviors come with some overhead having 15,000 and objects control their own movement Behavior might not make the most sense so I'm going to control their movement inside this enemy spawner script here and that's going to be really easy to do since I'm adding them into a list as they're being spawned in so now we have this nice list that we can Loop over so in update for all the enemies first we need to calculate their looking Direction which is towards us which is just the normalized distance between them and the player then we can turn this this into an angle using Aton 2 which returns radians so this will turn it into degrees and I'm subtracting 90 just to account for them by default pointing up instead of pointing right then we can use querian do angle axis to turn that angle into a look Direction based on Vector 3. forward which is just shorthand for 0 on the X 0 on the Y and one on the Z then apply that rotation okay and then we'll adjust their position to move along the negative Direction multiplied by time. Delta time and move speed but we're only going to do that if the distance between the player and the enemy is less than or equal to a threshold distance okay so we were at about 24 frames per second with 15,000 monob behaviors and now we're around 27 frames per second with one monob Behavior controlling 15,000 game objects which is still not great so now let's use jobs to improve this even further so what jobs will actually do is allow you to make use of multi-threading in your game basically default C code that you write in unity runs on one thread called the main thread but if you have a modern PC then your CPU has multiple threads and the other threads that are not the main thread are called worker threads jobs allows you to use both the main thread and the worker threads and as complicated as that sounds it's actually really easy to do all things considered you don't need to worry about which thread to use or anything like that it's all managed for you but it does require some setup on our parts so let's see what is involved in that modern versions of unity will have burst installed by default since it has dependencies in the engine now we also need mathematics because burst has an easier time with the mathematics Library than the normal math F Library I'm going to get rid of this here because we'll need to rewrite it using the other math library to make it compatible with burst now for our purposes here we need two name spaces unity. jobs which is always required for jobs and for whatever reason the interface that I need is inside Unity engine. jobs and I have no idea why there are two libraries but anyways we need to create a struct you can do this in the class outside the class in its own script whatever helps you stay organized the best my preference is in the same script but outside the class so I'm going to do that and we need to inherit from an interface we have a few options here I job is if you want to schedule a single job and you'll probably want to use that when your task does not involve iterating over a collection or a list of objects I job parallel 4 is when we do want to iterate over a collection or list and perform a task on each of those which I could use here but actually I want to directly change the transform of each enemy in this job and using I job parallel for transform as the name suggests makes that nice and easy to do so we need to implement the interface member and that'll give us this execute method with an index if you need it and a transform access so this represents the position rotation and scale of an object now I'm I'm going to add the burst compile attribute up here as well as at the top of the class burst translates your C or net code into a highly optimized native code but it's strict and doesn't support all the types that normal C does so we're going to need to do our math using the mathematics Library which is actually really not that bad it has almost all the same functions that math F has so it's going to feel pretty familiar to use we're also going to need the collections Library so first let's set up a couple of variables our player's position our Delta time distance threshold and move speed we're going to pass in all these values when we actually create a job in our update function and I'm using the readon attribute because by default native containers are read and write which means you cannot schedule two jobs referencing the same containers at the same time but by using this attribute it'll allow two jobs to run in parallel reading data from the same container so first we want to calculate the direction so we're returning a float 3 instead of a vector 3 but this should look familiar we're normalizing the player's position minus the enemy transform. position you'll notice our transform is referring to this transform access not the usual transform you can't access transform here like in a normal behavior there's no way to use normal reference variables but this one has some magic behind the scenes so that's why we are using this calculating the angle and the look rotation should be extremely familiar we can't use Vector 3. forward but this means the same thing and then we say the enemy transform. rotation is equal to the look rotation so that handled getting the enemy to face the player now let's get him to move away from the player so we're going to calculate the distance between the player and the enemy now interestingly normally I'd say we change the position if our distance is less than the threshold but my research showed that burst prefers a different way so we'll calculate our new position and then use a masked position with math. select which is cool you pass in the enemy position it's new position and then an if statement and it's going to decide whether or not it needs to update same as our if statement from before and then adjust the position so that's our job struct now in update I don't want that to run until all of our enemies have spawned in this is just for this example because I'm also using the script to spawn in the 15,000 enemies so we're going to get a new control enemy job and pass in all those public variables from our job struct so what's our player position it's equal to our player transform. position our Delta time is our time. Delta time distance threshold is distance threshold and move speed is move speed now before we can actually schedule this job we have one more little thing to do because remember this is meant for a for Loop so it knows we're doing this for more than one thing so we need an array of enemy transform access so that our code knows what to actually apply this data to so I'll create a new transform access array and an array of transforms the enemy transforms will be the same as the number of enemies to spawn and we'll assign each transform to each enemy's transform as we spawn them in then when that's done we can say our transform access array is equal to the enemy transforms array basically we're just passing in that array of transforms into the transform access array now down here we can just create a new job handle and say job. schedule and pass in that access array so it knows what to apply the transform to and then job handle. complete so that it waits for the current job to complete before continuing the update and that's literally it no ECS no entities just a struct and a little bit of different math and we've nearly doubled our frame rate because now we're getting about 47 frames per second now there's two more things I want to show you but I can show you both of them at the same time so I made another example as a test I've commented out all the code in enemy spawner because we're going to control the enemies individually again but try using a job for that instead so here's my new enemy behavior for that you'll notice we're just using I job here since it's just one enemy we're working with no iterating is needed but that means we don't have a transform access so I created a position and rotation native array here in our struct and so we'll need another for our class and set them equal to our position and rotation and then just set those to transform.position and transform. rotation when we pass in the references to the job struct now this by itself won't actually move or rotate the enemies this is just a reference we need to go one step further and actually apply this to our transform.position and transform. rotation and we can access data from the job very easily by just referencing the job so our transform.position is equal to our job. position and same with the rotation okay so you can still modify transforms without transform access you just have to do it a little bit differently and also we can get outputs from our job by just referencing our job and then grabbing the variable and also if we play we're getting like seven frames per second now which is way worse than the regular mono behavior that we did at the beginning so I wanted to show you this to prove that there is an overhead with jobs and if you are scheduling 15,000 jobs it's a lot so in a case like this iterating is definitely a better use case for jobs than just regular one-off jobs that you're scheduling 15,000 times so jobs come with an overhead they are not meant to be used everywhere for every single situation they're really meant to be used for heavy computations where you think that Distributing the workload among multiple threads could be faster than just doing everything on one thread just a little bit of different code for huge performance gains when used in the right situation which is so so cool that's all I got guys thanks for watching bye
Info
Channel: Sasquatch B Studios
Views: 4,729
Rating: undefined out of 5
Keywords: unity, unity2d, unity tutorial, sasquatch b, game development, unity tips and tricks, unity tips for beginners, unity game architecture, unity jobs, unity jobs burst, unity jobs tutorial, unity jobs system, unity jobs system tutorial, unity multithreading tutorial, unity ijob, unity ijobparallelfor, unity ijobparallelfortransform, unity performance, unity optimization, gamdev tutorial, game development for beginners, unity jobs for beginners
Id: 6gFyoMoa8dM
Channel Id: undefined
Length: 10min 31sec (631 seconds)
Published: Thu May 16 2024
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.