{"id":80872,"date":"2018-09-17T22:28:52","date_gmt":"2018-09-17T22:28:52","guid":{"rendered":"https:\/\/www.red-gate.com\/simple-talk\/?p=80872"},"modified":"2021-07-29T19:44:12","modified_gmt":"2021-07-29T19:44:12","slug":"introducing-the-unity-job-system","status":"publish","type":"post","link":"https:\/\/www.red-gate.com\/simple-talk\/development\/dotnet-development\/introducing-the-unity-job-system\/","title":{"rendered":"Introducing the Unity Job System"},"content":{"rendered":"<p>Made available to everyone starting with Unity 2018.1, the C# Job System allows users to write multithreaded code that interacts well with Unity. For many Unity users, this was a big deal. Better performance is so important to many people playing video games that players will often set their game&#8217;s graphics settings to something low so that the game will run optimally. But who says you have to force players to tweak their game&#8217;s settings to get the performance they want? Why not have peak performance from the start?<\/p>\n<p>\nWith Unity&#8217;s C# jobs, this is made much easier for the developer. This is especially true if you plan to create a game that requires many objects in the game\u2019s world, with all of them doing something at the same time. Normally, this would be incredibly taxing for the machine running this game, but thanks to the newly implemented Job System, you can now more easily achieve this scenario without taking a performance hit. Unity&#8217;s C# jobs have been touched on before back when <a href=\"https:\/\/www.red-gate.com\/simple-talk\/dotnet\/c-programming\/whats-new-in-unity-2018-1\/\">Unity 2018.1 was first released<\/a>, but it only went skin deep. This time jobs will be explained in much more detail along with a tutorial showing you how to create a job that moves 3,000 cubes around in a scene. Note: you may need to adjust this number depending on the power of your computer.<\/p>\n<h2>Setting Up<\/h2>\n<p>Once you&#8217;ve started Unity, create a new project.<\/p>\n<p class=\"caption\"><img loading=\"lazy\" decoding=\"async\" width=\"1002\" height=\"133\" class=\"wp-image-80894\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2018\/09\/word-image-18.jpeg\" \/><\/p>\n<p class=\"caption\">Figure 1: Creating a new project.<\/p>\n<p>After that, name the project <em>3000Cubes. <\/em>Then set your file path of choice. You&#8217;ll also want to make sure you&#8217;re using the 3D project template. After this has been done, click <em>Create Project<\/em>.<\/p>\n<p class=\"caption\"><img loading=\"lazy\" decoding=\"async\" width=\"1002\" height=\"582\" class=\"wp-image-80895\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2018\/09\/word-image-19.jpeg\" \/><\/p>\n<p class=\"caption\">Figure 2: Setting project name, location, and template.<\/p>\n<p>Unity will then do some work, then present you with a blank project like that shown in Figure 3.<\/p>\n<p class=\"caption\"><img loading=\"lazy\" decoding=\"async\" class=\"wp-image-80896\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2018\/09\/word-image-20.jpeg\" width=\"762\" height=\"388\" \/><\/p>\n<p class=\"caption\">Figure 3: A new blank project<\/p>\n<p>Believe it or not, there are only two things that need to be done before jumping into the code. First, in the <em>Hierarchy <\/em>menu, click the <em>Create <\/em>button and select <em>Create Empty <\/em>to create a new object.<\/p>\n<p class=\"caption\"><img loading=\"lazy\" decoding=\"async\" width=\"284\" height=\"309\" class=\"wp-image-80897\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2018\/09\/word-image-21.jpeg\" \/><\/p>\n<p class=\"caption\">Figure 4: Creating a new object.<\/p>\n<p>Name this object <em>JobObject<\/em>. After that, with <em>JobObject<\/em> selected in the <em>Hierarchy<\/em>, click the <em>Add Component <\/em>button in the <em>Inspector <\/em>window. In the window that appears, scroll to the very bottom and select <em>New Script<\/em>.<\/p>\n<p class=\"caption\"><img loading=\"lazy\" decoding=\"async\" width=\"602\" height=\"519\" class=\"wp-image-80898\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2018\/09\/word-image-22.jpeg\" \/><\/p>\n<p class=\"caption\">Figure 5: Creating and adding a new script component.<\/p>\n<p>In the next window, name this script <em>CubeMovementJob, <\/em>then click <em>Create and Add<\/em>.<\/p>\n<p class=\"caption\"><img loading=\"lazy\" decoding=\"async\" width=\"602\" height=\"519\" class=\"wp-image-80899\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2018\/09\/word-image-23.jpeg\" \/><\/p>\n<p class=\"caption\">Figure 6: Naming the script and creating it.<\/p>\n<p>With that finished, <em>JobObject <\/em>should now look like what&#8217;s shown in Figure 7.<\/p>\n<p class=\"caption\"><img loading=\"lazy\" decoding=\"async\" width=\"602\" height=\"285\" class=\"wp-image-80900\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2018\/09\/word-image-24.jpeg\" \/><\/p>\n<p class=\"caption\">Figure 7: <em>JobObject <\/em>with the new script attached.<\/p>\n<p>Setup is now complete! Yes, even the process of setting up a project is made faster thanks to Unity jobs. In the <em>Inspector <\/em>window, double click the <em>Script <\/em>field in the newly added <em>Cube Movement Job <\/em>component to open up Visual Studio and create your new C# job!<\/p>\n<h2>The Code<\/h2>\n<p>This project aims to show you two things: the first is to show how to create jobs. The second is to show off how much of a boost using C# jobs can give you compared to what you might usually do. Before declaring variables and creating your first job, you will need to enter some using statements. At the top of the script, before the class declaration, add the following lines of code:<\/p>\n<pre class=\"lang:c# theme:vs2012\">using UnityEngine.Jobs;\r\nusing Unity.Collections;\r\nusing Unity.Jobs;<\/pre>\n<p><code>UnityEngine.Jobs<\/code> and <code>Unity.Jobs<\/code> are required to access and utilize the job functionality in your script. They&#8217;ll also be required for certain variables you will declare later<code>. Unity.Collections<\/code> allow you to make use of the <code>NativeArray&lt;&gt;<\/code> struct type, which will be required when working with C# jobs. Next, declare the following variables:<\/p>\n<pre class=\"lang:c# theme:vs2012\">public int count = 3000;\r\npublic float speed = 20;\r\npublic int spawnRange = 50;\r\npublic bool useJob;\r\nprivate Transform[] transforms;\r\nprivate Vector3[] targets;\r\nprivate List&lt;GameObject&gt; cubes = new List&lt;GameObject&gt;();\r\nprivate TransformAccessArray transAccArr;\r\nprivate NativeArray&lt;Vector3&gt; nativeTargets;<\/pre>\n<p>The first four public variables will be used to dictate how many cubes will be spawned, the speed at which it moves, the range that the cubes can be spawned in, and, finally, if you wish to use C# jobs or not. They have been made public so that they can be edited later from within the Unity editor in case you wish to add more cubes or increase the area they can spawn in. After these have been declared, a handful of private arrays will be created. The first two, <code>transforms<\/code> and <code>targets<\/code><em>, <\/em>are arrays that will store the <code>transform<\/code> and <code>Vector3<\/code> data of the various cubes you create.<\/p>\n<p>Next, you&#8217;ll have a new <em>List <\/em>named <em>cubes<\/em>, followed by the creation of the <code>TransformAccessArray<\/code> and a <code>NativeArray&lt;Vector3&gt;<\/code>. <code>Cubes<\/code> will simply be a list kept of all the cubes spawned and will be used later when creating the same project in non-job code. Then there&#8217;s <code>transAccArr<\/code> and <code>nativeTargets<\/code><em>. <\/em>These two arrays will store the information gathered from <code>transforms<\/code> and <code>targets<\/code> and send them to the job you shall soon create. But why can&#8217;t we use the <code>transforms<\/code> and <code>targets<\/code> arrays instead? This is because, according to Unity&#8217;s own debugger, the type of <code>Transform<\/code> and <code>Vector3<\/code> is not a value type, and jobs cannot contain any reference types. Put simply, jobs can only work with other structs, which <code>Transform<\/code> and <code>Vector3<\/code> are not.<\/p>\n<p>So now you may be wondering why you would declare those first two arrays at all? They will be needed to fill the <code>transAccArr<\/code> and <code>nativeTargets<\/code> arrays, as you cannot simply add a new item to a <code>TransformAccessArray<\/code> and <code>NativeArray<\/code>. Instead, you will fill the <code>transforms<\/code> and <code>targets<\/code> arrays then hand the data over to <code>transAccArr<\/code> and <code>NativeTargets<\/code> to put to use in the job. In addition, you&#8217;ll also want them for later in the project when you use non-job code to perform the same task.<\/p>\n<p>Quite a bit of explanation to be done here! Let&#8217;s take a break and see where your code should be now.<\/p>\n<p class=\"caption\"><img loading=\"lazy\" decoding=\"async\" width=\"493\" height=\"528\" class=\"wp-image-80901\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2018\/09\/word-image-25.jpeg\" \/><\/p>\n<p class=\"caption\">Figure 8: All using statements and variable declarations.<\/p>\n<p>Now seems like a good time to create the C# job. Underneath your variable declarations, add the following:<\/p>\n<pre class=\"lang:c# theme:vs2012\">struct MovementJob : IJobParallelForTransform\r\n{\r\n\tpublic float deltaTime;\r\n\tpublic NativeArray&lt;Vector3&gt; Targets;\r\n\tpublic float Speed;\r\n\tpublic void Execute(int i, TransformAccess transform)\r\n\t{\r\n\t\ttransform.position = Vector3.Lerp(transform.position, Targets[i], deltaTime \/ Speed);\r\n\t}\r\n}<\/pre>\n<p>Let&#8217;s break this down. For starters, all jobs are <code>structs<\/code> and must inherit from either <code>IJob<\/code><em>, <\/em><code>IJobParallelFor<\/code><em> or <\/em><code>IJobParallelForTransform<\/code>. In this case, it\u2019s inheriting from <code>IJobParallelForTransform<\/code> because you will use this job to move objects, which <code>IJobParallelForTransform<\/code> allows you to do.<\/p>\n<p>Next, a few variables are declared. The first, <code>deltaTime<\/code><em>, <\/em>will simply keep track of what is currently in <code>Time<\/code><em>.<\/em><code>deltaTime<\/code>. You can&#8217;t simply say <code>Time<\/code><em>.<\/em><code>deltaTime<\/code> in the job, so you get the value of <code>deltaTime<\/code> and store it as a <em>float <\/em>in the job. Next is a <code>NativeArray<\/code> that will store an array of <code>Vector3s<\/code> called <code>Targets<\/code>. Finally, there&#8217;s another <code>float<\/code> named <code>Speed<\/code><em>, <\/em>which will simply get the value of the public variable <code>speed<\/code>.<\/p>\n<p>Then comes the interesting part. <code>Execute<\/code> is a function all jobs are required to have. As you may have guessed, whatever is inside <code>Execute<\/code> is what the job will actually do. In this case, you have the job doing a simple task. It will get all the cube objects and have them <code>Lerp<\/code> (meaning to smoothly move from one position to the next) from one point to another at a certain speed. Within the <em>() <\/em>of the <code>Execute<\/code> function lies two parameters, an integer simply named <code>i<\/code><em>,<\/em> and a <code>TransformAccess<\/code> simply named <code>transform<\/code><em>.<\/em> The variable <code>i<\/code> will be treated much like <code>i<\/code> would if this were a <code>for loop<\/code>, and <code>transform<\/code> will contain a given object&#8217;s transform.<\/p>\n<p>At this point, the script should now look something like what&#8217;s shown in Figure 9.<\/p>\n<p class=\"caption\"><img loading=\"lazy\" decoding=\"async\" width=\"728\" height=\"501\" class=\"wp-image-80902\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2018\/09\/word-image-26.jpeg\" \/><\/p>\n<p class=\"caption\">Figure 9: Your new C# job!<\/p>\n<p>Before working on the <code>Start<\/code> and <code>Update<\/code> functions, there are two more variables to declare, and they&#8217;re both important to the job you just created. Beneath your new job and above the <code>Start<\/code> method, enter these lines:<\/p>\n<pre class=\"lang:c# theme:vs2012\">private MovementJob job;\r\nprivate JobHandle newJobHandle;<\/pre>\n<p>The first variable is pretty simple. You&#8217;re simply declaring a reference to the <code>MovementJob<\/code><em>. <\/em>After that you declare a <code>JobHandle<\/code> that you&#8217;ll just call <code>newJobHandle<\/code>. A <code>JobHandle<\/code> is almost exactly what it sounds like. It handles jobs, doing so by scheduling and completing the jobs you assign it. With everything declared and ready to roll, it&#8217;s time to work on the <code>Start<\/code> function.<\/p>\n<pre class=\"lang:c# theme:vs2012\">transforms = new Transform[count];\r\nfor (int i = 0; i &lt; count; i++)\r\n{\r\n\tGameObject obj = GameObject.CreatePrimitive(PrimitiveType.Cube);\r\n\tcubes.Add(obj);\r\n\tobj.transform.position = new Vector3(Random.Range(-spawnRange, spawnRange), Random.Range(-spawnRange, spawnRange), Random.Range(-spawnRange, spawnRange));\r\n\tobj.GetComponent&lt;MeshRenderer&gt;().material.color = Color.green;\r\n\ttransforms[i] = obj.transform;\r\n}\r\ntargets = new Vector3[transforms.Length];\r\nStartCoroutine(GenerateTargets());<\/pre>\n<p>You create the <code>Start<\/code> function by first taking the <code>transforms<\/code> array and creating a new array of <code>Transform<\/code> with <code>count<\/code> defining the number of elements in the array. <code>Count<\/code> is the variable that keeps track of how many cubes you will spawn. Speaking of which, the next part of the function has you creating a <code>for loop<\/code>. Within this <code>for loop<\/code>, you create a <code>cube<\/code>, add it to the <code>cubes<\/code> list, give it a random starting position, and give it a green color. Of course, feel free to change the color if you wish.<\/p>\n<p>After that, the <code>transforms<\/code> array gets its next value by getting the recently created cube&#8217;s transform. This process continues until every cube is spawned. Then, the <code>targets<\/code> array gets a new array of <code>Vector3<\/code> using <code>transforms<\/code><em>.<\/em><code>Length<\/code> to define the number of elements within this array. Finally, a <code>Coroutine<\/code> will be run to fill the <code>targets<\/code> array. But that <code>Coroutine<\/code> has not yet been defined, so no doubt Visual Studio will start telling you it has no idea what this is. It&#8217;s now time to create this <code>Coroutine<\/code>, but first, check to make sure your code looks like Figure 10 below.<\/p>\n<p class=\"caption\"><img loading=\"lazy\" decoding=\"async\" width=\"711\" height=\"372\" class=\"wp-image-80903\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2018\/09\/word-image-27.jpeg\" \/><\/p>\n<p class=\"caption\">Figure 10: The <em>Start <\/em>function and the final variable declarations.<\/p>\n<p>A <code>Coroutine<\/code> is created by creating an <code>IEnumerator<\/code><em>. <\/em>It then operates very similarly to a function except that it can pause execution and return control to Unity, but then carry on wherever it left off on the next frame. It is required that a yield return statement is included somewhere within the body of the <code>Coroutine<\/code><em>. <\/em>The yield return line is the point when an execution pauses and can be resumed in the following frame. Now that you know what a <code>Coroutine<\/code> is, it&#8217;s time to create one! Place the following code underneath the <code>Update<\/code> function.<\/p>\n<pre class=\"lang:c# theme:vs2012\">public IEnumerator GenerateTargets()\r\n{\r\n\tfor (int i = 0; i &lt; targets.Length; i++)\r\n\t\ttargets[i] = new Vector3(Random.Range(-spawnRange, spawnRange), Random.Range(-spawnRange, spawnRange), Random.Range(-spawnRange, spawnRange));\r\n\tyield return new WaitForSeconds(2);\r\n}<\/pre>\n<p>In this case, your <code>GenerateTargets<\/code> coroutine will simply create the cubes within the range you specify. This is among one of the simpler tasks you can do with coroutines. You can also create a typewriter effect with text and more using coroutines. Now, move on to the <code>Update<\/code> function and input this code.<\/p>\n<pre class=\"lang:c# theme:vs2012\">transAccArr = new TransformAccessArray(transforms);\r\nnativeTargets = new NativeArray&lt;Vector3&gt;(targets, Allocator.Temp);\r\nif (useJob == true)\r\n{\r\n\tjob = new MovementJob();\r\n\tjob.deltaTime = Time.deltaTime;\r\n\tjob.Targets = nativeTargets;\r\n\tjob.Speed = speed;\r\n\tnewJobHandle = job.Schedule(transAccArr);\r\n}\r\nelse\r\n{\r\n\tfor (int i = 0; i &lt; transAccArr.length; i++)\r\n\t\tcubes[i].transform.position = Vector3.Lerp(cubes[i].transform.position, targets[i], Time.deltaTime \/ speed);\r\n}<\/pre>\n<p>Your <code>Update<\/code> function will do one of two things depending on the value of the <code>useJob<\/code> <code>boolean<\/code>. If you set it to true, then your project will utilize the job you created to move the various cubes around the scene. When using jobs, you create a new instance of <code>MovementJob<\/code> and assign the different variables in the job. Next, you utilize the <code>JobHandle<\/code> called <code>newJobHandle<\/code> to schedule the job you created. When scheduling the job, you give <code>transAccArr<\/code> as the <code>TransformAccessArray<\/code> that the job will use in its <code>Execute<\/code> function.<\/p>\n<p>If you set <code>useJob<\/code> to false, then the program will instead accomplish the same task without using the C# job system. You&#8217;ll see later that, especially with many objects in the scene at once, that using jobs can greatly improve your project&#8217;s performance at runtime. Once you&#8217;re finished, the <code>Update<\/code> function and <code>GenerateTargets<\/code> coroutine should look similar to the figure below.<\/p>\n<p class=\"caption\"><img loading=\"lazy\" decoding=\"async\" width=\"1029\" height=\"456\" class=\"wp-image-80904\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2018\/09\/word-image-28.jpeg\" \/><\/p>\n<p class=\"caption\">Figure 11: The <em>Update <\/em>function and <em>GenerateTargets <\/em>coroutine.<\/p>\n<p>There is still one last task to complete before you can test out the project. Between the <code>Update<\/code> function and <code>GenerateTargets<\/code> coroutine, create a new function called <code>LateUpdate<\/code> and give it the following code.<\/p>\n<pre class=\"lang:c# theme:vs2012\">private void LateUpdate()\r\n{\r\n\tnewJobHandle.Complete();\r\n\ttransAccArr.Dispose();\r\n\tnativeTargets.Dispose();\r\n}<\/pre>\n<p>There&#8217;s not much to this code, but it&#8217;s important to include this function to properly finish jobs and prevent memory leaks. <code>LateUpdate<\/code> is called whenever all <code>Update<\/code> functions have been called. It can be useful to order script execution. Some examples of where <code>LateUpdate<\/code> can be used include moving a camera or, in your case, disposing native collections. There&#8217;s also the act of calling <code>newJobHandle's<\/code> <code>Complete<\/code> function. This function simply ensures that the job has been completed before moving on to another job you may give Unity. Once you&#8217;ve added this code, your script should look like this:<\/p>\n<p class=\"caption\"><img loading=\"lazy\" decoding=\"async\" width=\"1029\" height=\"557\" class=\"wp-image-80905\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2018\/09\/word-image-29.jpeg\" \/><\/p>\n<p class=\"caption\">Figure 12: Script with <em>LateUpdate <\/em>added.<\/p>\n<p>The time has now come to finish this project. Save your code and return to the Unity editor.<\/p>\n<h2>Finishing the Project<\/h2>\n<p>Much like the setup, finishing the project has very little to it. Select <em>jobObject <\/em>in the <em>Hierarchy <\/em>window, then navigate to the <em>Inspector <\/em>window and check out the <em>Cube Movement Job <\/em>script component. All the variables shown dictate the number of cubes spawned, how quickly they move, and the range that they can spawn in. There is also a checkbox that toggles your <em>useJob <\/em>boolean to true or false. For the moment, leave this <code>boolean<\/code> as false (unchecked). The rest of the variables can be left at their default values if you wish, but the example will assume you kept the cube count at 3,000.<\/p>\n<p class=\"caption\"><img loading=\"lazy\" decoding=\"async\" width=\"572\" height=\"152\" class=\"wp-image-80906\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2018\/09\/word-image-30.jpeg\" \/><\/p>\n<p class=\"caption\">Figure 13: The complete <em>CubeMovementJob <\/em>script component.<\/p>\n<p>Before playing the project, it would be helpful to open the <em>Profiler <\/em>window to view the performance of your project. To do this, click <em>Window-&gt;Analysis-&gt;Profiler <\/em>or simply press Ctrl + 7. Place the profiler anywhere you wish on your screen.<\/p>\n<p class=\"caption\"><img loading=\"lazy\" decoding=\"async\" width=\"603\" height=\"455\" class=\"wp-image-80907\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2018\/09\/word-image-31.jpeg\" \/><\/p>\n<p class=\"caption\">Figure 14: Opening the <em>Profiler <\/em>window.<\/p>\n<p>After you&#8217;ve pulled up the <em>Profiler <\/em>window, save your project. If your computer finds itself unable to handle 3,000 cubes, it could lead to Unity crashing. Saving the project will, therefore, prevent any time and effort being lost. Should Unity crash, lower the number of cubes created. Begin the project by clicking the <em>Play <\/em>button at the top of the editor.<\/p>\n<p class=\"caption\"><img loading=\"lazy\" decoding=\"async\" width=\"314\" height=\"100\" class=\"wp-image-80908\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2018\/09\/word-image-32.jpeg\" \/><\/p>\n<p class=\"caption\">Figure 15: Starting the project.<\/p>\n<p>While the project runs, click anywhere in the top part of the <em>Profiler <\/em>window to view more information about how much time it takes to do specific tasks. The blue area in the <em>Profiler <\/em>window represents how much CPU usage is going towards performing the tasks in your script.<\/p>\n<p class=\"caption\"><img loading=\"lazy\" decoding=\"async\" width=\"1100\" height=\"750\" class=\"wp-image-80909\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2018\/09\/word-image-33.jpeg\" \/><\/p>\n<p class=\"caption\">Figure 16: Unity Profiler when not using jobs. Time to finish script functions is 4.2 ms.<\/p>\n<p>Remember, you should have it set up where you are currently not using jobs. Now, either pause the project or stop it to go back and set the <em>useJob <\/em>boolean to true, then run your program again to see the difference.<\/p>\n<p class=\"caption\"><img loading=\"lazy\" decoding=\"async\" width=\"1100\" height=\"750\" class=\"wp-image-80910\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2018\/09\/word-image-34.jpeg\" \/><\/p>\n<p class=\"caption\">Figure 17: Unity Profiler while using jobs. Time to finish script functions is now 2.89 ms.<\/p>\n<p>Notice how when using jobs, the amount of time the CPU takes to complete the task is cut almost in half. The difference is even more noticeable when increasing the number of cubes to spawn. On my computer, when increasing the number of cubes to 10,000 and not using jobs, the process could take 18 ms. Utilizing jobs in the same set of circumstances brought that time down to 13 ms. Of course, how much of a performance improvement one sees can depend on their individual CPU and what its capabilities are. Regardless, there&#8217;s no denying the improved performance that came once C# jobs entered the picture.<\/p>\n<p class=\"caption\"><img loading=\"lazy\" decoding=\"async\" width=\"845\" height=\"383\" class=\"wp-image-80911\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2018\/09\/word-image-35.jpeg\" \/><\/p>\n<p class=\"caption\">Figure 18: The finished project in action.<\/p>\n<h2>Conclusion<\/h2>\n<p>Multi-threaded code offers better performance to the developer though can be difficult to write. Thanks to Unity Technologies&#8217; latest offerings, creating multi-threaded code is more easily achievable for the developer. Though situations demanding the C# job system may vary, the performance boost it can bring is immense. Another new tech for Unity, the Entity Component System, can also be utilized to further increase performance. \u2019Performance by default\u2019 is the tagline for Unity 2018, and it&#8217;s easy to see why.<\/p>\n<p>Some examples of where the job system can be put to great use include battle simulators, ocean simulators, and more. This example shows the job system moving around objects in a scene but can also be used to deform meshes and other tasks. Many tasks with a heavy load on your CPU can be lightened thanks to C# jobs, and it all starts by simply creating a struct and inheriting from a job interface.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Performance is important to video game players. If a game is slow, the player may experience unintended difficulties in timing, positioning, and much more. Unity added a job system with the 2018.1 update which allows multithreaded code which can improve performance. In this article, Lance Talbert walks you through using the new job system.&hellip;<\/p>\n","protected":false},"author":317499,"featured_media":0,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[143538,53],"tags":[],"coauthors":[52549],"class_list":["post-80872","post","type-post","status-publish","format-standard","hentry","category-dotnet-development","category-featured"],"acf":[],"_links":{"self":[{"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/posts\/80872","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/users\/317499"}],"replies":[{"embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/comments?post=80872"}],"version-history":[{"count":4,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/posts\/80872\/revisions"}],"predecessor-version":[{"id":80892,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/posts\/80872\/revisions\/80892"}],"wp:attachment":[{"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/media?parent=80872"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/categories?post=80872"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/tags?post=80872"},{"taxonomy":"author","embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/coauthors?post=80872"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}