2D Character Animation - (2021) How To Make A PLATFORMER GAME (Love2D) 3/6

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
in the last video i gave some homework which was to create a double jump for the player the way i approach anything that i have never coded before is to first define what it is a double jump is an extra jump that can be used when the player is not grounded that means that a double jump cannot happen when the player is running around on the ground the double jump can only happen one time so making a boolean that stores if we are able to double jump seems like a good idea the double jump can be performed once every time the player jumps to accomplish this we should refresh it when the player lands let's start by creating a new variable i will name it has double jump and set it to be equal to true next i will move the and self grounded to its own if statement because we want to be able to double jump on the same keys as we perform the normal jump with next we can go ahead and remove the self.grounded equals false we don't need it anymore since we implemented the end contact code in the last episode now we can go ahead and create an elsif statement that will check the has double jump variable if it's true then we will set it to be equal to false and set the y velocity to be equal to the jump amount i want the double jump to be less powerful than the normal jump so i will multiply it by 0.8 now we need to set pass double jump back to true in the land function if we run the game you can see that we are able to double jump to make our jumping feel better we are going to add a grace timer that will allow the player to jump if it was grounded recently if you prevent the player from jumping the very next frame when it is no longer on the ground it just feels bad to do this we need two variables one that keeps track of the grace time that remains and one that stores the total duration i will name them grace time which will be 0 by default and grace duration that i will set to be 0.1 seconds inside the jump function we will add an or statement that will check if the grace time is greater than 0. this will allow the player to jump even if it is not on the ground so long as it has grace time we also need to remember to set the grace time to 0 if the player has jumped next we need to create a function that decreases the grace time when the player is not on the ground i will name it decrease grace time remember calling it in the update function inside we will check if the player is not grounded and reduce the grace time by delta time if that is the case finally we need to reset the grace time when the player lands we can do this by setting the grace time to be equal to grace duration inside of the land function this is a bit difficult to show off so you will just have to take my word for it but it feels a lot better now you should experiment with different values on the grace duration and see what you find works best in your opinion let's make the player look a bit more interesting by adding some animations to it first of all i'm going to create a new function named load assets in the player file this is just to break things up a bit so we don't end up with a massive load function remember to also call it in the player load function next we create a table named animation and create two new variables inside of it timer that i will set to be equal to 0 and the rate that i will set to be equal to 0.1 our player will have 3 different types of animations which will all animate at 10 frames per second if you want to have different speeds for different animations you will need to create these two variables for each of the animations i will start by creating the run animation make a new table inside of the animation table and name it run inside of the run table we are going to create two new variables total which we will set to be equal to 6 the total number of frames in this animation the second variable will be named current which we will set to be equal to 1. it's going to keep track of which frame we are currently on now we can go ahead and load in the animation to do this effectively we will use a loop a loop is a way to tell our program to do something specific multiple times in this case loading images we will use a simple for loop to create it we type the for keyword then we need to declare a variable and set it to be equal to a number value in this example i equals 1. next we need to add a comma and type another number this will be the target for a loop in our case we can use the self.animation.org variable similarly to adding a then at the end of an if statement we will add do at the end of a for loop let's check what happens inside of this loop and what the local variable i is equal to we can do this by simply printing the value i with the print function as you can see i gets incremented by 1 for each loop we could rename this variable to anything for example number the important thing is that we declare a variable with a starting value for the loop if we for example set the value to be equal to -2 and run the program you can see that we go from -2 all the way to 6 so the second number is the target value not the amount of times it will loop there is a third argument that we can set which is the amount that i will increment each loop if i set it to be equal to 2 and re-run the program you can see that it now increments by 2 each loop i will set the i variable back to be equal to 1. one way to insert data into a table is by using brackets and a number for example let's create a table named people we can fill it with a bunch of names by typing people brackets 1 equals bob and people brackets 2 equals fred this stores the values at the specified index instead of a named variable let's use that to store our images into a table named image that we will store inside of the run table outside of the loop we would need to store the frames individually by setting run.imagebrackets1 to be equal to love.graphics.new image and passing in the path to the first image for each new frame we would need to increment the index that we stored the image at as well as the number in the file name a better way would be to do this inside of the loop and have it be automated since we know that i will count up to the total number of frames we can use it to load the animations it will be very similar to the manual way except that we replace the hardcoded value with the variable i inside of the brackets to do the same thing for the file path we need to first break it up into two separate strings assets player run slash and dot png between these two strings we will concatenate the i variable by adding two dots on each side of it this combines them into one string i will repeat this process for the two other animations that i intend to use idle and air the only difference here is that those only have four frames so we need to make sure that we set the total variable to be equal to 4. next we will create a new variable named self.animation.draw this will store the current frame that we want to draw initially we will set it to be equal to the first image of the idle animation we want to do this so that we can get the dimensions of the asset to store the dimensions we will create two new variables self.animation.width and height we will set these variables to be equal to the self.animation.draw variable and call the love functions get width and height on them this returns the width and height of the image now we can go ahead and draw the player even though it doesn't animate yet it is still a good idea to check that we are on the right track down in the player draw function we will use the love.graphics.draw function the first argument it takes is the image that we want to draw we want to use the animation.draw variable next we need to pass in the coordinates self.x and self.y if we run the game now you can see that the player is being drawn but there are two problems first of all it's quite heavily offset and second it's very blurry the reason the sprite is offset has to do with the fact that the image is being drawn from the top left corner while our players x and y coordinates are at the center of the physics object we can fix this by offsetting the image by half of its width and height while we could do this by modifying the x and y coordinates that we draw it on we might as well do it properly and change the origin point to be able to offset the origin point which is done with the 7th and 8th arguments we need to first set the arguments that come before them the first of these arguments is the rotation which we will set to 0 meaning no rotation next we need to set the x and y scale of the image to 1 meaning no change of scaling now we can reach the origin point offset and set them to be equal to half of the image width and height respectively if we re-run the game now you can see that it looks a lot better next we need to fix the blur this is caused by anti-aliasing which kicks in by default when we scale images this is normally fine except when you're working with pixel art to disable it we can call love.graphics.setdefaultfilter and pass in nearest and nearest this only applies to images that are loaded after the fact so we should call it at the top of our main.lua file now when we run the game you can see that our player is looking sharp it's time to make the animation well animate to do that we are going to create a new function named animate remember calling it in the update function this will handle how often the animations will swap images and we will use the timer and rate variables to set this first of all we will increment the timer by dt then we will check if the timer is greater than the rate if this is the case then we need to set the timer back to 0 so that it starts over now we can create a new function and name it set new frame this function will take care of actually updating the image that we want to draw we want this to occur when the animation timer resets so we will call it inside of the if statement in order to have this function being able to handle all of our different animations we will create a local variable named anim and set it to be equal to self.animation.run the local and imp table is now a reference to the self.animation.run this is really important to understand we did not make a new table and copy over the values if we do any changes to the anim table those changes are mirrored in the original animation.run table when this function is called we want to increment the current frame that the animation is at but only if the current frame is not the last frame if it is the last frame then we want to set it back to 1. to accomplish this we can make an if statement that checks if the anim dot current is smaller than an image.total if that is the case we can increment the current frame by 1. next we make an else statement that handles when that is not the case and set the current frame back to 1. all that remains now is to set the image that the player should draw to be equal to the current image of the animation we do that by setting the self.animation.draw to be equal to the anim.image in brackets and m.current if we run the game now you can see that the player is animated however when we walk towards the left it does a cool but non-intended moonwalk to fix that we will create a new variable for the player named direction and set it to be equal to right then we should create a new function named set direction that will handle changing the direction variable as usual make sure that you call it in the update function now we can check if the player's x velocity is smaller than 0 meaning we are moving towards the left and set the direction variable to be equal to left to set the direction back to the right we can create an elsif statement that checks if the x velocity is greater than zero if that is the case we set the direction to be equal to right this sets it up so that the player's direction does not change if you stop moving next we can make a local variable in the draw function and name it scale x i will set it to be equal to 1 by default and replace the fifth argument in the player draw function which is the x scale with the local variable that we just created now we can do a simple if statement that checks if the player's direction is left and set the x-scale variable to be equal to -1 a negative scale will flip the image and as you can see the player is now always turned towards the direction that it's moving in we have two more animations to add to the player but in order for us to be able to draw the correct animation at the correct time we need to have a system in place that determines the state of the player let's start by creating a new variable named state and set it to be equal to idle by default next we will make a function and name it set state remember calling it in the update function if the player is in the air we want the player's state to also be r but what can we use to determine if the player is in the air or not well our grounded variable already keeps track of this so all we need to do is type if not self grounded then and set the state to be equal to r next we need to determine if the player is idle or moving we can do that by checking if the x velocity is equal to 0 and set the state to be equal to idle if that is the case finally we can make an else statement because if the player is not idle and not in the air well then the player must be moving here we set the state to be equal to run to check if this is working we should go ahead and print self.state if we run the game you can see that the state that is being printed is correct it's always a good idea to validate that your code does what you intend it to do as early as possible you will save yourself a ton of debugging later down the line remember that we could use brackets on a table to reach a value at a specific index we can use brackets to reach a key or in other words named variables in a similar way in the set new frame function we can reach the run table by typing brackets and the string run inside of it now if only we had a way to get a string of the current state that the player is in well we actually do that's why we made the self.state variable simply insert it into the brackets run the game and enjoy the fact that all three animations are working it's almost like magic while we are in the game i want to demonstrate an issue that we have not yet resolved if you stand below a platform and jump the player seems to get stuck for a brief moment this is due to the fact that the y velocity is unaffected by the fact that we collide with the platform because it's above us our begin contact code is only checking and resolving collisions with objects that are below the player fixing this is very simple inside of the if statement that handles when the player is the a fixture we create an elsif statement that checks if ny is smaller than zero if this is the case we set the player's y velocity to be equal to zero we need to do the same thing but flipped for the times when the player is the b fixture check if ny is greater than 0 and set the y velocity to be equal to 0. if we rerun the game now you can see that the player no longer gets stuck one of the coolest things with programming is that you can approach and solve an issue in multiple different ways i want to show you an alternative way of doing the code for the acceleration and friction which uses a lot fewer lines of code i have two reasons for doing it this way first of all i believe it is easier to understand the concept of the original method that we used finally i believe that it is easier to understand this new way that i intend to show you if you did it the other way first let's look at the first if statement inside of the player move function basically it compares two values the maximum allowed speed and the x velocity that we would have if we added acceleration to it and then it picks the lowest of the two in the lua math library there is a function that compares two values and returns the lowest one it's called math.min this means that we can set the x velocity to be equal to math.min and pass in x velocity plus acceleration multiplied by dt as the first value and the maximum speed as the second when it comes to moving towards the left it compares the same values except that it picks the greater value for this we can use the math.max function this function works the same way as math.min except that it returns the larger number similarly to the other example we set the x velocity to be equal to math.max and pass in the x velocity after we have applied acceleration to it and the negative maximum speed now the move function is a lot cleaner with fewer lines of code while still having the same functionality we can shorten our friction code in the same way that will be your homework so go ahead and try to do that until the next episode i will show you how it's done then so don't worry if you struggle or run into any kind of issues thank you for watching if this video was helpful leave a like and subscribe so you don't miss the next episode i read all the comments if you have any questions don't hesitate to ask the full source code of this episode is available in a link below [Music] you
Info
Channel: DevJeeper
Views: 2,728
Rating: undefined out of 5
Keywords: programming, coding, lua, love2d, game development, Platformer games, platformer, platformer tutorial, unity, godot, how to make a platformer, tutorial platformer game, tutorial platformer, scratch, game dev, programming tutorial, Tiled, how to make games, how to use love2d, programming in lua, Adding animation
Id: Is0cmmZ4sAU
Channel Id: undefined
Length: 19min 42sec (1182 seconds)
Published: Sat Nov 07 2020
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.