In this video we’re going to implement different block types and use random noise to procedurally generate terrain so now that we have this basic chunk created we’re going to start defining uh some more interesting block types to use first we’re going to make a new scene it’ll be a 3D scene and let’s Call this level this is going to be our actual game level so let’s go ahead and save that and we will instantiate a child scene we’ll give it a chunk to work with now we’re going to add another child node and we’ll just make this a base Node and we’re going to rename this to block manager and what this is going to do is it’s going to keep all our data about our blocks and it’s also going to generate a texture for this chunk to use based off all the blocks that we’ve Added so we can save this again and we’ll do attach a new script call it block manager and here it is and much like all our other scripts we’re going to add the tool attribute to it because we want it to run while we’re in the editor now let’s define a few blocks That we’re going to have in our game so we’ll start with an AIR block I love the uh the suggestion there to go from air to bear um no thanks I’m going to call that stone instead export public bone no it’s going to be dirt and lastly let’s see what it Suggests here bone again nope it’s going to be grass so those are our block types next we’re going to make a dictionary make it read on only and it will map from a texture 2D to a vector 2i and what we’re essentially going to do is we’re going To make a texture Atlas out of all of our block textures and this is going to map from an image to where it is inside of that texture Atlas if you followed like the previous tutorial I did on this uh you might be familiar with the whole Texture Atlas thing so we use that there I think this is a better way of doing it just because it makes it a lot easier to add new blocks by just adding a new texture I’m going to call this Atlas lookup and we’re going to instantiate it to be a new Dictionary next we’re going to define the size of the uh the grid that we’re going to draw all of these images to and create a texture Atlas out of where the grid width will be fixed it for but the grid height will be variable because is as we Add new images you know it’ll just keep getting taller we’ll keep adding more rows to our texture next up we’re going to add a vector 2i which will be the block texture size if you remember before when I show Oops why did it I did it do That when I showed the uh the textures I made they were oops they were 16x 16 pixels so if you want you know more resolution in your textures you can modify this just make sure it actually matches their sizes and then that should just work lastly uh we’re going to define a Texture Atlas size get private set because we only want to set it in here and this is essentially going to give us you know these values once it’s done generating our uh atlas of textures and I’m leaving it as a vector 2 instead of a vector 2 I because I’m Going to do some division using these values later on and integer division can get weird with rounding and stuff like that so we’re just going to keep it as a float lastly and this is part of the reason why I’m using C is I like using static classes static Instances so this will allow us to access our one block manager from our chunk script and later on from our player script and think this the last thing we’re going to define a standard material called chunk material sometimes the autofill is really annoying there you go Get private it’s set so once we’re done generating our atlas of textures we’re going to make a material and we’re going to use that material in all of our chunks right now this is going to be some of the more complicated code in this So I mean if you really don’t get it you could just manually Define the chunk material and and create a texture Atlas but I don’t know I like this one first we’re going to set our static instance to be our current class now we’re going to use some link Or link q i I don’t actually know how you’re supposed to say it but it’s a it’s a way of doing stuff with arrays that’s very useful in C so we say we’re going to define a list of all of our block textures and actually that reminds me if we go to our Block script uh blocks do not have textures yet so let’s do really quickly export public texture 2D texture get set and this should be a capital T actually by convention and for now the blocks will just have the same texture on all faces we’ll come back and and add To this uh later on once we actually have it working so we want to get all of our block textures to do that first get a list of all our blocks air stone dirt and grass then we’re going to call Select CNC now it’s using system. link or linku I i’ have no idea how you’re supposed to say it and with this you basically give it in a function so this would be that it takes our block and from that block it gets the texture next we’re going to add in a wear which will check texture where the texture isn’t Null right because uh say for our air Block it’s not going to have a texture so we want to filter that out lastly we call uh no it’s not unique it’s distinct so for this this will be say for example our grass block in the future the underside of a grass block Looks identical to a dirt block so the texture might appear here twice so we use distinct to make sure we only take it once and lastly we’ll do to array just to get this back into a more familiar form so there you go if you haven’t used uh inline array modifications and good Luck now we’re going to Loop through all of our uh textures so for an i equal z i less than the length of our textures array we’re going to grab the texture and we’re going to add it to our lookup table texture and it’s going to taken a Position so basically we’re going to take you know its index and figure out where that would be as we go along the rows left to right and every time we get you know to grid width we go down to the next row down to the next Row do that we do a new Vector 2i take I modulus grid width and then for the Y we’re going to floor to int I divided by the grid width um and I’m missing some brackets next we’re going to determine what the grid height is for that we’ll Do sealing to int so basically this just means round up and we’re going to do uh well the suggestion is close to what we want to do but not quite we’re going to do the length of our block textures divide by uh the grid width and I think Actually have to cast this to a float else it’s going to do integer Division and it’s going to mess up so this would again be you know if we had five images and the grid width is 4 5id 4 is 1.25 but of course we can’t have 1.25 Rows that makes no sense so it’s going to round it up to two so we’re going to have two rows just the second row isn’t going to be completely full now what we’re going to do is we’re going to create an image that’s going to contain all the textures from all of our Blocks and this excuse me this image is going to be uh in pixels right it’s going to be the number of images we have uh going widthwise times how wide in pixels each one of those images are and then similarly for the height do the grid height time the actual size of the Texture now for this we’re missing um so it takes in use mid Maps we’re just just going to set that to false I’m not even sure what mid maps are and for the image format just use rgba 8 again not entirely sure what the optimal value is For that but this has worked for me so we’ll just use that next we’re going to Loop through uh every texture in our grid and this is where we’re actually going to start adding these textures to our main image so for that from the XY coordinate First we’re going to get the image Index this is just sort of the inverse of what I did here where for this it’s going to be x + y * grid width now like I kind of mentioned here uh when we rounded this up you know the Last row might not be entirely full of images so we’re just going to check that our image index if it’s greater than the number of images then we’ll just continue and eventually it’ll finish looping otherwise let’s actually get the image for that block so for that we’re Going to index into our uh array of textures and then we’re going to call get image so this is a method on a texture 2D that copies the data from the texture and makes an image next we’re going to convert that image to make sure that it Is the same format as our uh large image that we’re creating and lastly we’re going to draw into that large image using the blit rect method which is going to copy in the pixels in a rectangle it’s going to copy from current image it’s going to take uh essentially everything in that Image so for that we Define a new rectangle that starts at zero and has a side size that’s equal to you know the entire size of the image and then the position that we’re going to copy it into is going to be XY uh but multiplied by the size of each texture Right because this is the specific pixel position that we’re going to draw it into so now we’ve drawn everything into our image so we can actually get out of this for Loop so our image now contains all our blocks and what we’re going to do is we’re going to create a texture From that going to call it texture Atlas and for that you do image texture. create from image image and this returns a uh image texture type which we’re then going to use in our chunk material so we’re going to make a new chunk material and in here we Will set its albo texture to be texture Atlas another very important thing that we’re going to going to do in here is set its texture filter to be nearest and essentially this is what you use when you’re doing you know pixel art and stuff like that where you want it to actually Really crisp crispy crisp make the pixels really crisp I don’t know what I’m saying there but otherwise it it it sort of Blends them together and it looks really really bad for pixel art now with that all done we’re just going to set our texture Atlas size to the grid width grid height That’s going to be really important later and just as a bit of a sanity check we’re just going to print out uh the size of our Atlas how many blocks we made it out of just to make sure that this actually worked and with that done the only thing Left is to actually make it so we can access this dictionary and actually read from it so for that we’ll make another method called get texture Atlas position that’s going to take in a texture 2D and it’ll say you know given this texture say it’s the dirt texture it’ll Tell us where in our material that image is actually located so that the chunk can actually render that texture properly next thing we’re going to do is just because we’re a bit paranoid that maybe we’re going to be asking for a texture that’s null if that happens just return zero It’ll just default to the first block uh in the texture which is fine better than it you know failing horribly and throwing errors otherwise we’re actually going to index into our dictionary and return the position of that texture so now that this is all implemented we Can go into our chunk script and update it to actually use the material that we’ve created and actually change the generate method so that we get you know different blocks so let’s remove this line where we were creating that placeholder block said here we will Define block Block and now we’ll do some if statements to see which blocks we’re going to use so I think for now I’m going to Define a variable called Ground height we’re just going to set that to 40 for now in the future we’ll use some random noise to procedurally change the height of the Train but so placeholder let’s set it to 40 now if our y value is less than let’s say the ground height divided by two so be less than 20 then we are you’re going to use a stone block else if it’s just less than the ground height we will use a dir block Else if Y is equal to the ground height so we’re at the surface now we will use a grass block and finally for above ground we’re going to use an AIR block then we’re going to set our block to be equal to that block of course this would work for now But we’re not actually doing anything to use the textures of any of those blocks how we do that is here before we commit uh what we’ve created in our surface tool to our mesh we’re going to do surface tool. set material and and we’re going to tell it to use the chunk Material that we created in our block manager now here we’re creating the block mesh firstly we’re going to want to figure out what block are we actually generating so to do that we will get the block at the Block position now we’re going to do a bit of A hard coding uh if you were to actually extend on this and add more blocks you’d probably want to change how this works but for now we’re going to say that if we’re making an AIR block just return don’t create a mesh don’t do anything just return right because you can’t see The air you can’t touch the air uh well you don’t collide with the air so no point in even doing anything now if we look at create face mesh we’re going to want to take in a texture so we’re going to add a parameter that texture 3D texture so That way we can actually draw the texture that we want to make and of course not complaining that we’re not telling it which texture to use so just add block. texture to all of these and again in the future we can come back and change this so that the Top texture can be different from the bottom texture or different from the side texture so we can change this in the future for that we go down to create mesh face there’s a few things we’re going to want to do first we want to get the position of that texture in our texture Atas so we’ll do block manager. instance. get texture Atlas position and we’re going to give it our texture next we’re going to want to get the texture Atlas size which is like this and I’m just doing that so I don’t have to write out this entire thing every single Time now what are we going to do with these if you’re somewhat familiar with you know 3D modeling you’ve probably heard of you know UV unwrapping for texturing models so we’re going to figure out our UVS which is the location in our texture Atlas uh of each corner of our square that we’re Drawing and that’s done as a value between Z and one so this position here is given in terms of pixel coordinates right but we don’t we don’t want to use pixel coordinates we want to use a value between 0o and one so to do that we just divide our texture position By our texture Atlas size we also want to get the width of our Square in terms of UV coordinates so to do that you know say the texture atas size is like you know uh four so that would mean that we have four you know Blocks say the width is four so we have four blocks per row so that means that if we’re going from 0 to one each block is going to be 025 wide and then similarly for the height we do one divid by the size in y in the y Direction next in much the same way that we’ve defined every corner uh in like physical space we’re going to Define Each corner uh the UVS of each corner and for that we first start with the UV offset because that’s sort of you know the position of this square that We’re drawing or that we’re getting the texture in and firstly this first corner is going to be say in the top left the next corner is going to be in the bottom left third corner is going to be in the bottom right and also seeing that I made a typo go back fix That and lastly in the uh top right and if you remember when I was talking about these ones I was they were also in you know top left bottom left in the same order because again the order is very important here and then in the same way that here We defined uh the triangles we’re also going to want to define the uh UVS for each triangle just this time they’re Vector tws because they’re the positions on the texture and again uh pretty much exactly the same where it goes ABC and then a c D and lastly down here oh got to rename that to Triangle two lastly down in here we also pass in you can see the next variable that I would take in would be the UVS so we give it UV triangle one and down here UV triangle 2 I think we’re almost there now one last thing to do is now that we actually have you know a block that is transparent that being the air we can actually make this uh use the the block that’s actually at that position instead of just returning true so I’m going to copy paste some Stuff in so basically here this is just making sure we’re not going out of Bounce right so if we’re at the very bottom of a chunk we are going to render those faces because you know the is just void essentially outside of this so we will always consider those to be Transparent otherwise we check the block at that exact position and if it is air then it is transparent and again if you were to extend this and add new block types maybe you would add you know to your block class a parameter that says if it’s transparent or not so if you Were adding like leaves or something like that maybe they would be transparent so this is left open for extension I think with all that done we can go back to our editor build our project and when we look at our block manager we can see exposed in the Editor we have the air stone dirt and grass if we click on this because we use the global class attribute we can define a new block we can do that for each one we open up the block we can see it takes a texture so for air we’ll leave The texture null for stone we can drag in that texture for dirt drag in that one and grass drag in that one if we save our scene close it we can close this old chunk scene as well and open the level back up uh we can see it’s broken okay I see I’ve had this problem before I’m not sure why this happens but yeah if I check my import here for these images it uh imported them weirdly it thinks that they’re they’re meant to be compressed yeah like this one is lossless this one has vram compression don’t want any of that so I’m going to select all of them click here preset click 2D then click reimport now if we save close the scene and reopen it now it’s actually working properly so not sure why it does that because the first time I tried any of this it didn’t do that at all and it Just worked and then the second time I tried it it happened not sure what that’s about but now there you go we actually have different block types uh so what we can do now so we’ll go in here and let’s modify our chunk time to actually Implement some uh random Terrain uh for that what we’re first going to do is we’re going to define a new Vector toi called chunk position and essentially what this represents is the position of this Chunk in the larger you know tile set of chunks so you’d have you know your chunk at 0 0 your chunk at 01 uh this essentially defines where every single chunk is going to be in our ready method we’re going to want to set that value so we’ll do chunk position equals new Vector 2 I ff. floor to in and this will be the global position of the chunk divided by The width of a chunk and then again I have. floor to int the global position. Z of the chunk divided by the depth of the chunk we’re going to use that value down here where we’re going to alter our generate method where we want to get the position Of this block in the world so that’s going to be at the chunk position times the size of the chunk and you might think this is a bit redundant um that up here we’ve divided by the size and then here we’re multiplying by the size uh I forgot to write new um and You’re right it’s just because in the future later on this project we are going to modify how this works and it’s you’re going to have to do it like this else it breaks so currently yeah it’s a bit redundant but in the future uh it’s going to be very Important so this is the uh sort of real world position of the chunk itself and then we’re going to add on the X and Zed position of the block and we’re going to use that position to look up in a uh noise uh generation object and we’re Going to get some random noise and that’ll be our ground height so for that we’re going to export a public fast noise light instance we call that noise get set and instead of just setting the ground height to 40 we will do noise. getet noise 2D at the global block position now this Returns a value between -1 and 1 so we’re going to add one to it which now gives us a value between uh negative no between zero and two and me check why is it complaining oh it does not take in a vector okay position. y so yeah get noise 2D gives a value Between negative 1 and 1 we add one to get a value between 0 and two we then divide by two to get a value between 0 and 1 and then finally uh well actually not quite finally but we’re then going to multiply that value by the height of a chunk so That now gives us a value between 0 and 64 and lastly we’re going to cast that to an integer so now with that our round height can vary randomly and it’ll use random noise to do that if we go back to our level scene we’re going to build It then on our chunk and actually we can go into the chunk scene itself for that be a ton of Errors now when we open up the chunk because it’s expecting there to be that block manager that’s in the level so that’s fine uh we don’t we Don’t need to be in this scene right so here we can Define the uh the noise for it just do new Fast noise light and we can just leave it with the default values for now but you can Tinker with these to get different terrain generation we now go back to our level We can see it’s now using that uh randomly generated terrain and we actually get some varied uh terrain height now if we add another chunk to the scene I’m holding control while moving it to have it snap to uh the grid you can see that at the start it Looks identical but if we just reload the level since it’s based on the actual position of the chunk we’re actually getting some smooth looking terrain we can add in another chunk is that yeah that’s lined up well add in another chunk and again if we save and Reload and get some smooth terain going around uh one thing you’re going to want to avoid is don’t duplicate the chunks because when you do that when it duplicates these values over here it doesn’t say you know in the duplicate the Collision shape doesn’t point to the duplicates Collision shape it points to The original Collision shape of the object it was duplicated from I don’t know why GTO does it that way I think Unity does it the way the other way and that’s why I expect it to be that way um because I’m more used to duplicating things in unity I guess but Uh yeah just a little Pitfall to avoid right there’s just uh there’s one last part that I forgot to implement which is if you look at the grass blocks you can see they don’t have like a nice side text or anything like that so we’re going to fix that Up uh actually let’s go into the block script first so here we’re exporting uh just the texture oops I copy that line twice at this one we’re going to call it the top texture here we’re going to call it the bottom texture I’m also going to write a helper method called Textures uh we writing this as like a property get which is why this is written as an arrow and it’s going to return a new array which is going to have texture top texture and bottom texture in it and the reason why this has to be an arrow here Is because f was just an equal sign uh these values don’t exist yet right so when we do this it just means that when this gets called it’s going to make this array and what this method here is for is just to collect together all the textures that this block uses for the Block manager so if we scroll down in here and we look at where we’re getting our block textures see right now we’re just getting the one texture instead we will get all the textures however this is an array of textures meaning that now instead of this just Being an array of textures it’s an array of arrays of textures and to fix that just change select to select many because what this does is it you know applies this and then it flattens the result down into a single long array so instead of an array of arrays it’s just one long Array so with that implemented it’s now actually going to be you know looking at all three possible textures that we can have here next we go into our chunk and in here and create block mesh as I mentioned before uh we can now modify this to use the top Texture but what if you have something like Stone where the top texture isn’t distinct from the side Textures in that case we’ll leave the top texture as null so what we can do is use the null coalesence argument or operator rather and what this does is if this value is Not null it just uses this value but if this value is null then it will default to this value over here so it’s a very useful uh operator now similarly we do the same thing for the bottom texture where it’ll use the bottom texture if it is Not null otherwise it’ll default back to the uh default texture so now here we go back in here and we rebuild now see that there are all these extra textures that we can Define if we go down here to our grass block we will change the default texture this is now Like the side texture right so we will use our side texture for the top we’ll use the grass and for the bottom we’ll use the dirt and now if we restart our level we can now see that it’s using the appropriate texture for the side a different texture for the top and there Is a different texture for the bottom we just you know can’t see it right now because those are all underground so there you go that’s uh We’ve now finished up our sides and then you know if say you did want to uh go ahead and like Define a block say Like you know think back to Minecraft a furnace has different textures on the front as it does the left and the right so you could change this to have a different texture here uh stuff like that Video Information
This video, titled ‘Godot 4 C# Tutorial – Minecraft Terrain – Part 2 (Blocks)’, was uploaded by xen-42 on 2023-12-05 18:00:16. It has garnered 362 views and 24 likes. The duration of the video is 00:37:25 or 2245 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 implementing different block types and procedural terrain generation.
This is the second 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.
I just copied the description from the first video instead of finding new unique ways to say key words for the Youtube algorithm. Gottem.