In this video we’re going to create a player character that can place and break blocks in the world all right so this brings us now on to the next part which is actually getting a player character in here so we can explore our world and also start uh breaking and placing Blocks so to do that we’re going to make a new scene pick other node and we will use a character body 3D and this will be our player me name that to player and we can save this new scene player of course needs a collision shape I’m going to use a Cylinder and for the size of the cylinder remember that our blocks are one unit wide so for the height we’ll do 1.90 7 to 1.95 that way if we’re going into a two block Gap we will actually fit into it similarly for the radius set it just a bit below half right because We actually want to be able to fit between things that done I’m going to add just a node 3D this will be called Head this will be a you know a pivot point for our camera that we’re going to add as a child of the head and we’re going to move the Head up to actually be near the top of the body uh this might seem very familiar for any basic uh player controller next we’re going to add a ray cast 3D this is going to be you know looking out from our camera and seeing what block we’re looking At and for that we have to change the target position here if you look at it you can see it’s actually casting downwards we don’t want that we want to go out let’s say -5 m in the Z Direction so by default or by convention in gdo forward Is considered to be the negative Z Direction so that’s why it’s finally when we’re looking at a block we want to indicate uh to the player what they’re actually looking at and to do that I’m going to add a mesh instance 3D going to call that block Highlight we’re going to add a uh a cube there you go a new box mesh to that we’re going to uh change its material new standard material 3D go in there we’re going to enable transparency set that to Alpha and uh go down to albo change the color can make that say Yellow give it a bit of transparency and this way we’re going to highlight the block that we’re looking at with this yellow Cube you also want to change the size of the cube to be slightly larger than 1 meter that way it’ll actually envelop the block that you’re looking at instead Of kind of clipping into the block so with that all done we can now write a script for our player so inherits character body 3D call it player create now I won’t get too into detail here because a lot of this is like very standard uh for any player controller Start by copying in my uh node exports so you have of course our head our camera our Ray cast and our block highlight and I’m actually going to quickly save this go back to gdau build it so that before I forget you can set the head by dragging it in set the Camera set the raycast and set the block highlight and we go back into our script now I’m also going to Define some parameters for controlling our character so we have the mouse sensitivity movement speed and the jump velocity next we need to track our camera X rotation this is the rotation Around the x-axis so that’s going to be looking up and down uh we’re going to use this later to make sure that the player doesn’t look higher than 90° or lower than 90° so you don’t like turn around backwards on yourself from looking up too much lastly we’re going to get the Gravity which we’re going to get from the project settings uh so that it matches the Gravity used by other rigid bodies and uh as we did with the block manager and this is going to come in useful later we’re going to get a static reference to our Player uh we’re going to use this when we do infinite terrain so that we are tracking the position of the player and updating the terrain accordingly R ready method that gets called when the player is added to the scene we’re going to track our player by setting Instance and we’re also going to adjust the mouse mode to be captured so we can actually look around with our Mouse next I’m copying in the input uh method this is basically for you know having the camera uh look up and down look left and right so this is Rotating around the Y Direction that’s looking left and right based on the X movement of your mouse so moving your mouse left and right will rotate around the Y AIS and then as I was saying for the camera we make sure that any change in this angle wouldn’t take us Beyond 90° and if it wouldn’t then we can actually rotate the camera now we’re going to do our process method and this is where we’re going to handle uh one updating the block highlight and two actually breaking blocks but now to do that we need a way Of actually setting uh a block so we go over to our chunk script scroll down to the bottom and we’ll Define public void set block and now what this is going to take in is it be a block position and a block oops go what he’s going to do is he Going to index into our blocks array whoops block position dox block position doy block position. Z and it’s going to set that block to be the block that we’ve Chosen and then it’s going to update which will refresh the mesh and the Collision shape with that done we go back to the player Script and now firstly the block highlight should only be visible if our Ray cast is colliding with something uh in fact we should check if raycast is colliding and if we do get collider we get what it’s colliding with we want to make sure that that’s a chunk if you do is chunk Chunk that just means that henceforth we now have this variable called chunk that we can actually use in this if statement and uh maybe what I want to do instead is instead of setting this this this way we will actually say that if we’re in here so we’re colliding with The chunk specifically we’ll set that to be true else we will set that to be false so now we’re actually colliding with something what we want to do first is let’s get a POS the position of that block so to do that we do R.G get Collision Point however this gives us the point on the surf surface of the block we want to get a point inside the block now to do that we will basically step inside the block by doing get Collision normal and we’re subtracting half of the Collision normal so this is a normalized Vector that points out of the face that we’re pointing at so if this points outwards if we Step In by half we essentially get you know around the middle of the block but now we don’t want around the middle of the block we want you know the exact Position of the block itself do that we’re going to call this int block position and we’re going to really do it manually really make 100% sure that we’re actually rounding stuff properly and to do that we’re going to floor to int so it always rounds it downwards towards negative Infinity now we do block position. X math f. floor to in block positiony and then the same thing for Block position. z now I suppose this can be a vector 3i now now we have the block that we’re looking at first we’re going to adjust the position of our block Highlight to be at that block set it to int block position however we have to add on new Vector 3 0.5 0.5 0.5 and that’s because our blocks in our chunk are sort of like defined by you know one of their Corners however this mesh instance here you can see it’s Defined by its Center so that’s why we just have to add that little offset uh otherwise it’ll be in the wrong position not that out of the way we’re going to do if input. is action just pressed now we haven’t actually defined these inputs in our project yet we’ll do that right After so first we’re going to check did the player try to break the block if they did then we do chunk. set block and we’re going to do uh we going have to cast this to a vector 3i and it’s going to be the integer block position minus the global Position of the chunk itself because this uh this value here expects you know the relative position within the chunk whereas this is the global position in the world so we do that and now breaking a block is the same as placing a AIR block so that’s what we’ll do there then Similarly if they have pressed the place button which again we will Define then we will do chunk. set block and um actually I’ll just copy paste and we’re going to set it to be Stone and this is something that you know if you want to continue working on This project on your own you could set up an inventory system where you change what this value is you know depending on how many blocks you have in your inventory and what you’re currently holding and all that however the only thing is it’s going to feel weird to be Looking at a block and then right clicking on it and we change that block so instead what we have to add here is plus Ray cast. getet Collision normal so it’ll be whatever face we’re looking at it’s going to move one block out in that direction and we’re going to set that block Instead so that’s uh the block placing system done at least for now next up uh I’m just going to copy paste this in because this is just the movement controls so you know if the player clicks jump and they’re on the floor then they jump we get the left right back forward Movement move them based on which way they’re facing now this is all pretty standard so uh I mean feel free to pause and copy down all that so now with that out of the way we need to go back into our project and actually Define all these uh Movements or inputs rather so we go to our project settings input map add new action so we have uh just our block commands so place we’re going to bind that to the right Mouse button break we’re going to bind that to the left Mouse button and then of course we have things like Forward which will be W back should be S left b a right b d and lastly jump will be spacebar so all that done if we go back to our level scene we can instantiate a child node we’ll put in a player now before I open up the scene It’s going to look really ugly that’s because we don’t have any uh lighting and we don’t have a world environment set up so since this stuff is uh less specific to voxal terrain generation I won’t go into great detail but basically you know we’re going to set up an environment For the background let’s use a sky we’ll do new Sky set the material to be a procedural Sky material so now we’ll actually have you know a sky in the distance and let’s also add some directional lighting uh we can make this cast Shadows it’s up here let’s turn it there you go And we can build uh going to play the scene no main scene has ever been defined select one we’re going to select the current scene load in and here we are so we can move around as our player we click on a block we break it we right Click we place a block now looking at these blocks now that we added the directional light and all that you can see the lighting on them is looking a bit strange that’s because the normals aren’t set up properly for the mesh so we’re going to exit out of here and make A quick uh detour back to our chunk script where we’re going to go down and uh it used to be right that you could do surface tool. generate normals and this would just automatically create the normals for you uh last time I I tried this it looked really bad it didn’t Actually change anything and I’m not sure why so instead we’re going to manually Define the normals now to do that I’m just going to copy paste in some code I wrote Going add it in down here so what does this mean basically if you do c minus a so That’s Corner C to corner a um this is one of the edges of the face then B minus a that’s another Edge now since these are vector 3 I I have to cast them to Vector 3es so I can take the cross product because if you take the cross Product of two vectors that Define a plane you get the normal Vector coming out of that plane and just to be safe I normalize the result now with those in much the same way that we did for all the uh other triangles we’re going to define a list Of three values this is essentially you know the normal Vector for each uh vertex in the Triangle now since it’s just a flat plane they all have the exact same normal Vector now we just add that to our triangles we can do normals normals normals normals and if you Haven’t used a lot of C the reason why I’m writing the name uh before doing it is because if you look at all these you know it’s a lot of optional parameters and we’re not necessarily doing them all we’re not necessarily writing them all in order so with the UVS I could just write UV triangle because you know the UVS come after the vertices but we’re skipping over the colors array we’re skipping over this UV 2s array and we’re doing the normals so for that you have to write the name of the parameter that you’re setting Essentially so now with that done if we go back to the problem to the project and we rebuild then the problem should be fixed and you can see looking at the blocks they’re uh Shadows are looking a lot nicer now there’s a problem in this that maybe you noticed when I was Rting the code if I start placing blocks like this it’s no longer working and you can see I’m getting some errors in my logs if I go over here I can place the block just fine but now trying to place a block here again it doesn’t work why is that this right Here is the boundary between two chunks so when I’m looking at this block I’m looking at that chunk over there but now I’m trying to set a block in the chunk that I’m standing in so that that breaks it goes out of bounds to fix that we’re going to write another Script uh we’ll use just a basic node we’re putting it at the top up here uh same way that we did with the plot with Excuse me the same way we did with the block manager it’s at the top of our hierarchy so that it gets loaded before The chunks to so again the order matters we’re going to call this the chunk manager is it before the chunks do or after the chunks do well now I’m not sure regardless we’re putting it up here you have a new block manager going to create a script for it or not block manager Excuse me chunk manager like we’ve done with a lot of other things we’re going to put tool on this so it can work in the editor and we’re also going to add a static instance get private set because we’re going to want to use this in various different Places now what this chunk manager is going to do is it’s going to record the positions of all of our chunks so that we can really quickly look up where our chunk is to do that I’m going to make two dictionaries oh and it’s complaining that it doesn’t have the type click show Potential fixes now we could either use gdau do collections or use system. collections. generic make sure you use this if this is written at the top of your project if up here it said you know using G do collections uh you’d start getting some errors because it would conflict with This one this is the specific one we want so these two dictionaries we have one that takes a chunk gives you the position of the chunk another that takes a position and gives you the chunk at that position we’re also going to want to record a list of all the chunks in our Scene and uh for now we’re using this class to better look up where the chunks are so that our player can properly place blocks but in the future this class will also handle the infinite terrain generation in updating the chunks as the player moves so now we want to do is we’ll Write our ready method as we’ve done before we’re immediately going to set our static instance to this so that we can keep track of where this jump case now we’re also going to write a bit of uh link Q or link or whatever you call it I should have looked up how to Say it so what we’re going to do is we’re going to get our parent which is the level then we’re going to get its children we specifically want the children where let’s say child child is chunk however this is going to give us essentially an array still of just nodes So we going to do select and we want the child as a chunk so that’ll actually cast the children to be chunks and this will actually be a list of chunks once we cast it to list so now we have all the chunks in the scene um we’re actually not going to use This quite yet that’s that’s my I should have waited till later but oh well this is going to be very useful when we do the infinite terrain generation what we really want is a way to actually update these dictionaries so we know where all our chunks are to do That we’ll make a method called update chunk position that will take in a chunk it will take in its current position and its previous position what we’re going to do with that is we will look at our dictionary of positions to the chunk at that position we’ll try to get a chunk that Was at the previous position it exists we will have it as a variable called chunk at position and now if that chunk at position is equal to the chunk that we’re updating then we are going to remove it from our list the reason why we make this check Is uh say at the start when we first load the project and say we’ve generated a bunch of new CH chunks and say they’re all at 0 0 then their pres previous Position will all be written as 0 0 but maybe another chunk is meant to be there Where you know and has already overwritten the fact that this original chunk was there then we end up removing that chunk you know it become a whole mess so we just make sure that we’re actually removing the chunk that we intend to remove and then aside from that we just Simply update our dictionaries so our mapping from of chunks to their positions do chunk equals current position then the backwards mapping we do a current position equals chunk and the reason why for this array we don’t have to go and remove the previous value is because you know we Will always have the chunk that we have and we will just be changing the positions they are at but this array might not continue to contain all the positions that it originally contained because if the player is wandering far off into the distance the positions behind them will eventually no Longer be holding chunks so that’s why we have to make sure we remove them now we have to actually call this method and we’re going to call it from chunk so what I’m going to do is up here at the top going to define a new method called uh public void set chunk Position and actually we’re going to take these things in here we’re going to put them up there right because uh we’re going to keep reusing the same chunks the same chunk objects we’re just going to reposition them around the player instead of you know deleting the previous chunk creating a new chunk cuz That’s a lot of overhead that we don’t necessarily need so that’s why we’re going to generate an update as the chunk position gets set we’re also of course going to uh make sure that this actually takes in the position that it’s being set to we’ll say that the chunk Position will Be equal to that position and now the global Position will be equal to new new Vector 3 chunk positionx uh times dimensionx so times the width zero in the Y because we’re not doing you know chunks on top of chunks and then oh chunk position do y Times dimensions. Z it’s always a bit confusing when you’re going between a 2d Vector to a 3D Vector where here we intend this axis to be the same as the Z axis but it’s called Y in a 2d so yeah don’t get confused and then of course we want to Actually call our chunk manager method where we do update chunk position with this has been moved to position and its previous position is uh whatever chunk position is currently set to so there you go and now here instead of setting chunk position like this we’re going to use the method we just is Defined clean this up so cool now we’re actually setting in our chunk manager the positions of all the chunks now what we can use that to do is we’ll Define public void set block which will take in a vector 3i Global position and the block that we want to Set it to now first what we got to do is get the chunk that’s that this position is in so for that I’m just going to copy paste this in cuz it’s long what this is the chunk tile position where it is the global position divided by the width of the chunk I’m Casting it to a float to make sure that the uh the division’s actually nice and then I’m flooring it to an INT feels unnecessary to do all that but I tried this before where I was just doing integer Division and it was messing up maybe something else was Messing up maybe it was this not sure but I’m feeling a bit superstitious so I’m going to leave it this way so that gives us the X and Y uh X and Zed coordinate of the chunk itself now we’re going to check our position to chunk Lookup I’m going to see if it oops if it contains a chunk at that position if it does we’re going to get it and then we are going to set a block in that chunk cast to Vector 3i Global position right so the global position of the block minus the global position of The chunk itself and I forgot a parenthesis and we will set that to block so now if we go back to our player and up here where we’re placing the block instead of doing chunk. set block we’re going to do chunk manager. instance. setblock now remember this takes in a global position whereas This one takes in a relative position so for that we just take out this part where we’re subtracting the global position of the chunk and with that in place if we go back to our project just make sure we save it hit play now if we go in here I’m placing a Block I think this is yeah this is around where our our uh sort of Chunk boundary was and you can see I built right across the boundary without any errors Video Information
This video, titled ‘Godot 4 C# Tutorial – Minecraft Terrain – Part 3 (Player)’, was uploaded by xen-42 on 2023-12-06 18:00:30. It has garnered 228 views and 21 likes. The duration of the video is 00:29:49 or 1789 seconds.
Get the source code on my Patreon: https://www.patreon.com/xen42 Join my Discord server idk why not: https://discord.gg/UGGZSngByb
Watch part 1 first: https://youtu.be/TM3r2V4980k
Was made in Godot version 4.2 (mono)
This video covers creating a player character that can break and place blocks.
This is the third video in a tutorial series on how you can make editable voxel terrain with threaded chunk loading/unloading in Godot 4 using C#. This is an intermediate tutorial series that assumes you have working knowledge of C# and Godot, as well as LINQ.
You can also just blindly copy stuff down I can’t tell you what to do I’m not your mom.
I want to be real and just call the video voxel terrain but Youtube analytics tell me people search for “Godot Minecraft” and we just have to play the algorithm am I right, a man’s gotta eat.