How to Make a Third Person Character Controller with Unity ECS

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
hi everyone i'm going to show you how i made a third person character controller with unity's entity component system here is the finished result a simple third person character controller and also an ecs compatible camera that can follow the player around this is just what i have found to be a good way to do this but i am by no means an expert i'm learning ecs from a complete beginner's perspective so please feel free to jump in the comments if you know a better way or if i've spoken incorrectly or if you have any tips that i might have missed i decided to make this video because i've been pouring through a lot of resources to figure out how to do this and so i've seen that there isn't much current material to work with which can be very frustrating so i thought i would share what i've personally done to get this to work with the current state of ecs one last thing i want to add before we jump in is that the reason for a lot of how i've set things up in this video is to try and follow the data oriented design paradigm as much as possible it's definitely hard since i'm a beginner to get out of that object-oriented mindset but i think it's important to note that this is the guiding principle behind the choices i'm making in this video i'm going to open up the latest version of unity um which at this point was 20 20.1 so you can go ahead and use that version to follow along okay i'm just making a new blank 3d project and i will name it open world because that's kind of the direction i plan to take the basic character controller after we get that implementation working here is our blank fresh new unity project let's create a folder for scripts and one for materials just for now now we can import the dots and ecs packages so we just need to go ahead and click this gear icon and open up advanced project settings then in the package manager tab tick this enable preview packages and i also tick show dependencies too so now preview packages can show up but we need to tell unity where to find them so hit this plus button to add package from git url and type in com.unity.rendering.hybrid for the hybrid renderer package and just hit add and then wait till that installs okay on i'm getting some warnings but that's okay it's a preview package i'm not going to worry about that for now now we need the dots physics package so once again we add package from git url and type in com.unity.physics and let that install all right so now if you have dependencies on you can see that a lot more packages like entities were installed and we are good to go for now we can add more packages later but i believe this is all we need to get started now we're going to make a ground placeholder and for that i will create a subscene to store it in which automatically converts anything inside to an entity basically my understanding is that it's an already optimized method of entity conversion so that is why we want to use subscenes as much as possible kind of not the best name but it's just going to store our basic plane to act as the ground for now so that's okay now we can add a plane which we can just name as ground and scale this up i'm going to just do 10 on x and z that looks fine we don't need that big of a world for now now let's make some basic material for it which i'll call test ground underscore matte and we can make this green and just drag it on okay now save the subscene and you can untick the subscene box when you're finished editing it um and if you want to make changes you could just re-tick it like for instance we can get rid of this mesh collider because we don't need that um and then untick it again when you don't need to make changes now we will make a player so right click and make an empty object and rename it as player and then as a child of that empty object we can make a capsule which we can name as playerbody to be our placeholder body and we can make a player material as well and call this test body underscore matte drag the material on and let's go for a nice cheery looking color for this little guy so i'm just going to tweak the settings until i have something i like in the capsule we can get rid of the capsule collider because we will be using the ecs equivalent which is the physics shape component and then we can give this guy some eyes to indicate which direction is forward so make some sphere game objects as children of player body um and i will make a new eye material for them as well extra glossy so he looks all cute then we can scale down the sphere and reposition it where we want and then when we have it in a good place we can duplicate it for the second eye also in the eyes we can get rid of these sphere colliders because we don't need them and um great now we have our player and he's looking good our little baby is born congratulations so now we can teach him to take his first steps so in our scripts folder i'm going to divide it into subfolders one called components which is your data another called systems which is the instructions and i'll make another one called utils for utilities because there are some model behavior conversion scripts we will need to write so that is where that will be stored along with any other non-ecs scripts okay so the first thing we need in order to get the player to move is inputs so we need somewhere to store the input data and so we will make a new component which we can do by right-clicking create go down to the ecs tab and choose runtime component type and i'm going to call this raw input data okay so now we have our data and we just need a system to process this data so under systems go to create ecs and select system and we'll name this process input data alright so here is our raw input data component if you read this text it explains a little bit about how this works but we can just get rid of it we can get rid of this serialize and get rid of some of these default libraries we don't need any of this just the entities package and we need to add a directive called generate authoring component okay so let's store the input values which i believe are floats and we can name one input h which will be our horizontal input and one called input v which will be our vertical input and that is all you need it is just storing the data now we can work on the process input data system so when you open this up similarly it has some helpful default text which you may want to read but we'll just get rid of that and clean this up a bit all right so now all we have is our generic entities for each loop which is where we will add the instructions that will process the component data that we have stored so to get our inputs we need to get access horizontal and get access vertical which are part of using unity engine so we can add that library and horizontal and vertical are strings so they can't be in the for each loop so we can store it in float input h which will be our horizontal input equals input dot get axis horizontal and similarly input v equals input get axis vertical i'm just going to type a comment here to restate that important part now we just need to set that data from our raw input data component in the for each loop for this entities.for each the ref keyword means that you are writing and the n is for reading so we don't need this translation we need ref raw input data and let's call it input and we don't need the rotation either okay and now we can set our data input dot input h equals input h and input dot input v equals input v okay save this and let's go back to unity back in unity after it finishes compiling we can now put our finished input component on the player but it needs to be an entity first so for the player we are not going to be using a sub scene like we did for the ground because of some camera issues that i'll explain later on in this video instead we can just attach a convert to entity component and now we can attach our input script and you can see that those public variables are exposed to the unity inspector we don't need those to be set here so we can go ahead and go back into our raw input data component again and add a hide in inspector tag which is part of unity engine library now we need somewhere to store our movement variables so let's make a component called move data and we can create a system called move system in move data we can add our generate authoring component get rid of this default text and now for our movement data we'll need speed and a target direction so we can make a public float move speed and a public float3 call it target direction and the float three is the equivalent of a vector three but for unity mathematics which is our dots compatible mathematics package okay so that's the component now for the system we need this delta time but we need to store this outside the for each loop inside our entities for each loop we need to iterate over all the entities which have our move data component and this data needs to be read here not set so we can use the in keyword so you can say in move data and call it movement we can keep this rough translation because we're going to be writing to this translation value which is kind of the ecs equivalent of model behavior transforms and we can set this to plus equals movement dot target direction times movement dot move speed times delta time we set the move speed in the inspector per entity and the target direction is based on our input values so we need to set the target direction where we process our input data so in process input data in the for each loop we add a ref for move data because we are writing to it and we will set the movement.target direction to a new float 3 made of our input horizontal and input vertical one more thing we can do is go back to our move data and hide the target direction from the inspector just to be clear that we're not setting it here and now we can save all our scripts and let the engine recompile now we can add the move data component onto our player and set our speed variable to 2 or whatever you like and then when we press play we can see that the player will now move on input press you may have noticed that when i press play it had a bit of a delay before the game started and that can be pretty annoying so there are settings that you can tweak in the editor so that it doesn't take a long time to load the game every time you hit play so go into your project settings and under physics you can uncheck auto simulation and then under editor in enter play mode check the first box for enter play mode options and then leave these reload domain and reload scene unchecked now when you hit play it should be much faster okay great so our player moves but now we want him to rotate to the direction of the input keys so we can go ahead and make a rotate data component and a rotate toward system i am choosing to make rotate its own component here rather than store that data in our move component because everything that moves i may not necessarily want to rotate and vice versa in our rotate data component let's add generate authoring component and all we need for now is a public float for the rotation speed in the rotate towards system we are going to interpolate the current rotation towards the input direction the player will be facing so besides reading in our rotate data we also need to read the direction which we have stored in our move data component we are going to use the unity mathematics equivalent for a look at rotation which is going to be quaternion which we'll call target rotation equals quaternion dot look rotation safe and take in our movement.target direction and math.up which tells the player where to look and on what axis he needs to rotate now we are setting our rotation value to be a math.slurp to smoothly rotate from our rotate value to our new target direction at our rotateddata.rotatespeed okay so we save this and go back to unity wait for it to compile and now we can attach the rotate data component to the player set a rotation speed and we can test it and there you go the player rotates in the direction of the inputs one more thing to add if you notice that when you aren't holding down an input the player still snaps back to a certain position which is because no inputs is a zero value which does still correspond to a direction so we can just solve this and go back into our code uh check to see if the input isn't zero because i want the player to remain facing towards the last input received and we can do this with a if not movement dot target direction equals float3.0 which is just a zeroed out float three now our player moves and rotates correctly so that's kind of step one of our character controller but now i'm going to show you how i've set up the camera to be compatible with all these systems we've written because in theory all of our systems that we've written will iterate over every single entity and it should affect them as long as they have the corresponding data components so i will illustrate this by making a follower entity to show how our systems should work on any entity and then why the camera specifically is a little bit tricky so to do this i'm going to go ahead and make a new empty game object called follow object and inside we can add a cube to be our new little follower buddy i'll make him his own material and let's get rid of his box collider and add our physics shape in its place you can copy the player's eyes and give the cube follower some eyes of his own alright so now we have a cute little buddy and now we can make a system that tells this cube to actually follow our player to do this we need a reference to the player entity so we need a new component to store this and we'll call it target data inside target data we need a public entity and we can call it target entity now we can make a system to act on the reference and we'll call it follow target so in follow target what we need to do first is get component data from entity and we're getting the translation this is going to return an array of all the entities with a translation component and if we can't find a translation that matches our target entity we can return out of the loop all right now we can set this translation to be our target position and now we need a ref to our move data which we'll call movement so that we can set our movement.target direction equal to our targetposition.value minus our followers own translation value and for this we also need a in translation back in unity we can test this out let's drag our target data to our follower object okay it's not letting me because i forgot to add the generate authoring component so let's change that really quickly in our script and then make sure to convert our follower object to an entity now i can drag our target data component onto our follow object and set the move speed and the rotate speed and set the target entity as the player okay he's following the player but he is in the floor because we aren't using any physics or collisions yet so let's just move up our player capsule for now so he's not clipping into the ground okay there we go we have a little follower guide that will follow the player around so i think i want to expand on this system to add in some more functionality that a camera would also have such as differentiating between what it follows and what it looks at so we'll make a look at system so that those two things can be independent of each other so first i need to decouple our move system from any rotation data so they can act independently of each other so in our rotate data we will add in its own rotate target direction instead of reusing our target direction from our move data component and alt enter because float 3 is part of unity mathematics now in our rotate towards system we no longer want to read in the move data so let's replace this target direction to our rotate data target position now that we have decoupled this we now need to set this rotate target position which we do in our process input data so that the player still rotates towards the direction he is moving all we need to do is add in a ref to rotate data so we can write to it and set it equal to the same movement.target direction we can compile this now and see if we set it up correctly okay great so the player still rotates in the correct input direction and you can see our follower cube does not look at the player because its rotate target position is not yet set so i'm going to make a new system now called look at target we can just copy and paste a similar code from our follow target system but let's change our rotate data to a ref and also read in our target data and read in the translation instead of this movement.direction we are setting our rotate target position now the movement and rotation are decoupled completely i can also define separate targets for them to follow or look at now so i'm going to go back into my target data component and change this to follow entity and we can add another public entity called look at entity then go back in our look at target system and change this to look at entity and in our follow target system we can change it to follow entity if we remove the movement component it should also still rotate to look at us but not move towards us now we have a look at system uh we can also add some code to make sure the follower doesn't rotate upwards as well once we add in physics and gravity i think that will nullify the need for this but we can just go ahead and do it anyways just to see in our look at target system i'm going to zero out the y component of the rotate target position okay let's test the code and there you go the cube is now looking at the player without tilting upwards okay so you can see that this code applies to a follower object and i think it would also be a good basic set of systems that we could apply to a camera object and if you comment out that line where we zeroed out the y value then we could also have the camera look down or up at the player depending on its y height okay so the reason i made the follower object was to demonstrate that the systems we have written are going to work on every entity that has the corresponding components but the camera is a special game object that needs to be converted a little differently if we want it to behave the same way as our follower object alright so if we go to our camera and treat it like we did our follower object and put all the same components onto it and do a convert to entity you will see that it doesn't function properly so what we need to do is convert and inject it instead of convert and destroy when you're converting to entity i'll try to explain why but being new to ecs i may not have the best explanation so i'm going to advise that you research this more on your own or find more qualified people to get a better understanding so for most game objects like our cube follower when we use this convert to entity component at runtime it's going to be converted and then the game object will be destroyed and only the entity will exist after that because it has no need for the game object data after it's been initialized and converted so the camera on the other hand is still somewhat reliant on the functionality of its game object so we don't want this to be destroyed and instead we need to inject the game object so that it will keep this data around at run time and we can use it to pass data between the ecs context and the game object context so correct me if i misspoken but that is my understanding so when i hit play even though we have converted and injected the camera it's still not affected by any of the systems we've written and we can check in the entity debugger what is happening which you can find in window analysis entity debugger and we can see that our main camera is being converted to an entity and it has all the components that we want move data rotate data and target data so in order to get our camera to behave the way we want and use our systems we are going to need to convert it manually so in our utilities folder we can make a regular c-sharp mono behavior script that i'm going to call convert camera to set up this conversion class we need to add the entities library and we need a public entity manager which we'll call entity manager and now we need to initialize the entity manager which we can do in private void awake and we set our entity manager equal to world dot default game now for the conversion we are going to add the i convert gameobject entity alt enter to implement this interface so now that gives us this convert method we can just get rid of this throw and now in this explicit conversion method we can add and initialize all our components manually okay so using our dst manager which you can rename to entitymanager if dst is not a clear enough name so dstmanager.addcomponentdata and we'll give it the entity which is our camera and then we can add a new move data component and set the speed to equal our own speed variable which i'm doing it this way so that we can still edit them in the inspector so just do this for the other components we need one for target data one for rotate data okay so now you can see that you can set all of these things in the inspector just as you would the other conversion way but with the exception of the target entity since this is a game object in a regular scene and the conversion and entity generation happen at run time you can't get the reference to the entity here so to bridge this we also have to make a similar manual conversion for our player game object so that we can pass this information so let's make another c sharp script and call this set player cam but future me has actually changed this to convert player so that it matches our other conversion script and the naming convention matches so same process here we need i convert game object to entity and alt enter to implement this interface so we need to get a reference to our convert camera script so to do this we need a public game object which we can call camera object inside the convert call we can make a convert camera which we can just call convert camera and set that with camera object dot get component convert camera if we can't find a convert camera then instead we can use gameobject.addcomponent instead so now we have our bridge to this camera object and then we can set the entity references to equal this entity which is the player entity at runtime after it's been converted one last thing that we need to do which is very important is to add component data so that our transform can actually be manipulated within the ecs context so during conversion at runtime the entity is also initialized with the data from the gameobject transform and we need to add the same thing to our convert camera script as well now we can drag the main camera object onto the script formerly known as setplayercam which i have now renamed to convert player i've also added some cubes to the camera just so that you can more easily visualize it in the scene so make sure your player is also injected and if you are still having some issues i found that deleting my library and assembly project files and letting unity rebuild them again helped in some cases now when you hit play you can see that the camera is behaving just like our little follower cube was and just like a third person follow cam of course this is like a very basic version of a third person camera you could spend a lot of time making this better but this was just to show you how i set things up and pass data between game objects and entities and how to use systems on game objects i am planning to upgrade this using cinemachine as well as unity's new input system and i also plan to convert everything to work with dots physics as well if you would be interested in watching that you can subscribe and check out the links below also i would love to hear your thoughts or comments on the video but i really hope that you found this video helpful and thanks so much for watching
Info
Channel: Peppaland
Views: 3,530
Rating: undefined out of 5
Keywords: ecs, ecs tutorial, unity, unity ecs, unity tutorial, entity component system, unity entities, ecs third person, ecs third person controller, ecs character controller, character controller, character controller tutorial, third person camera tutorial, unity third person camera, unity character controller, ecs beginner, unity DOTS, DOTS tutorial, DOTS character controller, DOTS third person camera, unity ecs tutorial, ecs guide, unity guide, how to, DOTS, ecs 3rd person
Id: ys6BZ3NdTwA
Channel Id: undefined
Length: 31min 49sec (1909 seconds)
Published: Tue Mar 30 2021
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.