Unity DOTS 1.0 in 60 MINUTES! [CHECK PINNED COMMENT 2024]

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
hello and welcome I'm your code monkey and in this video I'm going to do a nice simple project to check out the current state of unity dots version 1.0 is finally here although still technically in preview it's going to be fully released production ready in the 22 LTS stream which will happen sometime early next year but since version 1.0 is now here and leaving experimental very soon that means the API probably won't change too much until the final production Reddit release so while you probably should not be using this in production just yet it isn't an advanced enough state that you can absolutely start learning it if you're watching this video sometime in the future most of it should still be up to date so here on let's learn the basics of how ECS 1.0 works the great news is that if you've played around with ECS just like I did two years ago then nowadays everything is a lot simpler with a lot better tools now I initially wanted this video to be just a pretty quick overview something about 15 minutes long but I found it would be a bit of a disservice to you to simplify things so much just to fit a short length so it would end up being quite a lot more more detail than quite a bit longer than I anticipated if you find the video helpful and you want to help me out you can wish list my game on scene it's called total war on Liberation features lots of interconnect systems and I'll probably be using dots in the funnel game or you can get my complete step-by-step courses like my ultimate TNT overview or my turn-based strategy course which is actually the base for my steam game if you know absolutely nothing about dots you can go watch my quick explainer on the theory behind it that video is still up to date in terms of explaining all the Core Concepts from a high level view as a quick refresher the entities package or ECS stands for entities components and systems entities are basically just an identifier components are where you store the data and finally systems do whatever work you want on that data so here let's build a super simple demo we're going to set things up to have a simple character just spawn and randomly move around doing that involves learning about quite a lot of parts of ECS so we need to create the entity and look into the conversion workflow create a component to start the entire position use the baking system create a system to generate a random position and move it I will also cover how to mix ECS and game objects so this is an excellent simple demo to showcase the Epsilon basics of how ECS works and of course in the end I will showcase the performance comparison between ECS and regular Model Behavior code as well as give you some resources if you want to dive deeper into dots alright so let's begin first we need to install the packages so let's go into the package manager and up here let's click on the plus and let's add a package by name and for name let's go with com.unity DOT entities let's add this one and yep there you go it didn't sound over here the entities package as well as all of the required dependencies so we've got burst we've got mathematics relation and so on now another great package we can add is the graphics package that's what used to be called the hybrid renderer so it's going to add factor by name com.unity.entaties.graphics let's add this one okay so with this we have all the packages we need also important is what it says over here on the documentation the way this version of ECS works is heavily based on code generation so you should be using an ID that supports it like for example Visual Studio 2022 which is what I'm using and I also recommend that you modify the domain reload settings this helps the editor be quite a lot faster by not reloading the current domain to change that just go up here into edit then into project settings then on the side let's go into editor and over here let's scroll down until we find over here the interplay mode settings make sure that this one is enabled and make sure these two are unticked so just like this however like it says here and be very wary that using this option means that your static Fields will not automatically reset so keep that in mind you can read the docs to be more familiar with what exactly this option does okay so now let's begin making a simple entity and if you like me use dots two years ago then you're going to be very pleased with how easy it is nowadays how you do it is super simple let's go over into the hierarchy let's right click and let's create a brand new subscene so let's create an empty scene then over here just give it a name like for example entities subscene and if there it is we have a subscene and this little toggle here and lets you enable or disable the subscene and then over here A bunch of options for opening or closing the subscene but right now let's leave it open basically what this does is anything inside the subscene will automatically be converted from game objects into entities so let's add a very simple new game object in that subscene so let's right click on top of it and over here game object let's create just an empty game object okay that's it just a super normal regular game object with this if we hit play we can see the object is over there in the hierarchy and I often look over here on the right on the inspector right now as you can see it's normal so it's showcasing a normal game object with transform everything is perfectly normal however now if we unlock over here on the top right corner we see this little circle icon basically just like this it's currently showing the regular game object inspector and if we click on it it goes into this half mode I'm honestly not entirely sure what exactly this one does it's supposed to be kind of a mixed mode but it doesn't seem to change anything at all and if we click again there's actually a slight bug where sometimes this one does not update but we can easily fix it let's just select another random game object and and then back to selecting that game object and now if we click go into mix click again and yep now this showcases The Entity inspector with this we can see that the game object that we created was automatically converted into an entity so yep that's awesome as you can see this is how easy it is we just created an empty game object a normal game object in the subscene and it automatically converted into an entity with all the required default components like for example over here the transform now let's do something even more special let's add a simple visual so on the game object let's right click on top of it and inside let's create a new 3D object let's make a simple caption let's just lift it up a bit off the ground so something like this and just give it a nice fun visual all right there it is again note how we're using regular game object components so we've got a regular game object with a regular transform a mesh filter and mesh render just like you have in any other game object then by default it comes with a Capstone collider let's just remove this we're not using physics in this demo okay so just with this let's hit on play any of the objects is there now there's also another entities hierarchy so we can go over here into window and go down into entities and let's open up the entities hierarchy and it opens up this window which again also has this nice Circle button so over here we are seeing all of the game objects and we click on it it brings the mix mode click again and now we see all of the entities note how the icons here so this little icon the regular Cube that means a game object whereas this one this hexagon means an entity if we click on this and over here I'm looking the inspector make sure you are in the entities inspector mode and yep we can see this game object and the channel was indeed fully converted into entities so this game object which is apparent as you can see has a child component and inside has a reference to the child again this is an entity reference not a game object reference and if we select the Capstone visual yep over here we see that it has all the components required to actually render an entity okay awesome so it literally is that simple to make an entity and add a visual to it you just make a new subscene a game object inside it optionally add a visual and that that's really it everything is automatically converted this process is much much simpler than the previous method which involved tons of product code to get anything rendered on screen so now that we have our very simple entity the next thing we want to do is just store some data so we do that with components let's make a brand new script so in my scripts folder just right click and create a regular new c-sharp script let's call it just speed and open now here on this script for making a component first of all it needs to be a struct not a class so this one is going to be a construct and secondly it does not extend mono Behavior instead it needs to implement I component data as soon as you can see exists inside of the unity.inties namespace so we'll import that as well alright so that's it this is our very basic component now over here we can add whatever fields we want so for example let's add just a public flow just call it value okay that's it super simple now we want to add this component to our entity and the way we do that in version 1.0 is through the baking system which is a conversion that we just saw since this component does not extend amount of behavior we cannot add it directly to our game object so we need to make a separate amount of behavior which will then be used to convert and create into this speed component first over here let's create a new c-sharp script come with speed authoring by the way adding the authoring suffix is not required but it is a nice convention to follow so let's open this here for this one we are indeed going to leave it as a mono Behavior and then we're just going to essentially add the exact same Fields so let's add a public float value however just like this it won't work just yet the system does not know that this mono Behavior belongs to this speed component right now it doesn't know that connection is supposed to exist so how we make that connection is by making a baker now you can make it in a separate script or we can just add it over here right next to the amount of behavior since these are technically connected it makes sense to place it right here so let's make a public a class let's call it the speed Baker and we're going to extend Baker of type and let's include the type speed authoring and Baker as you can see is inside using unity.entries okay so with this we need to implement the abstract method so let's Implement that one so here it is the bake function and from here we can easily add add components and let's add the component that relates to our authoring component so let's create a new speed the speed component and let's construct it with the value and grab the value from the authoring dot value okay so just like this again we have our regular component so this one has no amount of behavior it's not a class then we have the speed authoring this is mono behavior that we attach to our object and then we've got a baker which essentially grabs a reference over here of our speed authoring component and simply grabs that in order to access the value in order to set the value on the speed component now this is the one part of the system that still feels a bit too needlessly complex there should be some way of automating this if the mono behavior is meant to have the exact same Fields as over here the component from what I read in the forms they are actively working on simplifying this process apparently it just didn't make it onto the 1.0 version okay so with this done if we go back in the editor over here let's select our regular game object and let's attach the speed authoring component and over here we can add whatever value we want now if we hit on playing and let's go into our inspector into entities mode and if we scroll down yep over here we do see our component Tower speed component and the value yep it does have the value that we set all right awesome okay so far so good next let's make a system to move our object and when it comes to systems there are two ways you can make them you can make a class that extends system base or you can make a struct that implements I system essentially system base is meant for managed systems and I system is meant for unmanage so that means that system base cannot use the burst compiler whereas I system can if you're doing some super basic code where you want it to run just on the main thread and maximum performance is not necessary then you can use system base but if you want the absolute Max performance and you should be using I system don't worry I'll showcase both methods for Summers let's use the system base so let's create a brand new c-sharp script and for this one let's call it the moving system base now inside instead of a mono behavior let's extend system base inside unity.inthes okay and on this one we need to implement the abstract function the on update so let's do that now just like this if we save and we go back in the editor yep over here we do see a nice error it is telling us that everything that extends system based must be defined with the partial keyword again this is due to how dots 1.0 handles a lot of things based on code generation so we need to make this partial so up here let's make this a public partial class okay just like this we don't have any errors great now here our goal is to move our entity and for that we're going to want to modify the entity position and there are two ways we can do that let's look at the entity as it exists by default also by the way you don't need to head on plane in order to see what entity is converted into if you're just like this with the game not playing you can still go over here in the inspector and swap it out for empties mode again we got this bug so we can just swap out the game object select that one and then click again and yeah there you go we do see the conversion so if you just want to see what this game object will be converted into you don't need to go into play mode you can see it directly over here so as I was saying we want to move and to do that there are many ways we can do that and looking at the entity by default we can see it has a local to World transform so one way to move into would be to access this component and modify over here the position that would work but modifying this position directly sometimes can cause some issues if the object is a child of something else so it requires a bit of validation a bit of external logic rather than just setting this directly and to solve this problem that is where a Brand New Concept in empties 1.0 comes in that is the concept of the aspect if you look over here in the inspector up top we do see a bunch of buttons so we see the components it was we see and then we have the aspects by default we can see that it does have a transform aspect holding the low composition low convertation and the uniform scale so this one is essentially the same as the regular transform what an aspect basically is is just a way to group together several components in a nice logical manner so this transform aspect owns all the logic related to a regular transform so the position rotation scale as well as a bunch of functions for working with those we can actually inspect this built-in aspect to see what it does so in the project let's search for transform aspect and make sure that we are searching over here in packages and let's open up the transform aspect and in here we see what it does it implements the aspect interface which again we're going to cover this in a little bit and then importantly it has a bunch of helpful properties as well as functions in order to interact with this object for example we have a nice function here to translate The Entity it just receives a photo 3 and then does the logic based on whether it's apparent or not so again aspects are basically a nice layer of abstraction so you can combine multiple components and do some custom logic which then allows your code to be much more simplified when you're doing regular things working directly with the aspect instead of the components don't worry if aspects seem a bit confusing now it's only going to become quite a bit more clear once we make our own so going back into our Moving System base over here I'm going let's find all of the objects with that aspect and let's move them and actually over here there are mainly two messes we can use one of those is what is called the entities dot for inch if you've used dots in the past and you're probably familiar with this method but nowadays in NTS 1.0 there is another method which is the one that you should be using it is called the idiomatic four inch so you do a four inch just like you do in any other C sharp code so just a regular 4 inch then the type so for now we want to move so let's use the type for the transform aspect transform aspect which again is over here inside using unity.transforms so we do a four inch in and then we do a query so let's access the system API and let's do a query and we're going to query for the type that we want so in this case the transform aspect all right so here it is as you can see super simple just say regular for each like you've always done in C sharp now with this the code that we write here will run on every update for every single entity that has a transform aspect so for now let's just move it to the right so let's just access the transform aspect and into the position and let's increase the position so we're taking a full three which by the way if you don't know this is essentially a vector 3 it's just made with the unity mathematics Library which is meant to be more performant so let's use this and let's actually use our using up here so here using this and we're going to construct a new Fallout 3 and we want to move to the right so let's do just one zero zero although again this is going to run on every update so in order for movement to be frame rate independent we really should be using time dot Delta time so instead of being just one let's use time dot of time although this time is not the one that you should be using when working in ECS you should be using system API dot time dot of time okay so that's brilliant with this every single entity that we have that has a transform aspect should be moving to the right at a speed of one unit per second also here I should point out just like we saw the transform aspects function so if you don't want to add directly to the position over here you could just use translate translate in the world then pass in the same Vector tree so again many options just like you have with regular transforms okay so this is all you need to do in order to move an entity and just for clarity's sake let me quickly show you how the entities for each works so here are both methods basically they are extremely similar with the entities for each instead you have a Lambda instead of doing a regular four inch so you have there then over here you put whatever components you want just like over here we're doing a query and the one big difference is how in entities for each at the end over here we need to call either schedule or schedule on parallel or we call run basically the entities dot for each this one returns a job so then that job needs to be either scheduled or run and what these three options mean is that basically run this one runs a code just on the main thread this one runs the code in a single worker thread or alternatively schedule in parallel this one runs a code on multiple worker threads whereas over here when using the arithmetic four inch over here you don't have an option everything over here is going to be run on the main thread so it's exactly the same as doing run that is why I previously mentioned that when using system base and the ID manifold inch you should only do this for more simple use cases if you want absolute maximum performance and you should be using I system along with some proper jobs that can be paralyzed which again I'm going to cover in a little bit now one more difference is that over here the idiomatic range this one can have multiple nested Cycles so you can do this for example this is a valid necess cycle whereas with nt4 inch you cannot Nest them okay so like I said those are the differences but now as of entities 1.0 you should be using the idiomatic four inch so let's get rid of the empties for inch so just with this code Let's test and if there it is the entity moving all right awesome we can even select it and again lock in the entities inspector and you look at that the 11 position constantly increasing on every second also another thing we can look at is go into window then go into entities and down here and look into the systems window and it opens up this nice window let's just anchor it down here and we can view all of the various systems and how they are organized in various groups and if we unlock by default if we look over here in update in the simulation system group if you look here we do see our Moving System base that we just created and yep we can see that it is currently running okay so now instead of moving at this speed let's use the speed data from our components let's just use a proper speeds over here let's say put it a bit faster but just on two okay now how we use our speed is super simple we just add over here yet another field so first on our query let's add the speed component and then over here let's put this inside parentheses so we can add multiple and let's say the transform aspect and then our speed component so here when moving we can simply use it so our Delta time multiplied by speed and we grab the value also one more thing which is up here in our query we can Define if this is going to be a read-only or a read write component so we can add the ref R doubling so this one are the only stands for read ranked but in this case we're just going to read the spin so we can use refro which means reference read only so let's use this and also modify the value up here so this one is the refro for our speed and then we access speed we access the value Ro and then access the value okay so let's see if it's moving twice as fast any update it is and by the way over here you can also modify it in runtime So currently moving at this speed if I put this on three if there go moving quite a bit put it on point one there we go moving really small so it's one of the benefits of ECS is you can easily edit any values at runtime okay so the movement is working now let's make another component to Define our Target position so in our project let's make another c-sharp script let's call this the target position and over here this one is going to be a component so once again let's make this a struct and we're going to implement I component data then over here let's just add a photo 3 for our value okay now let's quickly make the authoring component then the baker so over here and let's create new c-sharp script for the Target position authoring over here this one is a monobehavioral let's add a public k43 for the value and then we've got another one for the Target position Baker and we extend bigger of type Target position authoring and then over here we simply Implement so let's first of all add using entities and let's implement the abstract class and over here we're simply going to call our add component and let's add new component of type Target position and in some let's add the value to authoring dot Valley okay that's it super simple now here in the editor let's add the target position authoring let's also put it on a regular speed and for them let's make it move up so let's put it just on a z F5 now back into our Moving System base over here let's add our brand new component so here we've got our components in our query so for the query let's add another one so let's add in this case we're actually going to be updating our Target position so let's make this an frw and let's add the target position and then up here let's add a ref R doubling of our Target position so here it is some simple movement code so we've got our Target position we subtract our current position in order to kind of like direction we use math our thermalized to normalize that vector and then as usual we just move along this Direction with time dot of time and using our speed so let's see if it is now going towards the target position and if it is indeed going up instead of going to the right so let's put it to speed a bit faster and let's modify over here Target position let's move it a little bit to the right and yep there we go does move a bit backwards and if there you go it doesn't work okay awesome so all of our logic isn't it working but as we can see over here we're getting quite a lot of components over here in our system so with this this is the perfect example to showcase the benefits of making our own aspect like I said the main purpose of aspects is to group components into one logical unit over here we are grabbing three components and we're using all three in order to make our movement logic so let's refactor this code with a nice aspect to help make this code quite a bit more clear first in the editor let's add an easy sharp script so a new one let's call it the move to position aspect then over here the first thing that we're going to do is instead of mono behavioral we're going to extend I aspect which is inside Unity dot entities in order for it to be an aspect it needs to be a struct then we see a nice error telling us that an aspect must be struck and also must have the partial keyword so let's add that finally this one must also be marked as read only so read only okay so that's our basic aspect definition it needs to be a read-only partial struct I need to implement I aspect now over here we can add whatever components we want it can be regular components or they can also be other aspects so for example let's begin by adding our transform aspect so just a regular transform aspect and all of these films must also be read only then we can add components so in our system we were using the speed components so let's add that as well except in certain aspect we cannot use the component directly we need to use one of these so it needs to be a read write read-only or enabled read write enable read-rolling so we're here for the speed like we saw we're only reading so let's add a ref r o of this the other one we were using was the target position so let's use a private read only and for that one we were writing so ref RW of our Target position finally in our aspect we can optionally add an entity films so over here we can add a private read only entity for our entity this one is optional you don't need to add this but if you do it won't be automatically filmed with whatever entity is currently running this aspect one important thing is you can only have one so if you have multiple entities you get an error so you can only have one field of type entity okay so here we have our nice aspect and now if we go back into our Moving System base over here instead of doing a query for all of these components instead of that we can simplify this and just use our nice aspect so just like this our move to position aspect so it is you can see the code is much much simpler and now if we want we could expose all of the same components from the aspect or since the home point of the aspect is to group together logic then we can just make some functions there so let's copy this code let's go into our aspect and over here let's just make a public void move and in here let's face the exact same code so you do the same thing we access the target position calculate Direction and move our transform aspect then back into our Moving System base over here instead of doing all this we just go into the aspect and we call our move function also one important note is over here once inside I aspect over here we cannot use the system API directly if we try running the code like this we see this error which is not very harmful just invalid operation but real in this case what that means that we cannot use the system API to get the Delta time whilst inside the aspect so instead let's modify this code and let's receive over here a foil for the Delta time and then we just use that one okay just like this so we received that as a parameter and on the moving system base over here we can indeed use system API dot time.time okay let's test and yep now it does work perfectly okay awesome as you can see with all this our system code is extremely simple just a headphone lines of code same thing for the aspect again a really small file everything is super simple super easy to understand and same thing for all the components all of them are pre-symbal so many files but all of them are extremely simple extremely easy to follow this is one of the main benefits of ECS is how the architecture forces you to essentially write good high quality code everything is nicely decoupled nicely separated into separate files each doing just one thing all right so let's continue when we reach the Turning position we want to move to another random position for testing when we get there we can do a simple listen check so we can do a math dot distance also by the way in production code you probably should be using the distance squared since that one is a little bit faster but for this quick demo let's just use a simple distance just so our units make sense so this one just takes our two points so let's use the transform aspect dot position and let's use the target position dot value rw.position okay so if the distance is under a certain amount so if you get there then we want to generate a new random Target position let's make a nice function to generate this so probably going to return a fault 3 get random position and now usually what you would do would be something like this so a new Full three and then let's randomize and let's use the unity engine.random in order to randomize between 0f and let's say 15 enough then no random on the Y another random on the Z so this is what we would normally use but when working with dots we cannot use the random.range this is a class which would break when we eventually try to use bursts so we cannot use this random class but we can use the one inside the mathematics Library so we can use this random inside unity.mathematics however this one works differently for this one we need to initialize it we need to do something like new random which then takes in some kind of scene and then we can use this random which again note how this one is a struct so we can use this one in order to generate a next float so we can do something like this but again the way this works is this receives a seed over there on the Constructor if we were to do this then all of them would get the exact same random position because we were creating the seed and then generating exactly two numbers so if we use it just like this then this would return always the exact same random position in order to make something properly random we need to essentially store just a random instance store this somewhere and then reuse it to generate all of our random values so again back into how ECS Works how we hold some data and random is indeed a piece of data we call this in a component so let's make a component to hold this over here let's create new c-sharp script call it the random components now as usual let's do the same thing so this one is an eye component data and then in here we're just going to store a random again make sure you use the correct type so that's unity.mathematics.random and let's go with just randomly and again component is not a class but rather a struct okay so we have random component now you might think that to add this you could just make a authoring component just like we did and then essentially attach it to our entity however if you didn't and every single entity would add a random component and if you use the same seed for all of them then again you would not really have any Randomness so what we want is not one random per entity but just one random for the home world for that we can use a really nice entity Singleton so let's first of all make a baker which we're going to attach to a different object so let's create an easy sharp script called the random authoring then over here let's make the regular code although in this case we don't even need any fields alright so there it is super simple if you wanted you could input the seed over here if you want to customize the seed on the authoring component and in the final game you probably would want to use a proper scene like perhaps Acorn time but for testing a nice fixed value works great and it's actually quite helpful for debugging okay so we have our component now over here let's create an empty new game object on our empty subscene let's call this our random and over here let's attach the random authoring component okay so with this we have just one entity in the world with our random components now we just need to somehow use that over here in our random position now since we've made it a Singleton so there's only one random in the entire world since we made that we can easily access it so we can go into the system API in order to get the Singleton of type random component and we have access to it and then we can grab our random object so this is how we can grab it however again like I mentioned previously we cannot use the system API once inside an eye aspect if we run this we're going to see the same invalid error that we saw previously we can only call the system API from our actual system so instead let's receive our random as a parameter so let's receive this as our parameter here then over here in our move function we're going to have to generate so let's set this one so let's receive it as a parameter over here as well and when we reach a new position let's set the new one so we just set this one equals the get run position and let's pass in our random okay so we need to do is pass in the random to the move function so here in our system let's grab it so we can access the system API to get the Singleton of type our random component and let's access the random so this is the unity.mathematics dot random so we grab this one and then we pass it into our function okay so with this everything should be working we no longer have any errors so we pass in the random to the move function then the move function takes in the random and uses it to generate a new random position except this is not really going to work but let's test and see what happens so here we are in the unit is indeed going to the Target okay great and once it gets there generates a random position great but now as you can see it's stuck in that position we can test and see what is actually happening over here on the Move position aspect let's do a debug download to see what value is being randomly generated so let's do a debug.log on this okay let's see ideally it should always be different it should always be random so let's test so it's playing goes to the first position gets there okay generates another position gets there and nope there you go there's the issue as you can see the random is always generating the exact same position so we have something going on without random generation the issue that we have here is a very sneak issue due to how ECS works it's one thing that you always have to be very careful with since a lot of these things are structs which means they work as copies and not necessarily as references that's basically the problem that we're getting here we're grabbing the Singleton for the random component and grabbing the random except when you do this we are grabbing a copy so this one gets a copy then move this one passes in another copy so this one receives yet another copy which then does the random position so when this one calls the next one function this is calling next float on a copy ideally this would generate another Fallout and then update the current state on the original random but since we're working on a copy this one generates the next load but then it's always working on the same copy so the actual Singleton object that is never being modified this is something that you always need to be very careful with when working with dots you always got to be careful with are you working with a copy or are you actually referencing the original data the solution over here is pretty simple instead of using get Singleton let's use the get Singleton RW for read write this one returns a referred only so let's store that instead of storing the random component so let's sorry riff RW of type random component and we get just this one example like this again it's like this it's not the value R don't we no let's get exactly the refer going that's what we want then let's pass that one in so we just need to pass in this type so let's go into this one and receive the different type let's just rename this to the random component and then down here the exact same thing so then here we use the random component let's use the value RW and then let's grab the random and then we can call next float basically by doing this we are always accessing the value RW which does have an actual reference to the original data so with this it should be working and our debug download here this one should always be giving us a different random position okay so let's test so there's unit going to random position yup gets there and generates another random if gets air generates another random and so on so as you can see now it is always generating brand new random positions okay great so this was an excellent example of something that you always have to be very careful when working with dots you always have to be very careful with are you working with a copy or the actual original data okay so with this everything is working perfectly we have our unit going between random positions that's great and so far we made it using the system base but like I mentioned for maximum performance you want to be using the Isis instead so let's convert the exact same system onto an i system just to see the difference so over here let's make a new c-sharp script come with the moving eye system and on this one this one is not a mono Behavior instead let's Implement I system which is inside Unity dot entities also for this one since this one is an unmanaged system that means we need to make this a struct and not a class then we need to implement this interface which involves these three functions the uncreate on Destroy and on update this is another thing where I think they could improve the API they could make the interface have default empty implementations for the uncreated non-destroying that way you wouldn't have to implement them unless you wanted to do something complex so basically ideally it would work exactly the same as the moving system over here you can also override the on crate around destroy but you're only forced to implement the on update so any Moving System would be nice to work the exact same way only Force to implement the on update but as for right now we need to implement these but let's leave them as just empty okay so we have our own update now over here we can try putting the exact same code so let's go into the moving system base let's copy exactly this and let's paste it over here and as you can see on the error all I system must be defined with the personal keyword so let's also add that so here it is an i system with the exact same code everything running exactly the same you might notice that it's running at Double the speed that's because both systems are active right now one nice thing is on the systems window which again you can go into window entities and open up these systems on this one you can manually enable or disable systems so for example I've got the movingi system and moving system base and you can click over here to disabling system so disable the moving system base and yep now only the moving AI system is working now I can say on this one and nope nothing happens so just a fun tidbit of information this window is really very useful okay so back in the cone like I said the main benefits of using I system is that you are able to use burstable jobs so instead of running this code this for each which is only running on the main thread instead let's make a proper job that we can then burst and run on multiple threads to make a job you make a new struct so a public struct and let's call this the move job and then you implement one of the many job interface for example you have the eye drop chunk if you want to integrate over multiple chunks and you have the ijob entity which iterates over entities let's use this one now over here we just implement the public void execute method then the job also relies on code generation so over here this one also needs to be a partial instruct so we have the execute method and over here we can include whatever components or Asics that we want so basically inside of this execute function we want to run the same thing we were running on the four each so in order to run this code we need to have our move position aspect so let's add this one so let's add it right here and then we run the exact same code so just run this code this is required that we receive the random component so let's receive it as a field up here so the refrw for our random components and up here on the update instead of doing all this what we do is we construct a new job so a new move jaw we need to set our random components so let's set the random component and let's grab it so let's actually run the same as the other code to grab the field and then we pass in that exact same thing okay so this constructs the job and then afterwards we need to run it and again that's where those options that I mentioned while you'll come in so you can make run if you want to run it on the main thread but that would be kind of silly to do if we're working over here on an i system over here we wanted to make as parallelized as possible so alternatively we have schedule this one is going to run the job on a separate worker thread but only one separate worker thread and then finally we have the schedule in parallel this is going to run on multiple separate worker threads then each of those worker threads will then work on a certain number of entities so for example if you had 10 cores and 100 entities maybe each core would run 10 entities all of them running in parallel okay so with this we are creating and scheduling our job however like this we actually have an issue you might have started the issue already the issue has to do with as you can see over here we are using a refer dummy so we are reading and writing onto our random component and then we're scheduling it in parallel if we try running this and nope there's our issue basically in order to update the random component it uses unsafe pointers and if you do that then you cannot guarantee that you don't have any race conditions so you cannot run this job in parallel because multiple worker threads might be both trying to access and modify the same random component so in this case what we can do is simply split the logic for the movement logic over here we can easily run all of this in parallel so we can do this for all entities at the same time and then after all entities have moved then we can sequentially test if they reach the same position and reuse the same random component to generate another random position so let's just quickly split this code there it is so the move function only moves and this one does test if we reach the new position and if so generate a new random position now here on the system we can essentially make two jobs and the second job won't just test if it reached the target position so let's just give it a different name this one does receive the component and this one receives just the random component then this one up here does not have a random component and also over here the system API we cannot access the Delta time from inside of here so it's a public float Delta time and we're going to use this so then up here when creating a new move job let's hit the Delta time over here we can access the system API time dot Delta time then we don't have the random component so we schedule the move job to run in parallel and then we construct a brand new test reach position job and for this one we do pass in the random component and for this one we are going to run on the main thread now like this we still have some issues if we try testing we get the usual message that is really not very helpful just saying invalid operation no suitable code in this case the reason is because once creating the job over here we cannot access the system API we can access it just not inside the job initializer so we need to go up here to the final float for the Delta time system API access time.com for time and then we can indeed pass in that copy and like this it does work like you said return here just so we don't see the second issue just make sure that this part works and yep it does work it does move for our final issue like I said we want to do all of the move jobs and then we want to test if we reach the target positions so we want to make sure all of these all of these scheduled parallel of these run and complete first before running this one so the final thing we need is really just a job dependency if we look into over here all of the versions for this function one of them as you can see does return a job handle so over here we can grab the job handle and you have handles inside unity.jobs and to return a job handle we need to use the function that takes in a dependency so we can access over here the system state so the state DOT dependency that is going to be the dependencies for this job system okay so now the goal is basically you want to run and complete this job before running this one so when we can do that is just going to jump handle and count complete that is going to ensure that all of these this scheduled parallel is going to complain before we get to this point which is going to run on the main thread okay so like this it's almost working we just need one more tiny thing so let's test and there's our final issue again telling you that unsafe pointers cannot be used which are used in the random component as long as you know what you're doing and you're not messing around with multiple threads you can use this attribute to disable this safety so since here we're going to run this on Main thread and we know what we're doing we're accessing the random struct so we're not doing anything too special so over here we can indeed use the attribute in order to disable that restriction alright so like this everything should be working so we can finally go and see and yep the unit does go to the direct position gets there gets a random position and always goes from random position to random position the final thing that we need which is one of the main benefits of system is being able to use bursts over here let's add the attribute burst compile so we added over here then we also need to add it to all the functions and on the jobs themselves this one I'm not sure if you have to add this but you can also add it alright so now these jobs should be running with bursts which should be insanely fast so let's test and see any everything some works exactly the same and we can even look in the nice profiler so analysis profiler over here we see everything running insanely fast let's click on a random point so over here in this case really need to zoom in or to find it since we really just have one entity and that's how fast this is but over here we can see default World moving ICE system taking 0.02 milliseconds and if we scroll down we can find yep over here our burstable move job completing in 0.003 milliseconds and then finally appear as you can see all of those complete so that job completes and then we've got the test reach position job this one runs after and completes in 0.002 milliseconds so all of it insanely fast even if we were to compare just one entity with one game object this is still much faster okay so our eye system works here we have the two ways of doing a system so we've got the system base we get the on update and we do an idiomatic four inch and we've got the movingi system which manages to use burst in order to be much more efficient and for this one we Implement a system and you do things a bit differently by creating the actual jobs that we're going to execute so for simple and use cases where you want to run some code on Main thread you can use the very simple system base and for more complex code where you really want maximum performance just go ahead Implement an eye system with some jobs and schedule them in parallel as you can see all these syntax is actually pretty straightforward once you learn what all of these terms mean what is a system what is the burst compiler what are jobs how do you get components and so on of it is really relatively simple with the potential of being insanely fast okay so next let's look into instantiating let's go into our empty subscene to create a brand new game object call this the player spawner now let's make a component so an easy sharp script for the player spawner component then over here as usual let's make the say component so first of all it's a struct and Implement I component data then inside here let's add a simple empty film for our prefab so a type entity for our player prefab now let's make the usual authoring in bakerscript okay so here I've got the bigger now the important thing is on the mono behavior on the authoring component I made a Dev type game object but on the component itself it's of type entity so basically we want to convert this prefab into an entity which we can then instantiate in the empty world and how we do that is very simple first let's add our component let's add of type player spawner component and then over here for the player prefab how we convert game object into an NT is super simple we'll literally just call get entity and we pass in go into the authoring component to get the game object player prefab okay so that's it it's really that simple now here in the editor we have the player spawner first of all let's make the player into a prefab let's rename this college player then just drag it over here into the project and let's get rid of the one on the subscene so delete it then on the player spawner over here let's add the player spawner the authoring component and then we just drag our prefab reference okay now let's make a system to use this NTN spawn it so let's make a new c-sharp script for the player spawner system for this one you don't really care about maximum performance only to use the simpler system base so let's go with system base and then over here on update let's spawn a new player every frame until we have a certain amount for identifying players one thing we can do is use a nice tag component so let's create a new script called the player tag then up here let's do the usual thing except for a tab component we're going to make this empty so it's a struct i component data there you go just like this just an empty struct this is our nice player tag then we need to attach it so let's make the authoring and over here the only thing we do is just add the components so just a new player tag okay like this and now I need to actually fix this error just so we can compile it so our system base needs to be partial so just over here and let's make it partial just make that error go away in our code compile now here we can select the prefab and let's also add the player tag authoring okay so that is going to add a nice empty player tag and with that we can basically find all of the players in the world so you can go into the entity manager in order to create an NT query then we pass in the time so type of our player tag so with this we have an NT query and then on this we can calculate the entity count so that is going to tell us how many players exist in the world so we can just check if this is under a certain spawn amount if it is under that certain amount then we want to spawn a brand new one so for spawning one way that you can do it is go into the entity manager and call instantiate and pass in The Entity so we need to get our empty so let's do it just like we did previously so it's going to the system API in order to get the Singleton of our type so our type was the player spawner component and here we're just reading so we can use the regular Singleton we don't need the Singleton read write so you get that and from here we can access the player prefab okay so this would indeed instantiate it in this case since we're working inside of system base and running everything on the main thread in this case this should be working fine so let's do a quick test and if there you go it does work we do have in the two player units okay great so this does work however one very important Concept in ECS is the concept of structural changes these are changes like spawning or destroying entities or modifying an archetype by adding or removing components basically you need to be careful with when you do these types of changes if you do them while some system is cycling through some entities and everything will probably go wrong so for spawning or destroying entities or adding or moving components to do that you should do it at specific points if you look over here in the systems window we see all the systems that we already saw previously we've got a whole bunch of groups and then we also have some things called an enter the command buffer system basically what you can do is you can queue up some commands to run on one of these empty command buffer systems so you can queue up the commands and then they won't be executed the next time this runs basically these are called sync points so when adding or destroying entities when adding or removing components you should probably be using an empty command buffer which one you use is going to depend on what you're trying to do in this case let's use the begin simulation this one to spawn our entity so here let's do that so we go into the system API then we get Singleton as usual and for now let's go into the begin simulation and the command buffer system and let's access the Singleton with this we have the Singleton and then we can call create command buffer this one takes a world and for that we can use World Dot unmanage okay so this is going to return entity command buffer okay so we have this and over here instead of calling instantiate directly on entity manager which will spawn the empty right away instead we're basically going to queue up the command so go in here and concentrate and do the exact same thing so instantiate the exact same prefab then after instantiating this returns an empty reference so entity the spawn entity and if you want you can modify some things so for example let's go into the NT command buffer in order to call set component and let's modify our speed component so let's pass in a new Spin and for the speed let's pass in the value and put the value on something else to add a bit of Randomness let's grab our usual random components except since this one needs to update we need to use the read write so the random component value RW and then let's grab the random in order to get the next float and for the speed let's say between one F and let's say 5f Okay so we're giving a random speed to our brand new entity we just need to use the entity just like this okay so with this we should be spawning our entities and they should have a random speed let's also just increase the spawn amount by a little bit let's say just 20 Let's test any up there we have our 20 players all of them with some random speed and moving to random positions as you can see everything song works is done with the same so all the systems work regardless of how many entities you have for one more interesting thing let's look at how to mix ECS and game objects so over here I've got a very simple Circle Sprite texture then my scene has just created the game object like a regular game object not an entity and inside just play C circle with a nice bright run so pretty simple and again this is a regular game object nothing in here is being converted to any empty at all so now let's make a nice c-sharp script well it's our player visual let's leave this one as a mono behavior and attach it to our regular game object over here let's have a field for our Target entity so private entity for the Target entity and then let's make a function to select a random Target empty so private empty get random entity now here we can do similar to what we did previously so on the player spawner system over here we're using the empty manager to create an empty query however this empty manager this one is not the static entity manager this one is a component of the system base so over here if we use entity manager now we're using the class and not any property so we need to access a specific empty manager and when we can do that we can access the world in order to get the default game object injection world so it's going to be pretty much the default World in our case we know that that is the one that we're using we're not using multiple worlds so from this one then we can now get the empty manager and we can now create an empty query and let's pass in the same thing so type of our player tag so this is going to return an NT query of our player tag into the query and then from this one we can call 2 into the array which is basically going to give this an array with all of the entities that have the player tag we need to use an allocator we just need this to be allocated pretty much as one just as soon as we get the random entity then we want that allocated to be disposed so let's use temp that is the shortest live one this returns a native array of entity which by the way native array is inside using unity.connections okay so we have an array of our entities then let's just check if we have any so if this one down length is bigger than zero if so let's return a random one so just go in there and just use now we can use the regular random.range because again now we're in mono behavioral World between 0 and the anti-native array dot length and if there are no entities then we can return the default so the entity.no so this is the constant that always refers to an empty entity all right so now we want this player visual to visually follow our random entity so let's make a private void late update we want to update the visual after all these systems then in this one let's check if the target entity is not null so if it is not entity.no if so then we want to follow so we need to get the position of this target entity and again how we do that let's access the world to go to default World access the empty manager and from this we can call get component and we need a component of type that has the position so we can use the local 2 world transform we need to pass in the entity so that is going to be our Target entity and from this one we can access the value and then access the position so that's the vector 3 for the follow position which actually is not a factor three rather a43 but those who get converted automatically so we have our final position and then we just set this transform.position equals this followed position all right so with this we shouldn't have a nice connection between the game object world and the empty world let's just find how to set the target empty so let's do some simple input so input get key down so when I press e space key then let's get a random one so get a random empty all right so let's test any appear is indeed working so again here we have our Circle visual which is a complete normal game object so there's nothing related to entities on this game object itself a regular game object so that one is managing to interact with the empty World in order to follow an entity as it moves so you can see how easy it is to mix game objects and entities and access anything from the empty World while the game object Wireless and vice versa here note how I did it this way I basically made a mono behavior and from inside this I access the entities world but you can also do the opposite so on some kind of system base so something like this you could make a system then over here access some mono Behavior perhaps through a static field access that and then modify that over here so you can do both you can grab the game object worm from the empties world or you can grab the empties worm from the game object world finally let's do some performance comparisons so here I've got two simple Mona Behavior scripts running the exact same code first of all a player spawner game object so I just have a reference to the game object prefab which by the way I'm actually reusing the exact same prefab even though in modern behavior these components won't do anything so I got that then I just cycle through a certain spot amount I can instantiate on prefab then I add my game object move to position component and I set a random spin so for this component here it is again just regular game object code I've got a Target position and a certain speed then on update called the move function and the test reach position function then over here pretty much the exact same thing except using regular 3 instead of folder 3 and for the redness using Unity engine.random instead of the mathematics at random so there you go as you can see pretty much exactly the same code and up here it is running as you can see everything looks exactly the same looking in the hierarchy we can see all of these game object loads now let's increase the amount by quite a bit in order to bring the FPS down so here let's try increasing the spawn amount to 10 000. so let's see and up here we have 10 000 game objects and it's running at about 40 frames per second looking over here in the inspector it seems that all of these 10 000 are taking up 10 milliseconds then the rest is related to rendering which is taking another 10 milliseconds now let's bring the game objects back down to zero so spawn no game objects and on the ECS player spawner system let's put this one on Ten Thousand so let's see and if there we go here we have 10 000 entities we can see down here the moving eye system is accessing 10 000 entities and the frame rate is around 120 140 FPS and looking over here in the profile we can see quite a big difference so we really have to zoom in order to see just how much so the entire simulation system group is taking just 0.4 milliseconds and the presentation system group another 0.4 milliseconds and then over here for the rendering 2.5 milliseconds so previously we had 10 milliseconds in all the logic and now here we have less than one millisecond on logic so as you can see this is the kind of performance that you can get with ECS it's a pretty massive difference and hopefully with this video you saw that the Syntax for Dots really isn't that much more complex I mean it's definitely more complex than a regular amount of behavior but nothing too intense once you learn the main Concepts everything is actually pretty intuitive now even though this video actually ended up being a lot more detailed than I initially anticipated even with that there's still quite a lot of things that I haven't covered here if you want to continue researching dots in more detail I can help recommend their official tutorial guide over here you've got the introductions talking about on the concepts and then you even have some really nice really in-depth tutorials to learn absolutely everything then of course you have the entities documentation over here there's lots of great info to help you understand all the various Concepts within this and if you want a full-length project then I can also recommend the excellent 2 hour video by terrible mix games it's building a real nice project involving tons of Concepts and putting all together also you can follow his channel if you want to keep up to date with dots Intel 1.0 there's links on the description and I hope this video gave you a nice overview of the current state of the API and how it all works personally I think it's all pre-intuitive and easy to use so I'm very much looking forward to the future as dots hits 1.0 and regular people start using it to build all kinds of very interesting and very ambitious games speaking of that go ahead and add my game to your wish list I'll probably be using dots for some systems and making some nice devlogs from it or if you want to learn ninja in general check out my complete step-by-step courses alright hope that's useful check out these videos to learn some more thanks to these awesome patreon supporters for making these videos possible thank you for watching and I'll see you next time foreign
Info
Channel: Code Monkey
Views: 159,755
Rating: undefined out of 5
Keywords: unity dots 1.0, unity entities 1.0, unity ecs 1.0, unity dots, unity ecs, code monkey, unity 2022, unity tutorial, unity game tutorial, unity 3d, unity, game design, game development, game dev, game development unity, unity 3d tutorial, programming, coding, c#, code, learn programming, unity tutorials, how to make a game, unity dots ecs, unity ecs 2023, unity job system, unity 2023 ecs, unity dots release, unity 2023 dots, unity ecs tutorial
Id: H7zAORa3Ux0
Channel Id: undefined
Length: 62min 19sec (3739 seconds)
Published: Tue Nov 08 2022
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.