Building a Roguelike from Scratch in Godot, "Dungeon of Recycling" (Tutorial)

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
in this tutorial I'll show you how to build a simple roguelike from scratch in Godot 3.1 I'll be starting for the beginning on how to use Godot but I will not be covering programming basics just because it would take too long that could easily be an hour long video by itself so if you have a hard time following the scripting sections I recommend watching some programming tutorials first or reading the official documentation on Gd script as you can see I've chosen a pretty silly theme this time that's right he plays a recycling bin you attack packs of trash and they drop recyclables for you to collect to start let's do a little planning there are several key elements that make a game like rogue but not all games labels as roguelikes have all of them for the sake of time I won't be going into RPG elements or magic system but I will be doing a procedurally generated top-down tile based dungeon that's revealed as you explore with turn-based attacks of movement items with randomized effects and because I'm not implementing a safe system will get permadeath for free or break down the development into seven main areas initial setup level generation movement level progression fish and limiting enemies and items so let's get started first let's create a new project now we've got an empty scene and started in the 3d view by default but I'll be using the 2d view for this game over in the scene tab clicking 2d scene gives me a node 2d object everything in the scene is organized in a hierarchy specifically a tree the most basic type of object at a scene is called a node it doesn't do much other than let you group objects together under it a node 2d expands on this by adding position rotation and scale in 2d space anything you put inside it or beneath it in the hierarchy is called child node children are also affected by the position rotation and scale of the parent so for instance if you have a spaceship node and put objects inside it they'll move with the spaceship the children can also have children and this can go as deep as you want to organize all the pieces you need to make up your game however there can only be one root node or call it game and put everything else inside first up I'll create a tile map this basically gives me an endless creative spaces to put tiles basically images that could optionally have a collision shape among other things I'm going to use 32 by 32 pixels tiles and create a new tile set clicking on tile set takes me to the tile set editor I'm not trying to make an art tutorial so I created all my sprites in advance I'll just add them to the tile set for each I'll have to click new single tile to actually make a tile from the image then it'll turn on snapping to the tile size and draw a little square to cover the area that I'm going to use for the tile for most of these that'll be the whole image [Music] so now you could just paint with these tiles to make a level by hand but I'm going to use right-click to erase all these because we'll be generating the levels procedurally next I'll turn on snapping to a grid for the editor view and we'll make this grid the same size as my tiles now I want to create a player object I could right-click and add child node again but for sprites is a shortcut with the parent node selected I can distract the player sprite onto the scene now I'll rename this and make it not centered so if it's neatly inside a tile next I want to add a camera as a child node and what the view to stay perfectly centered on the player so I'll turn the drag ranges down to zero and make this the current camera I'll also offset it by 16 pixels so it's centered in the tile now might also be a good time to set the window size I actually want this to do pixelated upscaling so I'm going to set a low resolution here 640 by 360 and then set the scaling type to viewport that's the part that gets us pixelated up scaling but only if the window is resized so I'm going to resize the window automatically on startup to do that I'll attach the script of the game node but then I'm going to switch to a third-party editor to do all my coding just because I wrote a plug-in to type for me so you don't have to watch all my typos in the ready function let's set the window size to double what we just put in 1280 by 720 now let's save and run this and select the default scene [Music] in yep that sprite is pixelated okay now if you wanted to do this without also pixelating text you could instead turn on filtering on the images reom port them and use the two descaling mode instead of viewport now the last thing I want to do for this part is to make all the objects for our HUD I'll start with a canvas layer because that ignores the camera and will always see in the same place on the screen inside this will create a color rect for a background and set it to a semi-transparent grey then I'll add labels for the current level HP and score okay nothing fancy but that'll do for now this is going to be the most complicated part but once we get through it everything else will be easier let's go back to that game script and start by defining some constants these are all fairly arbitrary let's say there are five levels and they get bigger as you go and get more rooms these dimensions are just numbers I made up it seemed reasonable having them in constants at the top makes it easy to come back and tweak them if I change my mind I'll also create an enum corresponding to the tiles in the tile set so I can refer to them by name to keep the tile index okay so now let's make some variables to hold level information the level number an array which will actually be an array of arrays to hold a type of each tile and an array to hold the rooms in the size of the current level let's also go ahead and save references to some nodes that we'll need to refer to often the on ready keyword basically means this value is assigned after the node and it's children are added to the scene otherwise the child nodes might not exist yet to get a reference to them this dollar sign notation is just a shorthand for referencing a node by a relative path since the tile map and player are both direct children of the node running this script I'll just use the names of those nodes let's also add a couple of game state variables the player's tile coordinates and score now back in that ready function let's make sure we get a new random seed each time we play so the levels are different every time and this build level function doesn't exist yet but I'm about to create it first let's clear out the rooms map and tile map in case this isn't the first level and there's already something there then let's look up the size for this level now I'll just fill out this array of arrays I'll make everything start as stone and I'll set that in the tile map too if I just run this now you'll see the levels filled with stone perhaps this is where I should clarify I'm going for a somewhat traditional roguelike map with rooms connected by corridors I could just do a lot of random trial and error until I get room placements that fit but I'm going to try something more methodical I'll track the unoccupied space and only try to put rooms in that space to start I'll have just one region that leaves a two tile margin around all sides of the level that so I can put doors and corridors on any side of a room and still have a one tile border of stone walling in those corridors then I'll look up the number of rooms loop that many times and pass the free regions list to an add room function that doesn't exist yet add room will modify the free regions list so we'll check if it's empty and stop trying to add rooms if it is okay so how do we add a room to start let's pick a region from the list at random R and I gives us any random integer but using modulus allows us to constrain that to an upper bound we specify in this case the size of the free regions list I'll be using the same method a lot for picking random numbers throughout the level generation process then we need to figure out how big the room will be starting with a minimum dimension let's check if there's room to go bigger if so add a random amount to that size up to the extra that's available I'm doing it this way with an if and an addition because if that difference is zero module switch sugar divided by zero error now let's do the same thing with the Y size and let's constrain bus dimensions to the maximum we defined earlier now we'll do something similar with the star coordinates at minimum start X will be the start of the region but if there's excess space we'll add a random amount up to the amount of extra available and then the same thing for y and now we'll just bundle up this position and size together as a wreck 2d and put that in the rooms list now let's actually set the tiles in the map for this room I'll start with the top and bottom walls iterating across the width of the room the top balls y-coordinate will be start Y and the bottom wall will be start Y plus the height of the room minus one the set title function doesn't exist yet either but I'll get to that in a moment first let's do the same thing for the side walls except I'll skip the top and bottom tile for each side because those were done already but let's also fill in the florist by adding another loop to go across the interior width now for that Set tile function first I'll set it in the array of arrays and then on the tile map back in that add room function there's one last step to do or need to update the free regions list for that I'll use another helper function I'll need to both add and remove regions from the free regions list but you can't change an array while you're iterating through it without complications so I'll just keep a list of what to add and remove separately and process it later for each region if it intersects the room I just created will remove that region but then we'll calculate the leftover space on each side of the room minus a one tile margin that margin just makes path generation simpler because I don't have to handle the case of two rooms being directly adjacent now on each side that has enough left over space to fit a room I'll be adding a new region [Music] note that the regions I'm adding will overlap otherwise I'd be missing out on some places I could place a room because it would be across the border of two different regions and because these regions can overlap that's why I'm checking everything in the free list instead of just the one I chose to put the room in okay so now that I'm done iterating through the free regions list I can start modifying it this function is exactly the kind of thing you should probably do unit tests for because it's easy to make mistakes where you're off by one or mix up an X and y coordinate I did some minimal testing when I was planning this tutorial but testing is a complicated area on its own so for the sake of time I don't want to go into that in this video so if you run this now we'll see some rooms but now I want to connect them so let's go back to build level I'll add another helper function and implement it right below for this I'm going to use a couple of a star graphs a star is a graph traversal algorithm and super useful for path finding all we have to do is provide a list of points you can make a path through and how they're all connected thus defining a graph then a star can give us a shortest path between any two points on that graph for making corridors all the stone outside the rooms is fair game so at each stone tile is a point then the connection should be to any adjacent tiles I'll check the title to the left and add a connection if it's stone and the same for the above but I don't bother checking down into the right because when I get to those tiles I'll make the left and up connections from there and now I just need to increment the point ID so each point has a unique identifier so we'll be using that graph shortly but first we need to know what points we want to connect for that let's use a second a star graph this time instead of going tile by tile we'll keep track of which rooms are connected so we can see if there's a path between all of them I'll add each room is a point but to start with there are no connections then I'll add the connection somewhat at random until it's possible to get from every room to every other room once again I'm going to use a helper function to check if everything is connected and another one to add a random connection let's start with is everything connected first don't get a list of points which correspond to rooms and pick one to start from because all the connections are bi-directional I only have to use one starting point if you can get to everywhere from there that means you can also get to everywhere from everywhere else then for every other point or room I'll try to get a path if that fails something's not connected if we get to the end without a failure everything is connected okay so let's work on adding connections I'll be adding some more helper functions to keep this one from getting too complicated in my first attempt I just picked start and end rooms completely random but I felt like that resulted in too many connections I get better results by always picking the room with the least connections deciding at random whenever there's a tie and then I connect it to the nearest room that it's not already connected to next I need to pick where to put the doors in each room which I'll do totally random with another helper function and I might as well go ahead and implement these new functions now get least connected points starts with the full list of points least will hold the lowest number of connections seen so far and tied for least will be a list of all the points with that many connections going through all the points I'll check the number of connections if we haven't set that least variable yet or if this number is lower I'll set the new least seen and reset the list only contain this point otherwise if this point is tied with the current list I'll add it to the list and then at the end I'll just pick one at random from the list finding the nearest unconnected point will be pretty similar except instead of using these connections we want shortest distance I don't want to connect the room to itself so I'll skip that and if there's already a path connecting the two rooms I don't want to add another so I'll skip that and then the rest works just the same now for picking the door location this is we're putting a one-town gap around all the rooms makes things way easier I already know there's space around this room on every side to add a corridor so I can put the door in any wall but let's go along the width and add each tile along the top and bottom to a list of options then I'll go down the sides skipping the top and bottom row just like when I was adding wall tiles and it'll just pick one of those options at random as with a lot of things in this tutorial there are way more efficient ways we could be doing this but it's often not worth making things more complicated for speed if it's not code that has to run many times level generation will still be near instantaneous despite my own efficiencies now let's go back to the add random connection function we've got our start and end door locations and I know that stone a star graph will have a point right next to each because the rooms start out surrounded by stone so I'll just get the closest point each door then ask a star for the path between them I'm putting in a cert here because this should never fail but if it does I want to know about it that would mean I've got a flaw in my logic somewhere and now that we have the path I just need to add it to the map I'll start by setting the door tiles then for each position along the path I'll be replacing a stone with floor lastly I'll update that room graph because the two rooms are now connected I'm deliberately not changing the stone graph because I want to allow pasts intersect that means we'll get branching corridors my opinion that makes a little more interesting but it's also way easier there are lots of places you could change up this process if you want your levels to look different okay so let's run it again and now we see Doris and corridors but we can't see the whole level from where we are so let's get to work on movement there are lots of different ways we could approach this but for a game like this I think physics simulation is overkill so I'm going to do all the movement based on that map I created while building the level and because all the information I need is in the game script I'll handle movement there as well rather than putting a separate script on the player in fact I think I'll be doing this entire game with just one script however let's start by setting up an input mapping from project settings I'll go to the input map tab then add an action for up down left and right and then I'll just assign arrow keys to each but before implementing movement I want to go ahead and set the player starting location in build level I'll pick the first room in the list since it doesn't matter they're all readily placed any way then I'll pick coordinates inside the room excluding the outermost Isles because those are walls and then I'll Peck those coordinates together in a vector 2 for the player tile favourable but right now that player talent doesn't affect where the player object is for that I let another helper function called update visuals for now this is just going to convert town coordinates into pixel coordinates for the player objects position but we'll be adding more to this later if we run it now this is just ensured that we start inside a room now to respond to those input actions we just set up a lot of special function called input in general functions with an underscore or special lens of Godot calls for you in different situations this one gets called automatically for every input event whether it's a mouse movement key presses touch gestures or anything else that Godot supports first order anything that is in a press event then one by one I'll check if the event is one of those actions I just created and I'll call my next helper function here try move because the move won't always succeed for example if there's a wall on the way for now this is going to be pretty simple well calculate the new X&Y coordinates and assume the title is stoned by default so if the tile we're trying to move to is outside the map could acts like it's stone although really because of that to tell Marge and we added around the level we won't ever be in a position to try to move off the map but I'll keep the safeguard just in case I change things later so now that we've looked up what type of tile we're trying to move into let's use match to decide how to react if it's a floor we'll just update the player tile if its door we'll change it to a floor because we're essentially using the move to open it and if it's anything else we'll just do nothing you don't move and lastly I'll update the sprites position with update visuals right now we only really have to update if the player moved but later on there'll be other things to update so I'll do this either way ok let's try it out and now I can move through the level well that part was easy it wouldn't be much of a roguelike if we just have one level so let's add an exit to get to the next one for that I'll go back into build level I'll pick the last room in the list because again it doesn't matter this could be the closest or farthest room from the start it's all random chance just like with the player placement I'll pick a random location in the room excluding the outside edge then I'll just set the tile up tech coordinate to a ladder and while we're here let's go ahead and update that HUD label for what level we're on now back and try move let's add a new case for moving on to a ladder we'll increase the level number in the score now we've got a fixed number of levels we defined sizes for so if the level number is less than that size we can go ahead and build the next level but if not let's increase the score by more and then we need some way to show that you've won the game it'll add a new color rect under the campus layer and call it win then let's just cover the play area with semi-transparent black then I'll put a label in the middle to say you won [Music] and let's add a button to reset the game I want to call into my game script when it's pressed so for that I'll go to the note tab make sure I'm on the signal sub tab and double click on pressed here I'll select what note I want to send the signal to and in my case it's the game note and then alekhya doe insert the function declaration for me to restart the game I'll set the level number and score back to zero built the level and hide the windscreen now back and try move I just need to make the windscreen visible and by clicking this little eye icon next to the node in the scene tree I'll make it invisible to start now I'll test this out but I'll cut out most of the video so you can just see the end there we have it that's level progression done again there are lots of ways we could approach this at first I wanted to just use light occluders on the tiles and then use light and shadows to hide the parts you can't see but the problem is if a tile occlude slide it also gets covered in shadow itself so for example you couldn't see a wall right next to you there are ways you can kind of partly fix this but I wasn't able to get a result I was entirely happy with so I'm doing something different I'm going to create a copy of the tile map and call it visibility map then I'll just change it to a new tile set which is only going to have one tile solid black I'll be using this as a simple way to hide tiles by just covering them up I'll also go into the project settings and change the default clear color to black so if we see the background past the edge of the level these black tiles will just blend right in now back to the script right alongside that town map reference I saved I'll save the visibility map to then when I'm clearing out the tile map I'll also set every cell in the visibility map to 0 which is the only tile line defined so now let's go into update visuals I'm going to be using ray casting to determine what the player can see to start let's get pixel coordinates for the center of the players tile and I'll just define the helper function for that real quick I'm going to get this space date up front because it's sort of an interface to the physic system and that's what we'll use to do the Ray casting now I'm going to go through every tile on the level and if the visibility map has a zero tile there we'll do a ray cast but there's a complication here if we go from Center to Center some tiles will be cut off even though you should be able to see the side of it so I'm going to offset from the center of the tile to the closest corner once I've established the direction I'll just multiply that by half the tile size to get the offset okay so now I can do the ray cast with intersect ray starting from the player center and going to the point we just calculated if there's no occlusion we know we can see the tile but here's one other complication for tiles that block vision will always get a hit when casting away at it but that doesn't mean we shouldn't see it just that we shouldn't see past it so if there is an occlusion I'll check the distance between the point of the hit and the point we were testing ideally that distance should be zero if this was the tile we hit but I'll give it a one pixel margin of error because all the math under the hood has limited precision so if the ray cast hits nothing or hits only the tile we're looking at we should see it so let's clear the corresponding tile in the visibility map setting it to negative one clears the tile now we need to actually add collision rectangles to some of the tiles so the Rays have something to hit I'll do that for the wall door and stone now there's just one more problem the physic system gets updated asynchronously on another thread so sometimes these collision shapes aren't in the system yet when we start casting rays as is to fix that we'll go back into build level and instead of calling update visuals directly we can use call deferred this means it will happen after the current frame is finished at which point physics should be up-to-date let's also do the same thing where we update visuals and try and move okaythat's miscibility done it's time to add some challenge to the game with enemies so to start with I'll drag the enemy sprite into the scene this has two frames which I'm using for two different levels of enemy a wicker plastic takeout bag in a tougher garbage bag so in order to show one frame at a time I'll set the H frames to two once again I'll turn off centering so this aligns with the tile I'll also put a color rect as a child node that I'll use as an HP bar I'll make it a two pixel strip across the top and I'll just manually edit the height in the rect section I'll also make it red okay so there's one enemy but I want to place a bunch of these dynamically I want to package this up into something that I can create copies or instances of from code now let's may sound counterintuitive at first but I'm going to do this by splitting the enemy off into a separate scene Godot lets you have scenes within scenes and as many deep as you want in the game scene you can now see this clapperboard icon next to the enemy that means it's an instance of a scene if I right-click the game node I can add another instance manually if I open up the enemy scene and make changes those changes will show up in every instance automatically but for now I'll delete both of these because I'll be placing them from code to start with I'll define how many enemies are in each level now I'll load that enemy scene into a constant so I can refer to it from code and now I wanted to find a class to hold some information about the enemy again I could do this with a separate script on the enemy node but this works and I don't feel like changing it extending reference here just means this class will be reference countenance so each instance will be freed from memory once nothing has a reference to it anymore the sprite note variable will hold a reference to an instance of the enemy scene tile will be its position full HP will be the starting HP depending on the enemy's level and HP will be the current HP debt will be true or false it's not really necessary since we can just check for zero or less HP but sometimes the code just looks cleaner this way the init function is another one of those special ones Godot calls automatically this one gets called whenever we create a new instance you can put whatever parameters you want to on it we just have to make sure you specify all of them when we create an instance game will just be a reference to the route game node enemy level will be 0 or 1 in this case because they only have two versions of the enemy and then I'll also need X&Y town coordinates I'll start by setting the full HP from an arbitrary formula I just made up and then I'll set the current HP and tile location now for that sprite node we'll create an instance of enemy scene set the frame to reflect what level of enemy this is and then convert the title to pixel coordinates for the position lastly I need to add it as a child node under the game node now because nodes are not reference counted that scene instance won't go away automatically when we removed this enemy we could add a notification method here to detect when an instance as being freed from memory and get rid of the sprite note there but then it also gets called when they close the game and it can cause errors because the sprite node has already been deleted in kiddos automatic cleanup so instead a function called removed to clean up the sprite node and I'll call it myself as needed now let's go down to that current level section add an array for enemies and then back in build level when I'm clearing everything I'll call remove on all the enemies in the list and then clear the list now I just need to start adding enemies to the level after you've built all four rooms a look of how many there should be then for each one I'll pick a room at random excluding the starting room where the player is and again pick a random position inside the room excluding the outer edge however I'll do one extra thing here I'll check if the tile already has another enemy in it if it's not blocked I'll go ahead and create an instance of that enemy class passing in a reference to the game node a random level and those x and y-coordinates then just put it in the enemy list if it is blocked they could try again until I give a space that isn't but I'll just keep it simple and let it skip so there's a small chance there will be fewer enemies in the level so now if I run it you'll see enemies scattered around but they're visible even when they shouldn't be I'll fix that in a bit but also they don't do anything for that a lot a couple more functions to that enemy class first off let's add a function for when we attack will do nothing if the enemy is already dead although in practice I don't think we'll ever hit this case so now we want to reduce the HP but not below zero so that HP bar doesn't go negative well you probably wouldn't see it even if it did because the sprite would go away at the end of the frame still this might be helpful if we were to add a death animation later on speaking of that HP bar you might as well update it here well just change its width to be a portion of the tile width depending on the percentage of full HP and then if HP is zero I'll set that to true and after the players score here I'm arbitrarily deciding the points are based on the full HP just to keep it simple then back in the try move function for the floor case I'm going to check it to the tile as an enemy in it if so trying to move on to this tile will do damage to the enemy and if that makes it immediate and remove it from the enemies list but whether it's killed or not this attack will stop movement it will only allow one inmate to be on a tile so we can stop checking stopping the loop here also helps me avoid the problems of modifying a list while iterating through it then I just need to make sure I don't move the player if the movement should be blocked and I forgot to name that HP bar so I'll do that now and I'll update that score label and update visuals so now I can kill enemies in gain score but it's not much of a challenge if they don't fight back this is going to be a very simplistic AI where the enemies just move toward you and attack by moving on to you but for that we'll need pathfinding and once again I'll use an a-star graph I'll start by creating a start graph and build level but then I want to add a new function for updating the graph clear path will add a point to the graph as a point enemies can move through I'll just get the next available ID and add the point then similar to the stone graph I made for connecting rooms I'll check if the adjacent tiles are also floor and connect them if so I'll build up a list of the points I want to connect to and then I'll do the connections at the end now to get graph points for these adjacent tiles I'll be using it closest point and specifying tile coordinates this time we'll also be checking down and to the right because those points might have been already added and now we'll just loop through and make of the connections I was originally going to have a function for blocking paths - but I don't really need it the only things that could block a tile that isn't already blocked are the player or another enemy if they're adjacent to the player I'll just make that an attack and if they're blocked by another enemy I think I'll just have them stop to keep things simple we'll get to that in a moment but first let's actually call clear paths luckily we already have a function that we call every time we saw a tile so I'll just check if we're sitting at the floor and if so I'll call clear path now back up in the enemy class that's at an act function where we'll be using that path finding graph first let's get the points the enemy and player are in then we'll try to find a path between them if there is a path I'll assert that it's at least two nodes long because the enemy should never be in the same tile as the player then because enemies only move one space at a time let's look at the first node in that path after the starting location I'm converting it to a vector 2 because Godot is a star implementation uses the sector 3s to allow for 3d path finding but everything else I'm working with this 2d now if that tile the enemy wants to move to is the player tile will do damage to the player I'll implement that helper function in a moment otherwise let's check if the tiles blocked by another enemy if it is we'll set block to true and stop checking and if it's not blocked will update enemy's position now I'll have to actually start tracking the players HP so I'll to find the starting HP up front and then add a variable for current HP let's make sure you reset that when we restart after winning or losing and now I'll implement that damage player function just like with enemies I'll make sure it doesn't go below zero and if we hit zero I'll pop up a lose screen don't be just like the windscreen in fact I'll just duplicate that windowed and change a few things I'll rename it to lose change the text and I think I'll make the background color red instead of black now I just need to call that act function on each enemy which I'll do and try move because it's turn-based they move when you move well in one more detail let's update the enemy sprite positions in update visuals and update the player HP label so if you run it now enemies move an attack but they're still visible before they should be and I see I forgot to make the game over screen disappear when you click restart so I'll do that now so to fix enemy visibility let's start by going to that enemy scene and making an invisible to start then back and update visuals or use ray casting just like I did with a visibility map if the enemy sprite isn't already visible will cast array and if nothing blocks it will make it visible I also go back into the enemies act function and make it do nothing if it's not visible yet because if you haven't seen the enemy yet it hasn't seen you either so that should be it for enemies aside from dropping items which will be the next part all right this is the last section of the tutorial I'm going to start by plugging in sprites under the game node for the two types of drops cans will come in three colors with randomized effects so just like with the enemy I'll set the horizontal frame count and then I'll turn off centering I'll call it can and split it off into a separate scene then I can delete it from the scene because I'll be placing these from code paper doesn't have any variants so I'll just turn off centering rename it split it off to another scene and delete now for those cans I'm going to have three possible effects Healing poison and vision impairment to make it clear what's going on I'll add some labels to the canvas layer for each status [Music] they'll be invisible to start and then shown whenever that status is in effect I should make these new scenes for the Canon paper invisible to [Music] one more thing while we're here I'm going to implement that vision impairment with the light 2d and I'll go ahead and add that unto the player node I'll rename it to impairment I've prepared a texture for this light the size of the window with just a little bright spot in the middle in the rest black now by setting the light to mask mode this will basically hide everything worth light texture is black in all just the position so it's centered on the player we can see what it looks like by running it now but I'll turn off visibility on the light by default so it has no effect now back to the scripting first let's set a number of items to be scattered around each level from the start and then let's load those new scenes into constants just like we did with the enemy I'm also going to make a list of functions to call for each type of can I'll be implementing these later I'll also be making a class for items similar in some ways to the one for enemies it'll extend reference hold a reference to a node instance will create have a tile position and then I'll just add a variable for whether it's paper or not so don't have to make separate classes for the two types of item for the net function we'll take a game reference coordinates and whether or not it's paper then we'll just copy is paper to the member variable set that tile and now we need to create an instance of one of those scenes I like using ternary ifs for this this works almost like a sentence it'll be a paper scene instance if is paper otherwise it'll be a canned scene instance then I'll pick a frame that random based on the number of H frames which was three for the cans but just one for paper doing it this way makes it easy to add more in the future and then need to set the position of the new node again converting tile coordinates to pixels and in case you have multiple papers are canceling the same tile I want to offset them a little at random so you'll usually be able to tell if there's more than one and then we've got to add it to the scene under the game node and once again I'll add a remove function for cleaning it up now let's keep a list of items in the level while we're here I'm going to go ahead and declare a variable for the random mappings of effects for each cam type I'll set those in build level by just copying that list of functions and calling shuffle to randomize the order now let's place some items randomly around the level first I'll look up how many to add and then for each I'll pick a random room this time including the starting room and just like with other things I'll pick a random tile within the room and then let's put the new item instance in the items list to create it only to pass a reference to the game node those random coordinates and the true or false for that is paper variable I'll choose the random if my random 0 or 1 is 0 will be paper otherwise I can then down an update visuals this will be almost an exact copy of the enemy visibility code just with items instead of enemies [Music] and now we need to make the items actually do something in try move let's pick up items whenever we move to a new tile the pick up items function will be fairly simple anything that gets picked up needs to be removed with the list so make a separate list for that because again I don't want to change the list while we're iterating through it for each item if it's in the tile we just moved to let's first check what type it is if it's paper will heal the player by 2 points and add one to the score just another arbitrary decision that could be tweaked later to make the game easier or harder if it's not paper we'll call a function we can call functions by a string name using call and I'll look up the function name from that can types less we shuffled when we built the level I'll just use the sprites frame index to know which can type it is lastly we won't remove this item so I'll call remove to clean up the sprite note and then add it to the list to be removed from the items list later and once we're done iterating through the items I can remove items from the item list now all that's left is to actually implement those canned functions first we'll add some variables to the game state to keep track of how many turns these are in effect they don't add those functions let's make vision-impaired last for 10 turns this will turn on visibility for both the label and the light node heel of her time will last for 3 turns that make the healing label visible and poison will also last through 3 turns and show the poison label let's also make sure to reset these when we restart after we win or lose and now back in the try move function I'll react to these variables if impaired turns is more than zero I'll subtract one and it doesn't really have an active effect the impairment is handled just by that light node being visible but when we hit zero impaired turns we should make both the label and the light invisible again for poison will again check if it's still in effect and decrease by one but this time we'll damage the player for each turn that it is in effect and when it reaches zero I'll just turn off the label healing works exactly the same way except there's no need for a function for healing I'll just add to the player HP directly okay at this point in the video I realized I forgot a few small things first I don't want that vision impairment to hide the HUD so I'll select everything in there and clear the light mask so it doesn't react to the light and then I forgot to make the enemies drop items when you kill them so back in the enemy take damage function I'll pick a random number of papers to drop up to 1 less than the enemies HP and for cans I'll pick a number zero through two again these are very arbitrary decisions lastly I forgot to clear items out when generating a new level so I'll do that now and that's it we've now got a simple roguelike with procedurally generated levels randomized item effects and turn-based melee combat sort of and uncovering the level as you explore again there are a lot of ways we could have done things differently and in some cases better and the sky's the limit for adding more variety and mechanics I highly encourage you to experiment for me I've found that's the best way to learn however I've decided not to continue these tutorials on a monthly basis it's just too time-consuming for me so it might be a long while before you see another one but if you're interested in seeing more of what it takes to make games I encourage you to check out my game dev vlogs which I'll still be doing weekly and if you liked any of that music in the background you can hear more of my music at soundcloud.com slash shadow bait it's been a while since I've posted anything there but I hope to change that soon a lot of what you just heard we're unfinished works in progress that I'm eager to spend more time on if you'd like to support what I do you can do that at patreon.com slash thought quake and for those who already are patrons I really appreciate it special thanks to David nor koa and if you'd like to support a different way it also really helps if you hit that like button here on YouTube spread the word and subscribe if you haven't already and as always thanks for watching you
Info
Channel: Thoughtquake
Views: 87,226
Rating: undefined out of 5
Keywords: roguelike, tutorial, game development, godot, gdscript, scripting, programming, game design, lesson, class, course
Id: vQ1UGbUlzH4
Channel Id: undefined
Length: 42min 15sec (2535 seconds)
Published: Sun May 19 2019
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.