Transcript for:
Platformer Game Development Overview

[Music] a hello there in this tutorial we are going to create a platformer with an Overworld the game is going to be quite Advanced there will be a variety of enemies and traps a complex movement system that includes wall jumps and semic collidable platforms and there will be a ton of animations all of the levels and the Overworld are made in tiled meaning you can extend the game on your own easily I will talk about that throughout the video in fact to make this game I will be going through three major stages we will start with the basic logic of a platformer then we will add the proper Graphics enemies and the UI and finally we will create the Overworld if you only care about the basic platform of logic or the Overworld you can watch these parts in isolation as the code is very modular on top of that I will cover vital math concepts like Collision detection Delta time adjusted movement and trigonometry throughout this tutorial however I would very much recommend you to practice them Beyond this video and a really good way of doing that is via the sponsor of this video brilliant brilliant offers a huge range of courses that cover fundamental concepts for game development like vector math and trigonometry for every subject they cover both the absolute Basics and more advanced concepts meaning you can either start entirely from scratch or jump straight into more advanced topics their lessons are highly interactive and Incredibly wellmade and they also break up larger topics into more digestable chunks that you can focus on and practice learning that way is much more fun and intuitive than reading a book or passively following a lecture they also cover topics like data science artificial intelligence and Science and Engineering more broadly so if you want to learn how llms like jbt work or how robots are built then brilliant is for you you can sign up at brilliant.org Clear code and the first 200 people get a 20% discount on a premium subscription also there's a 30-day trial period to just test it so not much to lose now with all of that out of the way we can get started with the first part of the game which will be the basic outline of a level we have the main player and the collidable parts of the level we are also going to add some very basic movement but it's not going to be terribly sophisticated now before we get started let's talk about the folder setup at the moment the basic setup looks like this we have four folders and two are really important for now we have code and we have data audio contains audio files and Graphics is a very Mighty folder there's a ton of stuff in there but for now it really doesn't matter what we care about is the code folder and in there we have five files although in our case we only care about Main and settings for now I will talk about support and timer later they contain basic Support classes and debug is just a way to show some information inside of pame it's not going to be part of the actual game finally inside of data we have all of the level data for example if you look at levels we have a whole bunch of levels Zer to five are the levels in the game and Omni is a general level that we are going to use as a testing ground if you open Omni TMX you can see inside of til that we have a basic level doesn't look terribly sophisticated but this level contains all of the specific elements that are going to be present in every other level meaning if we can build this level we can create all of the other levels and by the way I have us tile to create all of the levels which is an open source and free tool that you could use as well I have actually made a whole tutorial on this although for the purposes of this video you are not going to need it unless you want to create your own levels then you should probably check it out anyway with that we have this setup which means inside of the code folder I want to open Main and settings once you have done that we have a completely empty file from main.py and if you look at settings.py we have a couple of things nothing too drastic Z layers we don't need at all and the really important part is that we are importing py game and sis at the top and then we are also getting py game. ma. Vector 2 as a vector we are going to use a lot of vectors throughout this video hence having easy access to them is really nice also before we continue I am using py game- CE which you are importing via pame but the actual module is called py game- C over the last year the pame community had a bit of an issue and kind of split although split isn't the exact word basically all of the developers bar one went onto pame C and the original pame is now very much outdated the pame version that you want to use is called py game- CE it is entirely backwards compatible and inside of the code you are simply importing it as py game and all of your old Pro projects will work just as before but you are getting a lot of extra features it also runs faster there really isn't any reason to use the old pame anymore the new one is significantly better you want to import pame and CIS and all of that happens inside of settings finally we have window width and window height two basic numbers then we have a tile size and animation speed so with that we can work inside of main.py and now we can work with some actual logic to get started we have to import everything from set settings so from settings import everything after that I want to create a class called game this one is going to run the basic logic in there we will need a Dunder init method with self and nothing else first of all in there we need pygame do init by the way we have access to pame Via this from settings import everything basically we imp putting py game in there and then we are passing it through via this line after after that we have to create self. displore surface that we create with pygame do display do setor mode and then we need the width and the height of the window which we're getting from settings window WID and window height I can simply copy it in there after that you can also set a window title this you do with py game. display. setor caption and then a string in there gives you a window title let's call this one super pirate world after that I want to create a method that I will call run once again no need for custom parameters but you will need self in there we want to create the game Loop meaning while true and then we want to check for event in tame. event doget and in there for now we only want to check if event. type is equal to P game g. quit P game. quit being the short hand for the x button at the top of the window meaning if this is the case we do want to close the window after that we can run py game. quit and then sis. exit both of these modules we are once again getting from settings.py with that we can check for closing the window the last thing that we have to do is py game. display. update and that should cover everything to get started all we have to do now is to create one instance of the class so game is game and then call game. run and we should be having a basic black window and that is looking really good also since we are going to have lots of files later on I want to wrap this inside of the if statement if Thunder name is equal to the string Thunder main that way we are making sure we don't run anything accidentally let's try this again and that is still working perfect now that we have that we can start working on the level and this should be a separate python file it is going to contain quite a bit of code so I'm going to create a new file and call it level. high and there just as before we want from settings import everything and then create a class called level no need for inheritance but we will need a thunder init method for now it's just going to use self but there's going to be quite a few arguments later on now in there first of all we want to get self. display uncore surface that way this class can also draw on the display since we already created the display surface this one we get with py game. display. getor surface and all that this one is going to do is it gives us this display surface and that we can draw on as a matter of fact this we are going to do right away let's do it via a run method no need for custom parameters for this one and all I want to do is self. display surface and then fill it with a color the color doesn't really matter let's go with gray now that we have that once we are calling the run method we should be able to fill the entire window with a gray color for that to work though we have to create and run the method and that we do inside of main.py all we have to do is from level import level and then create one instance of this inside of an attribute in the game class the name of the attribute that I have chosen is self. current stage because later on the current stage could either be a level or the Overworld because of that I not going to call it self. level although you could but it would be a bit weird anyway all we want to do is create one instance of the level no need for arguments at this stage and after we have that before we are calling py game. display. update I want to get self. current stage and then call the run method if I now run all of this we're getting a gray background and this is because of that line if you change it to Red for example you would get a red background that is leaving us with two major things to work on number one we need a basic outline of the player and number two we need the main parts of the level I.E the parts of the level that the player can collide with the second part is a bit easier so let's get started with that if we are looking back at Tiles the only tiles I really care about are inside of the terrain layer meaning everything else at least for now I want to hide and there is quite a bit more none of this I care about the only tiles I care about are these also for now we are not going to have any graphics all of these are going to have one color later on we are going to work on the proper graphics for that first of all we have to import the TMX file and that happens inside of main.py we are doing all of that via a module that is called P TMX more specifically we want to import a sub module from that which we do via from PMX do _ pygame and then import load P game if you run the code there shouldn't be any error messages and that is looking pretty good now this module you have to install via pip if you have gotten an error message this module doesn't exist you have to do that just to make sure you know how to do it you would open on Windows the Powershell or on Mac OS the terminal and simply type pip install py TMX and if you're on Mac OS this would be pip 3 install py TMX that's all you have to do afterwards your computer is going to take care of the rest now with that we can import PMX files and this I also want to do inside of the game class and then later on pass the information into the level the way I handled that is I created a dictionary with self. TMX Maps because later on we are going to have quite a few for the keys I'm going to use integers so the first level is going to get the integer zero the associated value is going to be the load P game that we are getting from py TMX in there we now want to import one py TMX x file the only argument we have to pass into load pame is a path to the file and you could on your operating system simply type the relative path so in my case this would be Das Dash then slash then data then levels and then Omni do emx after we have that let me simply print self do PMX maps to see what we get and we are getting it dictionary with a tile map inside this one is going to contain all of the data from the level however before we continue with that we don't need the print statement anymore this import statement is not ideal because depending on your operating system you might need a different path string for example some operating systems use a forward slash or a backwards slash and this isn't exactly consistent which can be really really annoying the way around rounded is an inbuilt python module which I want to import via from os. paath import join join is a really powerful method and it gives you relative paths on your specific operating system the way you are using it is via join and then in there you add the specific nodes of your path so in my case I want to go up one folder then to data then to level and finally I want to get Omni TMX and with that I don't need the original string anymore the end result should though still be the same but it's a lot more reliable that being said though if I print all of this then you can see what's going on all we are getting is the same path that we have written before with that we have imported the data for one level and this I want to pass into self. current stage I.E self. TMX maps and I only want to pass in this one bit of data and to make that work I have to give the level class another parameter let's call it TMX uncore map and after we have that we can inside of the level create another method that I want to call setup which will need s and then the TMX map and for now let's simply print the TMX map and call self. setup and pass the TMX map right through if I now run m.p we are getting the tile map printed and all of that happens inside of setup with that we can contain all of the logic inside of this method and this method is going to be very chunky because the level is quite complex but we'll go through it step by step it's not going to be too bad first of all we want to Target one specific layer inside of this tile map map and for that we will need TMX map and then get uncore layer uncore by uncore name the layer we want to get is called ter Rin if I go back to titled really quick the only layer that we have visible right now is terrain so we are importing all of this data and since we're working with a tile set we want to add dot Tiles at the end and don't forget to call it what this allows us to do is to put all of this inside of a four loop with four x y and this surface and don't forget in whatever we are getting returned from all of this the end result is that for each of the tiles you can see inside of the tile map we get the X position so the columns the Y position I.E the rows and then the surface that would be the associated image for each of them if I now run main. pi and expand the print statement you can see that we always have an X position a y position and a surface that we can use for the level Graphics although for that to work we have to create a class that can actually be displayed I.E we are going to need a Sprite and we are going to create quite a few different Sprite classes I'm going to St of them in a separate python file which means I want to create Sprites dopie in there once again I first of all have to import from settings and I want to have everything that way we have access to py game and the only class we want to create for now is called Sprite a very simple class and this one needs inheritance pygame dos sprite. Sprite and do pay attention to the second Sprite having a capital letter for the s for this class we want to have a Dunder init method with self the position where we want to place it the surface that we want to display and finally we want to have the groups and as a reminder we are displaying Sprites by putting them into groups and then the groups are displaying and updating all of them we're going to handle that in just a second first of all though we have to call super. Thunder init to initialize the parent class I.E py. sprite. Sprite and the one argument you can pass in here is the groups that way you are assigning this Sprite to a group when you are creating it which is super handy after that each Sprite wants to have self. image and self. wct the image is what we are going to display and in our case for this part of the video we don't care about specific Graphics we just want to display the basic outline so for this part you could be using the surface and then you would be displaying the graphics from tiles I.E the stuff you can see on here however I don't want to do that I simply want to create a pame do surface with the size of the tile size and that we get from settings I want to copy tile size and insert this twice once for the width and once for the height and just to make sure we can see what's going on I also want to fill this image so self. image. fill and let's say we want to fill it with a white color after that we will need self. rect and this will determine the position for that I want to have self. image and then getorf wrecked if you have used py game in the past you probably know get wrecked but nowadays you are using get F wck and this one creates a floating Point rectangle which is much better in terms of movement inside of py game basically a normal wct can only be placed via integer values so 0 1 2 3 and so on but an frre is placed via floating Point values meaning you can have 1.15 something which is significantly more precise and when it comes to movement much more reliable but in terms of arguments you are still using them like any other rectangle which means when we are creating it we want to decide which point we want to place and in my case I want to place the top left and this one will need the position that position we are getting from the parameters and passing it through cool with that we can go back to level. Pi and first of all we need from Sprites and I want to import this Sprite after that we we will need to create one group and later on we're going to create a couple of groups so let me add a comment here to organize the code the group I want to create for now is going to be stored inside of an attribute and I'm going to call it self. all Sprites every single Sprite that we are going to create will be inside of this group and Via that they will all be drawn now the group we are creating with pame DOs sprite. group and if you spell it correctly that would also help quite substantially all right with that we have a group that means next up we can turn this bit of information into an actual class we want to create a Sprite class and for that we will need a position a surface and a group let me copy the parameters the position is going to be X and Y at least for now the surface we can pass right through so this one doesn't have to change at all the groups for now is going to be do all Sprites with that we have Sprites inside of a Sprite group all we have to do now is inside of the run method called self. or Sprites do draw and then for the argument we have to specify on what surface we want to draw I want to draw on self. displore Surface also the background should be black red is a bit distracting so with that I can run main. pi and we can see some really messy stuff in the top left the reason for that is if you look at level. Pi this X and Y is not pixel position it's position inside of a grid these numbers would be something like 0 1 maybe a 10 maybe a 15 what pame is looking for is an actual pixel position so for example our window would be 1280 x 720 pixels if you move by 10 pixels it's going to be very little movement fix that we have to convert the grid position to pixel positions and to meth for that is super simple we just have to multiply both of them with the tile underscore size also I should be adding the multiplication sign now if I run all of this again we have a proper looking level comparing all of this to tied you can see what's going on we are basically creating this part right now cool that covers the first part next up then we can import or rather create the player now for that let's have a look at Tiles the player itself is inside of the object layer if I make this one visible you can see that we have a player this little icon is wherever the player is going to start and this if you look in the top right is an object layer I.E we are working with an object we can move around very easily we're not bound by the grid coordinates but other than that the differences are quite minor which means back inside of level. Pi I need another four loop I want four obj in TMX map and then get layer by name once again the layer I want to work on right now is called objects for this one since we're not working with tiles you don't need the dot Tiles at the end instead we're getting an obj object let's actually print what we're getting getting print. obj if I now run all of this we are getting a whole bunch of obj objects the only one that we care about at the moment is tiled object 15 the player player by the way is the name of this object which is super handy because we can for example obj do name and check if this is player also there should be an if at the beginning if that is the case we can for example get the player position let's say obj X and obj Y if I now run m.p again we get the player start position for X and Y now we can create a player and this part is going to be your exercise for this section of the video I want you guys to create the player inside of a player. pi file it's going to look for now very similar compared to the Sprite we have just created also the color of the player should be red and it it should appear in the position that we have set entitled pause of you now and try to work this one out on your own I want to start by creating a new python file that I'm going to save as player do PI we will need from settings and I want to import everything after that I want to create a class called player which like Sprite is going to inherit from py game. sprite. Sprite after that we will need a thunder init method with self then we will need the position of the player and we will need the groups that the player should be in inside of the init method first of all a really easy thing to forget is to initialize the parent class and there we can also pass into groups after that we will need self. image and self. wct and this is the part we have already seen for the image I want to create pame do surface this one wants to have a two bu with a width and a height the specific numberers here for now really don't matter that being said if we open the player inside of Photoshop we can measure the size of his body and I think this should be roughly the player size so we have a width of 48 pixels and height for 56 those are numbers we can use for now but later on we are simply going to use the dimension of the image to determine how large the player will be anyway for now I want to have a width of 48 and a height of 56 that way at least we have a basic idea of how big the player is going to be after that we will need the rectangle and for that we need self. image. getorf with the top left being the position that we're getting from the parameters cool with that inside of level. Pi we have to import from player the player class after that inside of the if statement we want to create one instance of the player class and for that we will need a position and a groups the groups is easy we only have a single group self. all Sprites the position is going to be obj dox and obj doy and I should have mentioned earlier but obj or already gives you the pixel position meaning those numbers we don't have to multiply with tile size but anyway if I now run may not P again we cannot see anything and the issue that we have is that the background is black and the player itself is also black and black on black is very difficult to see to fix that we want to get self. image and then fill all of this I think I said with a red color but choose whatever color you want it really doesn't matter if I now run all of this again we can see the player that feels much better cool with that we have covered the exercise and this already let me run it again is a pretty good start to get ready with the level now to go a bit further I also want to add some basic movement but for that we need a few more things first of all though I want to get rid of the print statements because they are going to annoy me most importantly we want to work inside of player. p in there we will need two things an input method that doesn't need any parameters and for now I simply want to add pass in here after that I want a move method and this one also needs only self for now and pass so we don't get an error message also what we have to figure out is how to call all of these methods and for that we will create a third one called update now this update is kind of special because it's going to be called via the group the way it works is you want to call self. orites and then the update method and this update method is going to call all of the update methods inside of the Sprites that are inside of it right now the player has an update method so the group will call this method and to demonstrate let's print player if I now run m not Pi we can see we have player in the bottom left that's really good now in there I want to call Self Dot input and self. move since both method have no real Conta this isn't going to do anything but it's a good start now we can create some input to get input we first of all have to create a local variable called key or rather keys and then we want to get all of the keyboard input that we get with pame do key then getor pressed this is going to give us all of the currently pressed Keys which we can use with for example if if keys and py game. Kore right then we would for example print right if I now run main. pi and I press to the right we're getting right now we have to figure out how to actually use this to move the player and for that we will need a few more things let's all of this under common let's call it movement first of all I will need self. direction which is going to be a vector or more specifically a vector to 2 this Vector we can get because in settings. pi we have renamed the vector so instead of a vector 2 we are simply creating a vector by default the values are going to be zero and zero for X and Y which is exactly what I want besides that I also want to set self. speed for now let's say I want to go with five after that we can work inside of the move method and in there I want to get self. rec. Toop left plus equal self. direction multiplied with self do speed basically we are taking the current position of the player and we are increasing it by the speed in a certain direction that's all that's happening here now right now if I run main. Pi this is not going to do anything simply because we don't have a Direction but if I change this Vector to one and zero and Run the game again the player is moving very fast to the right the five here is very very fast let's say 0.1 might be better there we go this is looking a bit better and in just a second we are going to work on frame rate Independence that way it doesn't matter what the frame rate of the game is our players going to move at a constant rate first of all though I want to change direction back to zero and zero because the player should only move in the direction that is being pressed on the keyboard and for now the player can only move left or right so we want to check the keys for py game. K right and K left and usually how I approach these things is before we getting input we are creating another Vector that I usually call input vector and this is a completely new Vector with no values so the values by default are zero and zero meaning on every frame of the game the input Vector starts with Zer and zero and after we are doing that we can update it depending on what the player is pressing for example if the player is pressing on the right arrow key then I want to get input vector dox and increase it by one however if the player is pressing to the left then I want to subtract it by one with that system if the player is pressing both we end up back on zero so we have no movement whatsoever all we have to do at the end of it is set self. direction to the value of the input Vector if I now run may not Pi again and if I press left or right the player is moving so that's a really good start although I want to make one more update I don't just want to get the input Vector I also want to normalize it that just means that the length of the vector is always going to be one that way we are ensuring that the direction is only giving as the direction the speed is what's actually setting the speed I suppose I should do an example imagine self. Direction has a value of 1 for x and 0 for y and that we are multiplying with the speed which in our case is 0.1 the result is going to be another Vector that is going to be 0.1 and0 that would be the actual Movement we would move by a unit of 0.1 however now imagine that we get a different kind of vector that wouldn't be ideal where we have an X of two and Y could still be zero it doesn't really matter if we multiplying this by the same speed IE 0.1 the result would be 0.2 and 0 the problem with this second result is that the movement speed would be twice as fast as the original result and that we prevent with normalize it simply keeps the direction of the vector but it ensures that they all have the same length however this we can only do if the vector has any kind of value imagine you want to normalize a vector with zero and zero it doesn't point in any direction you simply couldn't normalize it as a matter of fact if you run the code like this you should be getting an error and we do that we cannot normalize a vector with a length of zero to get wrong that it's quite simple we only want to normalize the vector if there is an input Vector at all a vector with a value of 0 and zero will return false so this one would not run but we do need an out statement and we simply want to return the input Vector on its own now if I run main. Pi we get basically the same kind of movement so not much has changed but the game works a bit more consistently we are nearly done for this part there's just one more thing that we will need and that is frame rate Independence at the moment our game always runs as fast as possible it should be around 1,000 frames per second if not faster but if you have a really slow computer this number might be substantially lower and our game would run either too fast or too slow depending on what setup the player has this we have to account for and for that we will first of all need self. clock which we get with py game. time. clock this is going to give you a clock object and that in return can control and measure the frame rate the way you are usually using it is all the way at the top of your while loop you're calling self. clock and then tick whatever you insert into the tick method is going to be your frame rate if you leave it empty your game is going to try to run as fast as possible if I run the game now there's not going to be any change however if I change this number to f30 and run it again now if I press to the left we're going to move substantially slower because we have a lot fewer frames to account for that we will need a concept called Delta time which is going to be returned by the ti method we can store that inside of a local variable if I print the value we are getting 33 sometimes 34 that means that it takes py game around 33 milliseconds to draw one frame which makes sense we have 30 frames per second and 1,000 so 1,000 milliseconds divided by 30 is about 33 if I get rid of the 30 and run all of this again we get a much lower number because now the game tries to run much faster so the time to draw one frame is drastically lower so at the moment it takes about 1 millsc to draw one frame since our game is very simple that number makes sense in our case we want to have these numbers in seconds which you get by dividing the return value by a thousand and this data time has to be accessible to the player we want to have this inside of this function and for that we have to pass Delta time through to the player let me get rid of the print statement and add Delta time to the run method of the level with that inside of level. Pi we have to add one more parameter for Delta time after that we can pass the other time into the update method of all Sprites and next up inside of the player we can add one more parameter on the update and then pass thata time into the move method finally then inside of move we have Delta time available and we can multiply our movement with Delta time since datta time is a really low number our speed needs to be much higher I set this one to 200 if I now run may not P again we have one kind of movement that feels about right for the game the really important thing now is I can set the tick method or the frame rate of the game to something like 30 and we would still have the same kind of movement although now it looks much choppier because we are drawing a lot less frames because of that the game is going to look less nice basically for all games you want to have as many frames as possible because it simply looks better and with that we have the first part of the game where we are importing a couple of things and we're creating a player and a couple of Sprites next up we are going to work on the collisions on top of that we are also going to give the player the ability to fall down no jumping yet but that we can work on later the really important part are the collisions because they make the entire game to make that work we have to cover just a bit of theory let's go through it really quick the most important part is that we are splitting the movement and the collisions into the separate axes meaning first of all we are doing all of the horizontal stuff this includes the movement and then the horizontal Collision check after that we are doing the same thing for the vertical axis so we are moving up and down and then we are doing vertical collisions with that set up the math becomes substantially easier next up them we have to figure out how to actually do collisions and there's already a problem because because pame doesn't really have inbuilt collisions the only thing that we do have are overlap checks and since we are working with Sprites that all have a rectangle we can check overlaps quite easily but the really important thing to understand here is that an overlap is not a collision all we can tell is that two rectangles are overlapping but this does not affect the position at all for that we need step number three where we are updating the position the way you want to think about it imagine you have some kind of static object and then a moving object let's say this one's moving to the right and eventually this object is going to overlap with the static object that would be the overlap check after we have done that we want to get to step number three and update the position where the yellow moving rectangle should be somewhere here ultimately we're moving it a bit further to the left because of the overlap that way it looks like there's an actual Collision not just an overlap we do however have to account for something else when we are updating the position we kind of don't know yet where the Collision happened and let me demonstrate so in the previous example we had a static object and then we had a moving object and now think of this from the point of view of py game all it sees are two rectangles that are overlapping with that information it doesn't really know where the Collision happened and that makes it impossible to resolve this Collision for example this yellow rectangle could have been in the previous position here it could have been here it could have been here it could have been here all of those would be valid positions that could have happened and we simply don't know which one is the case and to keep all of this a bit Keener let's do it only on one axis so once again we have a static object on top of of that we have a moving object that right now is here we have to resolve this Collision meaning we have to update the position of the yellow rectangle to get a proper Collision but we don't know where that Collision happened if the yellow rectangle came from the left and moved to the right then the correct answer would be roughly here however what is also a possibility is that the rectangle came from the right and move to the left really fast in that case the correct answer would be that the correct resolution would be on the right side and we have to figure out if the moving object came from the right side or from the left side and for that we will need extra information and what I think is the best way is to check the position of both objects twice once in the current frame and once in the previous frame and let me draw all of this again we once again have a static object and we have an overlap but now we also have additional information besides the current position of the yellow rectangle we also get the last frame position and let's say it was here and now the answer is much easier to understand we know that the rectangle moved from there to there and as a consequence the correct answer for the Collision would be this side that is the logic we have to implement so let's do all of this straight in code back in the project before we can start working on the actual collisions we need to do just a bit of setup most importantly the player needs to know where the Collision objects are at the moment he can move around but well we don't really know where other objects are for that inside of level. Pi I want to create another Sprite group self. Collision uncore Sprites and this is just going to be another Sprite group I can actually copy the previous one so py game. sprite. group with that we have Collision Sprites and this group should contain all of the collidable Sprites at the moment we only have basic Sprites and those we want to assign to two groups they will always be in all Sprites but they should also be in self. Collision Sprites hence for the group argument you want to have a tle with all Sprites and collision Sprites that way this Sprite is going to be in both on top of that for the player we want to insert these self. Collision Sprites and what is really important to understand now is that a Sprite is inside both all Sprites and collision Sprites however the player is only inside of all Sprites but on top of that has access to Collision Sprites but is not in it itself and that's a really important difference the player only has access to it it is not inside of it otherwise it would collide with itself and that would be kind of weird to make all of this work though inside of the player we have to add another parameter let's call it Collision uncore Sprites later on we are going to have quite a bit of collision stuff so let me add another comment I simply want to store this information inside of an attribute self. Collision Sprites is Collision Sprites and just to make sure we see what's going on let me print self. Collision Sprites if I now run m.p we can see we have a group with 94 Sprites that looks about right that means we can now work on the actual collisions and to get started we have to work inside of the move method because remember step number one is that we want to separate the axes meaning we start with the horizontal stuff and then we get the vertical stuff for that let me duplicate this line the points that I want to update are X and Y for the direction I want Direction dox and Direction doy this line does exactly the same thing that we have seen earlier if I run the game now we still get the same kind of movement very hard to see because we only have horizontal movement but trust me this is doing the same thing but now what we can do is create a method called Collision which is going to get self by default but then also the AIS and this axis could be horizontal or vertical and the way we are going to use it is we're going to call it after we are doing the horizontal movement so self. collision and then we're going to add horizontal then I can duplicate the line and after we're doing the vertical movement I want to check the vertical collisions that way we are doing the horizontal movement then the horizontal collisions then the vertical movement and then the vertical collisions so now we have to figure out the collisions itself and first of all we have to look at all of the Sprites inside of collision Sprites that we can do do let's say for sprite in. Collision Sprites after that we want to check if sprite. wct do Collide and the method here is called Collide wct although a better name for this one would be overlap rect because we are only checking if there's an overlap but anyway what we want to check is self. wct this line basically checks a collision between the Sprite rectangle so one of the Sprites and collision Sprites with if self. rect which is the rectangle of the player if that is the case we want to check if the axis is horizontal and then we want to do stuff let's say pass for now and else then we want to do something else else would be the vertical Collision for now I will focus on the horizontal part and there are two cases we can cover left collision and right Collision let's get started with the left left side and we already know that there is an overlap so we now have to figure out where the rectangle has come from as a matter of fact before we start working on that let me simply print a overlap and if I now run m not pi and I overlap with one of the Sprites we're getting overlap so we definitely know that something is happening the issue is now we have to figure out where the overlap occurred and and this could be either on the left side or on the right side for the left side I want to check if self. rec. left is smaller or equal sprite. rec. right and if that is the case I want to set self. rec. left to sprite. re. right the way you want to think about this line is we have a static rectangle for now and this would be the Sprite and then we have a moving one that would be the player what we are checking with this if statement is if we have a player let's say here and we first of all want to check the left side of the player against the right side of the obstacle or the Sprite in this case the left side of the player would be this side and the right side of the obstacle would be this bit and you can see that the left side of the player is smaller than the right side of the obstacle and if that is the case we want to set the left side of the player exactly to that position that way we are resolving this Collision this should actually work already let's try may not pi and now if I go to the right yep we have a collision although this only works on one side if I do it the other way we get some really weird Behavior to get around that we have to also cover the right side and this one works in basically the same way we want to check if self. rec. right is greater or equal then sprite. re. left and if that is the case we want to set self. re. right to sprite. w. left let's write this one now and that is looking not terrible let's try the other side and now we get some weird behavior that was intended because our Collision logic is currently incomplete basically pame became confused with these two lines where to put the player and well we have to add one more condition to actually tell which direction the player came from so for example if we once again have a Sprite and we have a player overlapping with it we now have to figure out if the player came from the right side and we have to put the left side of the player here or if the player came from the left side and moved right and then the player should be here with the right side on the left side of the obstacle to get that information we have to find where the player was on the last frame and the best way to get that information I found is when we are creating the entirety of the player we get two rectangles so let me another a comment with rects we first of all have the main rectangle this is going to show where the player will be drawn but on top of that I want to create self. old rect which by default is simply going to be self. re. copy the trick with this old rectangle is that bobia doing anything else in the update method we are setting self. old W to a copy of self. rect so self. rec. copy once again we are storing the current position of the rectangle inside of the old rectangle and then when we are doing all of the movement we are getting a new position instead of self. self. direct has the current position Old direct has the previous one although this information we need both for the player and for the Sprites now at the moment all of these Sprites do not move but they still need an old wct and that is quite easily added self. _ wct it's going to be self. rec. copy with that we can expand our Collision logic let's start with the left side I want to check if currently the rectangle or the left side of the player rectangle is to the left of the right side of the obstacle on top of that I want to check if self. allir do left is greater or equal than sprite. old. right so what does that mean once again to draw we have the static rectangle and we have the player one as well the first condition we already covered basically we are checking if the left side of the player is further to the left than the right side of the obstacle but on top of that we now have a second condition where we are checking the last position of the player and let's say old wck of the player was here and we are checking if this left side of the player was greater than the right side of the obstacle IE side that's why we know that on the last frame the player was to the right of this obstacle and in the current frame it is overlapping with the obstacle and both of those conditions combined mean that we are landing exactly on this line here and the same thing we have to do for the other sides so let me copy it for the right side Collision we basically want to check the opposite if self. w. right was smaller than or equal than sprite. rec. left and now if I run main. Pi that Collision looks pretty good and let's try the right side and that is also looking very solid that is going to cover the horizontal collisions so now I can minimize this and then we are going to work on the vertical collisions the issue is at the moment we don't really have vertical movement so doing this is going to be a bit of a problem to fix that we can start working on the vertical movement at least a little bit for now we are only going to make the player fall down for that inside of the move method and let me actually minimize everything so we can focus on one particular issue inside of the move method I want to first of all check the horizontal stuff that is what we have just done and then I want to work on the vertical logic to figure out the fall mechanic we have to work on a couple of things number one we shouldn't be using speed anymore because the player shouldn't be falling by the same speed as he is running that would be a bit weird although I guess you could be doing it I would rather want to have some kind of gravity and that we can set inside of the dunder init method and the speed I also want to have self. gravity in my case I went with 1,300 but that number is quite subjective just choose later on what you think looks good well with that we have a gravity next up we have to work on that our fall speed increases the longer we fall remember that gravity is not a constant if you fall longer you should be falling faster and all of that should also work with Delta time and well there's one specific thing that basically solves all of that first of all we want to get self. direction and only work with Y we basically want to increase this value by self. gravity that we want to multiply with Delta time as well that is going to give us a new Direction and this direction we want to use to update self. w.y actually what I've have done below so I don't need this line I can reuse this line we now have self. Direction doy but we don't need self. speeed or time anymore with that we should already have a decent start at the very least the player should now be falling although it might be a bit fast if I go back to the player and I change the gravity to something like let's say 400 then we get some decently looking falling Behavior so that's not bad however this system is not ideal because because it actually isn't frame rate independent it might look like it because we are using Delta time but if the frame rate changes then we would get different Behavior what you are actually supposed to do is divide the Gravity by two and then duplicate this line and then after we are applying the movement do the same thing again that way we getting the average of the downward velocity and then when we applying direction. y we also want to multiply it with Delta time once we have that logic and by the way I found this code snippet on stack Overflow I'm going to link the page it's super useful what we can now do is set the gravity back to 1,300 and then try m. pi and we can see that the gravity very barely works the issue for that is a bit deceiving because the problem is the input method more specifically this line where we are normalizing the vector because of that the length of Direction can never be greater than one regardless of what we are doing down here in the gravity bit the way around that is that direction should only cover the horizontal part meaning only self. direction dox should be updated and then well from the normalized vector we only want to f x same with the input Vector if we are not normalizing it or well we could just return zero here it would be the same thing but anyway let's not try of this and we have gravity that looks good with that we have the two major parts of the movement so I can minimize this method and now we can work on the final part of the Collision the vertical bit that part is going to be your exercise I want you to finish the vertical collisions it should look very similar compared to what we have done for the horizontal ones pause the video now and see how far you get to to get started there are two options we could encounter we have a top collision and we have a bottom Collision for the top Collision I want to check if self. re. toop is smaller or equal compared to sprite. w. bottom on top of that I want to check if self. old. toop is greater or equal compared to Sprite dot old wed do bottom and if that is the case I want to set self. dotop to sprite. rec. bottom once again we have our Sprite and we have the player let's say now the player is roughly here the first condition that we are checking is if the top of the player is smaller than the bottom of the Sprite the top of the player layer is this line and the bottom of the Sprite is this line in the current image this would be correct remember that the origin point in P game is in the top left so this would be zero and our coordinate system extends from there so this condition would be true next up we have to check the old rectangles and for the player let's say the old rectangle was here on there we want to check the top of this rectangle if it was greater than the bottom of the Sprite rectangle that way we know that in the last frame it was below the Sprite which in this case would also be true so this line would be executed which is exactly what we want after that I can duplicate this entire line and then cover the bottom side we want to check if self. re. bottom is greater or equal than sprite. re. toop and on top of that we want to check self. old rect bottom is smaller than sprite. old. toop and if both of those conditions are true then self. re. bottom should be sprite. re. toop if I now run all of this we are landing on a platform and that looks really good there's just one minor issue and that is while we are standing on here Direction not why so the gravity is continuously being applied meaning if we are falling down we are disappearing immediately as a matter of fact let me demonstrate this a bit better after the move method down here I want to print self. Direction doy and now if I run main. Pi you can see that our downwards gravity keeps on increasing and this shouldn't happen the way around that we don't need the print statement anymore is if we have any kind of vertical Collision then we want to to set self. Direction doy to Z and now if we run off this again I can fall down and we have a fairly constant gravity applied that looks good and with that we have the gravity this is basically all we need all righty at this point we already have the basic outlines of a platformer but there's one really important part missing and that is jumping on top of that we are also going to add wall sliding and wall jumping and most of the logic for that actually isn't that difficult so let's jump straight into the project here we are and I want to work inside of player. py first of all we need to get some kind of input to tell the game that the player wants to jump that's going to happen inside of input all the way at the bottom of this method I want to check if keys and then P game. Kore space if that is the case I want want to jump for now let's simply print jump to see if this is working if I now press space we get jump so that part is working just fine that means we can add the actual logic now in the most basic sense all you really have to do to get a jump mechanic is set self. Direction doy to some kind of negative value for example if I set this to -200 run m not Pi again and press space then we have a little jump also if I hold on Space we keep on going up so it's not really a jump it's more of a float but you get the idea however I want to have the input function only for input it shouldn't do anything else and this would count as something else so I am not going to add a -200 in here instead I am going to get rid of all of that and instead set self. do jump to through this attribute doesn't exist right now so let's create it inside of the dunder init method self. jump and by default this attribute should be false now we know that we have input that the player should be jumping and that we can use inside of the move method and we can minimize the input method I want to work inside of the vertical Movement we can simply add a bit of code afterwards if self do jump then we want to do something most importantly we want to set self. Direction doy to some kind of value and that value I have called self. jumore he this attribute doesn't exist right now so let's create it inside of the dund NIT method below self. jump and give it a value the one that I went with was 900 however ever you do have to be careful for this one 900 right now is a downwards movement because the origin point of the game is the top left meaning if we want to go up we have to go in the negative direction that being said I don't really like having a jump height or some kind of strength and then set a negative number that feels kind of weird although you could be doing it in my case I am simply going to equal it to negative jump height when we are setting self. Direction doy and now I can run main not Pi again and we get a massive jump eventually we should be coming down again actually no we're not because this self jump is never going to be false as a consequence this negative direction will always happen hence the player never comes down again a fairly easy thing to fix all we have to do is self do jump should be fults right after that line with that I can run m not Pi again again and now we are coming down again and that feels like not a terrible jump although I can jump as many times as I want which also isn't ideal although kind of fun to fix this kind of behavior we need one important check we need to know if the player is on the floor or not and later on we also want to know if the player is on a wall or not that's going to be really important for the wall jump and the wall Slide the way I organized that part in side of the Dand method under the Collision part I have created another attribute called self dot onore surface and there are three surfaces that I care about number one is the floor by default this one gets false then we have left also false and finally we have right which by default is also going to be false and I want these values to become true if the play players colliding with the floor a wall on the left or a wall on the right and that we can use for example inside of this if statement to check if the player is self on Surface and then we want to check against the floor and only if that is the case we want to allow it jump that means we now have to check for contact with the floor and I called the method for that check uncore contact no need for custom parameters and in there we have to talk about some logic it's ultimately a very simple concept imagine we have the player and the player is surrounded by a couple of Sprites we could have one here another one to the side then let's say one here and another one here all that we are going to do is we are going to spawn three rectangles one to the left of the player one to the right of the player and one right below the player and if one of these rectangles collides with any of the collidable walls for example if the left rectangle collides with this one for example then we know we have a contact on the left side of the player for that we have to create a couple of rectangles I want to have a floor rectangle I want to have a right rectangle and I want to have a left rectangle for the first one I want to create pame do rect and now we have to define a left a top a width and a height or more specifically I want to create two t bus one with the left and the top and one with the width and the height both approaches are perfectly fine as far as P game is concerned left and top is actually really simple because all we need to get is self. rec. bottom left and I should probably draw all of this so once again we have the player and at the moment the floor rectangle should be a rectangle that covers the entire bottom of the player and at the moment the point we have placed is the bottom left this would be that point after that I want to get the width and the height now the width is really simple we are just going to get the width of the player and for the height we can set a random number that is sufficiently small I think two is reasonably okay here which means for the height we want to have by two and for the Wii we want to have self. dowi and I suppose the other two rectangles for now we can ignore although we will come back to them more importantly for now I want to get all of the rectangles that we could be colliding with and I want to have all of them inside of a list let's call them Collide rects and that is just going to be a list which I can get via list comprehension I want to get sprite. W for sprite in. Collision [Music] Sprites this step you don't necessarily have to do but I think it makes the code a little bit cleaner because in the next step and let me add a comment for that I want to check the actual collisions and ultimately all that we are going to do in here is self dot on Surface and for now we only really care about the floor and this one should be true but only if a certain condition is true and that is that the floor wrecked is colliding with any of the rectangles inside of the Collide W list for that pame has a really neat function it's called Collide list and that is checking if this rectangle is colliding with any rectangles inside of a list which in our case is the Collide rects list if there is a collision between the rectangle and the list then the return value of collide list will be the index of the rectangle inside of that list unless there is no Collision whatsoever then it returns -1 which means if this value is greater or equal to zero then we know there's some kind of return value or in other words our flow rectangle has hit one of the Collide rectangles if that is not the case however we have no collisions whatsoever so we can set the value to false and that is all we need with that we can look at the update method and simply call self. check contact now if we're trying all of this I should only be able to jump if I'm on the floor and I keep on pressing space and we only jump once we hit the floor however there's a bit of a weird Behavior if I press space then the player is always going to jump twice and I think I know what the issue is if we're looking at the player this jump equals false should not be indented inside of this if statement because of that we are only setting self. jump to faults once we hitting the floor but we should rather set it to faults as soon as we are jumping so now if I run m. Pi I should only be jumping once and that is significantly better cool now we already have a very basic platformer and everything else works pretty well so quite happy cool that means next up we can work on the wall collisions and for that we will first of all need to check if there's contact with the wall at all and that means we now can work on the right rectangle and the left rectangle both of those are going to be fairly similar compared to the floor rectangle I suppose let me draw the entire thing we once again want to have the player and now we want to have a rectangle to the left and to the right and importantly for this one I don't want these rectangles to fill the entire height of the player there should be a bit of space at the top and the bottom I suppose a good number might be that the height of these side rectangles should be 1/2 the height of the player I am telling you all of this because this is going to be your exercise I want you guys to create the left R and the right red and afterwards use them to update on Surface pause the video now and see how far you get let's start with the right rectangle for that we once again need pame dot w where we have to Define left top width and height I think for this one width and height are the easier Parts width is simply going to be two pixels and I should really draw all of this we have the player and we want to create a rectangle that's roughly this big at the moment we have set the width of this thing to two pixels next up we need to get the height which should be 1 half of the player height or to be a bit more specific we want to get half the height of the player rectangle so self. rec. height and then divide this by two after that we need to figure out the left and the top and for that ideally I would like to use something like I have done for the floor rectangle where we start with one of the corner points of the original rectangle angle so for that I want to use two bus for the left and the top and then another two bu for the width and the height so for the left Ed top the way I approach this is I first of I got self. rec. toop right that point would bring us all the way up here meaning now we have to go down by one qu of the height of the rectangle if we're doing that we end up down here to make that work all we have to do is add one more Vector which we can do via a vector I want to go zero pixels left or right and then I want to go self. re. height and divide this one by four and that is pretty much it with that we have the rectangle next up we can work on the left rectangle and this is going to be py game. rect and once again we want to have a tuple with left and top and then another Tuple with width and height once again width and height are going to be the easier Parts in fact I can almost use the same numbers I have used for the right rectangle the only change I have to make is that the width should be -2 and I should have mentioned this but in pi game you can totally use negative numbers for rectangles makes your life quite a bit easier but anyway now we have to Define left and top for that I want to get self. rec. toop left and once again go down by one quar of the height of the rectangle which I have already done before so I can simply copy those numbers also for left W I should fix my typo that is starting to get a bit weird at this point I think it's a good idea to test if this is actually working so I want to draw both of these rectangles actually we can draw all three of them I want to py game. draw do wct and now I want to draw on the display surface which we don't have available right now for that as a quick test self. displore surface is going to be py game. display. getor surface that way the player can draw on the surface but I am going to remove that in just a second it's just for testing purposes this is the surface I want to draw then I need the color let's go with yellow and finally we need to rectangle let's go with the floor rectangle and now if I am running all of this we are getting an error and that error happens on this line because we are not closing a bracket that happens for this Vector very easy fix if I now remain. Pi again there we go and you can see that we cannot see the floor rectangle the reason for that is that we are drawing this line inside of check contact and check contact happens inside of the update method which we are calling before we are filling the entire background as a consequence we are drawing black on top of this drawing but if we are filling the background first and run m.p again now we can see we have a very small yellow rectangle below the player and this is telling us if the player is on the floor or not and that looks perfectly fine the same thing we can now also do do with the right rectangle and with the left rectangle let's try m not Pi again and you can see that this is almost working we do have the right rectangle but something went wrong with the left one and I believe I know what the issue is if I go to the left rectangle and set this WID to a two and then for the vector for the offset I'm going to set the number to -2 that way if we have the player now this offset will get us down by one4 and then two pixels to the left so this would be -2 from that point we are then drawing the rectangle size and the width is now going to be two so this number now works out and I think now this is going to work so if I run main. Pi now you can see it that feels much better so I think in P game if you using negative numbers for a rectangle the rectangle is still going to work but the drawing logic gets a bit weird in the actual project I have used negative numbers for this one so definitely works but I guess I leave it like this so it's a bit more consistent we know that we have the proper rectangles to check if the player is colliding with a ball that we can now use to update self. on surface I want to check self do on Surface and let's go with the right side this one should be true if we have the right rectangle and a Collide list with the Collide rects and this number needs to be greater or equal to zero if that is not the case the value should be false after that I can duplicate the entire line change right to left and then I want to check the left rectangle also let me align those lines that feels a bit cleaner and with that we can print self dot on Surface and let's try the game now you can see we are on the floor if I collide with the right wall we get right being true and if I collide with a left wall we are getting left being true and all of this works pretty well so I am very happy with that we also can get multiple values so all of this is going very well that means we are done inside of check contact and now we can work inside of the move method and let me close everything else so we can focus on this one method at the moment the player is always falling down because of these three lines but if the player is sliding on the wall then we want to have a different kind of gravity or rather we want to have a lot less Gravity the way I approach roach test is simply with an if statement where we are checking if we are on a wall if that is not the case so else then we are going to do all of this stuff oh and self Collision should probably be all the way at the end of the vertical movement now we have to check if the player is on the wall or more specifically we want to check a couple of things first of all the player should only be able to slide on the wall if we are in the air which means we want to check if not self dot on Surface and lore next up then we can check if we are either hitting a left or a right wall and that we can check via the any function any wants to have either a list or Tuple as an argument and if any value inside of this list or tupal is true then any is going to return true in our case we want to check if self dot on Surface and left and the other value will be self. on Surface and right if both of these conditions are true we know that the player is in the air and colliding with a wall which means which means we can set a custom wall slate Behavior first of all I have set self. Direction doy to Zer that why we are stopping any kind of existing fall or jump mechanic next up then we want to move the player down and that we do with self. rect doy plus equal and then some kind of speed I went with self. gravity divided by 10 and don't forget we want to multiply all of this with Delta time and that should already be a basic start if I now collide with a wall this is going to be a bit there you can see we have a basic wall slide and we can try right the other side as well I just have to hit the wall and that is working pretty good next up we have to work out how to jump from a wall slide at the moment if I run main. pi and we are wall sliding I can't really do anything so if I press space nothing's going to happen for that we want to work inside of this if statement at the moment we only allow a jump if the player is on the floor however we can also add a second if statement or rather let's use an L if statement I want to check once again the any function we have just used in fact I can just copy it from up there and then we know if the player is currently sliding along the wall if that is the case I want to set self. Direction doy to the negative self. jump height in the game let's go to the left and I want to jump but I press space nothing is going to happen and I kept on holding down left in this example however if I jump again and press space and go to the right then the player is going to jump in that direction so the issue we have at the moment is that once we hit a wall we set s the direction to zero and this is going to override any kind of jump basically what we want to force is that the player has to jump in the opposite direction from the wall for a very short amount of time for example what we could be doing is set self. direction dox is going to be one if self dot on surface is left and if that is not the case else this should be-1 now the jump should already work a little bit better but not by very much so let me try and well minor improvements as long as the player keeps on holding down left inside of the input so this line then the player is going to get immediately back to the wall regardless of what this line is doing we basically have to block the input for a bit and for that we will need a timer and that timer we actually already have if I open the timer. pi file in the project folder you can see we have something like this and this is a very basic custom timer I am explaining this thing in a lot of detail in another tutorials so check this one out for more information but quickly how it works we are creating a timer class and for this timer class we can customize a duration a function and if the timer repeats and then when we are creating an instance we are setting a couple of attributes for duration the function start time is going to be zero and then we are checking if the time is active and if it repeats repats nothing complicated once the timer activates we are setting active to true and the start time to get ticks get ticks gives you a time since our game has started in milliseconds that way we always know exactly when this timer has started and then inside of the update method we are using get ticks again except now we are checking this constantly this update method will run continuously ultimately the way this timer is going to work is imagine we have a game running for some amount of time and we are starting the timer at some arbitrary Point let's say here this point we are only checking once whenever we are activating the timer but after that we are running this update method continuously meaning we are checking the time right after and a little bit later a little bit later a little bit later again and so on and every time we are getting the current time again since the start of the game and the further we go the greater the distance from the original point will be and at some point we are getting to this line where the current time minus the star time is greater than the duration let's say the duration point would be this line and if that is the case we know that a certain amount of time has passed whatever we specified in the beginning and if that is the case we can run the function we have specified and on top of that we are also going to deactivate the timer and this one sets active to fults and the start time back to zero if repeat is enabled that we are doing inside of the method then we are starting the timer from scratch and with that we have a timer that can call a function although it doesn't have to and it can repeat itself this class is incredibly useful and I'm using it for my purposes all the time so how do you actually use it inside of the player first of all we have to import it from timer import timer after that inside of the NIT method I want to get rid of the display surface that was there just for testing purposes and instead I want to have a timer section in there I'm going to create an attribute called self. timer which will be a dictionary inside of this dictionary the key value pair are going to be the name of the timer and then the timer itself along with the arguments now for these timers you could specify a couple of arguments if you open the timer class you will always need a duration and this we are going to give it you could also give it a function or repeat but I don't care about either of those all I want is to have some amount of time let's say 200 and the name should be let's call it wall jump however we do have to be careful now because this timer by default is not going to work because it's only going to work if we are calling the update method for that the way I approach it and let me minimize all of the code because I want to create another method that I will call update timers no need for custom parameters all we are going to do in there is for timer in. timers and we only care about the values inside of it I.E the timers let me open it actually oh and this should be self. timers we want to look at all of the values or the timer instances inside of this dictionary which we are getting via values inside of the for Loop all we're going to do is call timer. update and that's it if we call this method let's do it before the input self. update timers then we have a working timer and we could add more timers to it they would all work automatically that's going to happen quite soon actually but anyway for now we have to figure out when to activate the wall jump timer although let's first test it let's say inside of the input when I press jump I want to activate the timer this I do with self. timers and then wall jump and we want to activate this timer activate by the way is just a method of the class and that we can then check inside of the update method I simply want to print if this timer is active or not if I now run main. Pi you can see by default this one is false but if I press space we get true for a very short amount of time I think I set this timer to 200 milliseconds so for 1/5 of a second this timer would be active if I increase this number to let's say 2,000 run all of this again press space we should have true for 2 seconds and we do that looks good cool so we know this timer is working which means we have to figure out how to use it and in my case if the player is moving and this if statement is true I.E we are trying to jump away from a wall then I want to activate the wall jump timer we can do that all the way at the top of it that way we have the timer active and that we can then use inside of the input to block these two inputs all we have to do is another if statement if not self. timers we want to look at the wall jump timer and we want to check if this one is active or more specifically if this timer is not active only then do we allow all of this which we can do quite easily I want to do all of that jump should be outside of this if statement that we are still going to need but now if I run main. pi and let try to get a wall jump this feels much better although the timer now is way too long so even if I don't press anything the player keeps on moving to the right let's try the other side and this is also working the last thing we have to do is set this timer to a more reasonable time let's say 250 and then we can try it again and possibly a bit more actually but this is something you can play around with let's try 400 and yeah but feels about right and just a quick recap all that we are doing is once the player is on a wall and we are trying to jump then we are activating the wall jump timer and as long as this timer is active we do not allow any kind of left or right input that is important because a bit further down when we are running the original if statement we are trying to make it so that the player goes in the opposite direction of the wall for a short amount of time that way we always move away from the wall and that's basically it the timer class makes this kind of problem really easy to solve but anyway I want to work on one more thing if we run main. pi and if I jump right next to wall then we are jumping away from it immediately and that is kind of weird so I want to use a timer a second time and what I want to do is that once the player is jumping we only want to allow a wall jump after after a short amount of time let's say after 250 milliseconds that part is going to be your second exercise for this part I want you to create some kind of logic to only allow a wall jump after 250 milliseconds of jumping for that you should be using another timer pause the video now and see if you can figure this one out to get started we first of all have to create a second timer and that I want to do inside of self. timers let's call this one wall slide I guess wall slide block is a decent name this is going to be another timer with the duration for this one 250 milliseconds and this timer is going to automatically work because we have our handy update timers function or well method now we have to figure out when to activate the timer and and we know when the player starts to jump either inside of this if self. jump or inside of the input I want to work inside of the move method we know that the player does the original jump inside of this line so in there we also want to activate the timer for that I can actually duplicate this line the only thing I have to change is wall slide block I believe they called it yep with that we should have a time at at start once the player jumps and let's see if this one is working I want to print the wall slide block and check if this one is active if I now run main Pi by default it's false if I jump we have true for a very short amount of time that looks about right which means we now have to use it and that's going to happen inside of the L if statement because in there we are getting the wall jump I want to check if the player is currently touching a wall and I want want to check if this timer is not active so not and then copy the timer itself and DOT active and now if we are trying all of this we get something not ideal the reason for that is at the moment we are blocking the wall jump but we are not blocking the wall slide to fix that we have to use the timer again I can actually just copy it we want to do this as well so just a recap the player should slide from wall if the player's in the air has contact with the wall and has been in the air for some amount of time let's try all of this now and I can still jump and now if I jump right next to a ledge this is working just fine let's see if I can still wall jump and yep I can so with that we have a very nice jumping mechanic or generally a platformer this feels pretty good at this point we have covered all of the basics of the platformer so we can start with some more advanced topics the first one is going to be moving platforms which is going to make the game feel significantly more interactive to make those work we have to update the player just a bit but it's not going to be too difficult first of all though we have to create moving platforms for that we will have to look at til here we are in til and so far we only worked inside of the terrain layer and the objects layer but now we want to add one more layer we want to work with the moving objects if I make this one visible we can see a few more things most of the stuff you can ignore what I want to work with are these two rectangles if you look on the left side there we have a couple of parameters most importantly at the top we have a name helicopter later on that's going to be important for the graphic but for now just don't worry about it what is more important at the bottom we can see a couple of attributes and the one we care about most for now is the speed that's going to be well the speed of the platform and the way this system is going to work and let me zoom in just a bit more we are going to pick the top middle point of this rectangle and then spawn a platform around it this platform is then going to move down in a straight line until it reaches the bottom Point once it does that it starts moving back up again until it reaches the origin point and this keeps on going so we have a platform that moves up and down the horizontal platform so this one is going to work in basically the same way except it moves left and right later on we are also going to get other kinds of platform for example those are going to be a saw it's not really a platform more of a trap and and then we have a spike this thing is going to create a spike that rotates around this point but those are not important for now I only really want to work with those two objects for that let's work inside of level. Pi more specifically I want to work inside of setup later on the setup method is going to become quite Mighty so I want to start adding comments right away I suppose the first part that we already created is the tile and then we have the objects after that I want to have let's call it moving objects all of those I get via 4 obj in TMX map doget layer by name and the layer name is going to be moving objects in case you're wondering where I'm getting those names from if you look at til you can see we have moving objects that's what I'm importing and and there we get a couple of objects so let me print obj and run the code and we don't care about the game but what we do get is a whole bunch of objects the ones that we care about are helicopter so the last two those we can isolate quite easily with an if statement so if objname is equal to helicopter if that is the case we can for example print obj do width obj do height obj dox and obj doy let's try that and we are getting a whole bunch of points that looks pretty good that information is already useful because at the moment we are trying to figure out if the platform is moving left or right or up or down and the easiest way to figure that one out is if the rectangle that we created in tiled is wider than it is tall so basically if obj do width is greater than obj do height if that is the case we want to have horizontal movement if that is the case let's say I want to create a local variable called move Direction and this one would be X on top of that we also have to create a start position and an end position both of those are going to be two builds with X and Y and now we have to figure out if this is the rectangle we have created entitled we currently want to get this point that would be the start point and then the end point would be this bit also I should mention obj dox so the information we already have is this top left point we always specify the top left for any object which means to get the X part of this point we simply need obj dox and for the Y point we need obj doy and then plus half the height of this rectangle so obj dot height / by two and that's it now we get the point next up we need the end position once again I want to have X and Y and this time I want to have obj dox plus obj do wi so basically we're taking the origin point and going all the way to the right side and obj doy for the endpoint is going to be the same y that we have specified already so that part's easy after we have that I want to cover the El statement and that one would be the vertical movement for most of that I can simply duplicate the lines I already created because we don't have to change that much most importantly move there is going to be y we now want to move up and down start position for this one and remember we are now working with a rectangle that is quite tall but not very wide and we want to get this point for the start and this point for the end we pretty much do the opposite compared to what we have done for the horizontal Movement we want to get obj dox and remember this is the left side of the rectangle and we want to add OB J do Wii and divide it by two obj doy for the Y part can just be obj doy and then for the end position we want to get the same point for X this one does not change however for obj doy we want to add the entirety of the height and that is pretty much it with that we're getting a direction and a start position along with an end position there's one more property that we do need and that is the speed and this one is the same for vertical or horizontal so we can keep it outside of this if statement now to get the speed we have to look at tiled and then look at the custom properties so on the left we have the speed to get that inside of the code we need the object and this object has a properties attribute which is just going to be a dictionary as matter fact let me print what we get so print this speed and we don't have to print the other two things anymore if I now run main. Pi we are getting all of the attributes most of which we don't care about but speed is in there as well this is what we actually want to work with since we now have a dictionary we can get the key quite easily we want to get the speed of all of this so now if I run main. Pi we're getting 160 that looks really good with all of that we have the basic attributes we just have to put it inside of a class so we can display it that's going to happen inside of Sprites dopy in there I want to create a new class and I call this one a moving Sprite what is really important is that this is going to inherit from the Sprite we just created this Sprite up here that way we don't have to recreate the image and a rectangle anymore it's going to save us a bit of work and over the course of this tutorial I am going to use quite a bit of inheritance just be ready for that but for now it's not going to be too difficult I think so we want to initiate this class we will need quite a bit of information most importantly as always we will need self on top of that we will need the groups this one is part of then we need a start position an end position we want to have the move Direction and finally our speed after that we have have to call the super Dunder inage method to initialize the parent class and really important now we are initializing this parent class which means this init method wants to have these parameters let me copy them actually first of all the position that can simply be the start position and then let's ignore surface for now that's going to get a bit more complicated instead with we are going to work on the groups and that part is really easy because we already have the groups we are simply passing them through via the parameters so we have to figure out a surface and if you look at the original I have created a surface parameter but I am not using it at all for this entire class instead for the image I am simply creating a new Surface from scratch as a consequence whatever we are specifying for this moving Sprite as a surface will be ignored and over written with a surface that is the tile size in width and height to get around that I want to cut out all of this bit and assign it as the default value for the parameter and then for the groups we have a default value of none once we have that I can assign this image a surface and with that the surface parameter will not be ignored anymore which means inside of the moving Sprite before we are calling super where in knit we can create a surface with a custom size let's say pame do surface and I want this thing to be let's say 200 pixels wide and 50 pixels tall later on once we have proper Graphics we are simply going to use those but for now since we don't have that we have to work a bit more creatively here but I hope the logic makes sense the only real difference now is that we are creating a py game surface as the default value for the surface surface parameter inside of the class and this gets assigned to the image and just to make sure I didn't make a mistake let me run m. pi and no error so this is still working just fine however now inside of level. Pi we can create the moving Sprite although for that first of all we will need to import it so import moving Sprite after the Sprite now I can create a moving Sprite and for that to work we will need a whole bunch of arguments all of those let me paste them in and go through them one by one first of all the groups and to keep it simple I will start with all Sprites then we have the start position this one we already defined here and here the end position we also have we have to move Direction and we have to speed so basically we have everything we need just to get started let's run M pi and we can see we have two platform looking things we count do anything with them but at the very least they are there also while we are here I want to close the timer because we don't need that part anymore instead I have to work a bit more inside of the moving Sprite and there is already one problem that you might have spotted the positioning of the moving Sprites is a bit off imagine we are currently working with a vertical rectangle this is what we imported from tiled and at the moment we are setting the start position as the top left of the rectangle because of that the rectangle is going to have a start position that looks something like this which really is going to be weird instead I want this point to be right in the center of the platform or to be a bit more specific I want to move the center of the platform to that point that makes more sense that part is quite simple all we have to do is update the rectangle so self. rect do Center is going to be the start position if I now go to main. P we get a slightly different position but this one is a bit more accurate cool that's a good start and also a very easy thing to fix on top of that we have to capture this speed and the end position as an attribute also the start position so self. start position is start position then self. end position is end position and finally self. speed is going to be the speed also and this I want to store under a separate command I want to have the movement and I guess the speed should be in there as well besides the speed I want to have self. Direction which is going to be a vector with one and zero I.E we are moving to the right but it should only be if the move direction is going to be X if that is not the case so we're moving up and down then and the direction should be Vector with Zer and one so we are moving down by default also while we are here I want to store the move Direction as a separate attribute so move de is moved deer after we have that we can create an update method with self and Delta time since we now are updating the position we also have to update the old rectangle so old rect is going to be self. re. copy this we didn't need to do in the original Sprite because this one isn't moving the old Rec can simply stay in the same position the entire time and next up all we really have to do is self. rec. toop left plus equal self. direction multiplied with self dop speed multiplied with Delta time and now if I run m. Pi we get moving platforms we still cannot interact with them and they keep on moving in the same direction forever so so that's not ideal but at the very least we get something to make sure that we are constraining them I want to add another method and this one is going to be called check border don't forget to call it and now we can create the check order method in the most basic sense all that we are really going to do is first of all I want to check the move direction if let's say we are moving on we moving left and right if if that is the case I want to check if self. r. write is greater or equal to self. end position and the X part so index zero on top of that I want to check if self. direction dox is equal to 1 if that is the case we are too far to the right and we are moving right then I want to set self. direction to -1 and this should be self self. Direction dox on top of that self. rec. right should be self. end position and zero it's honestly quite a simple logic imagine we have our platform and the platform is currently moving to the right and on top of that our end position is some kind of border that we have just crossed to check that we first of all check this first bit where we are checking the right side of our platform against the X part of the end position and if this part is greater we know we have exceeded this point on top of that we also want to know if the platform is moving to the right that way we are sure that we have crossed this border and we are going in this direction if both these conditions are true then we want to move in the other direction and then also set the right side of the platform to the border so we get something something like this with that I can run main. pi and now this lower platform should eventually bounce back and it does that looks really good although it only works once basically we have to add a few more options here that part is going to be your exercise I want you guys to make the moving platforms bounce pause the video now and see if you can figure it out first of all I want to finish up the left side of the rectangle for that we can actually copy most of the if logic I want to check if self. re. left is smaller or equal than end position zero this shouldn't be end position this should be the start position on top of that we want to check if the direction dox is -1 if that is the case Direction not X should be one and ourself. rec. left should be start POS zero and that should cover the entire left and right movement so we're bouncing there once and we should also bounce there then next up we can work on the other direction so else and now I want to check the up and down part let me actually duplicate all of this stuff because I want to check if self. re. bottom is greater than end position. one and self. Direction doy is going to be one so we are moving down if that is the case self. Direction doy should be negative -1 and self. re. bottom should be n+ one after that for the up movement self. re. top if it is smaller than start position one 1 and we are currently moving up so self. direction. Y is -1 if that is the case we want to move down and self. re. toop should be start position one let's try this one and we are going up again and also down again cool so that one is working just fine however I just realized I made a mistake and that is when we are starting to create the rectangle we are setting the center of the rectangle to the start position so if this is the path we are following at the moment before we are moving our platform is roughly here however then when we are doing these parts let's say we have a top Collision then we are checking if the top of the the platform is exceeding this Top Line and if that is the case we are moving down again as a consequence the highest point after the start is that the platform can only really go up to here and that would look a little bit inconsistent and well would make the game feel a bit less good to get around that I want to add one more if statement so if we want to check the move Direction and if move direction is let's say x then we want to send self. rect do mid left to the start position if that is not the case so else then I want to set the self. rec mid top to the start position and then we should have a bit more consistency and now these two platforms should feel a bit better and yeah I hope you get the problem this is a fairly easy thing to fix next up we can actually make these platforms interact with the player because at the moment they're only visible but not collidable to fix that we can place them into self. Collision Sprites let's see what happens in the game and the game doesn't crash that's a good sign and I can also jump on them we still have collisions however you can see the downward movement is a bit choppy not terrible but this doesn't feel amazing also I cannot jump on top of these platforms and if I go on the horizontal platform you can see another problem if I don't move well I don't move so we have to add a bit more logic to all of this oh also sometimes I can jump it's just very inconsistent but nothing that is insurmountable for all of that we want to work inside of the player most importantly we first of all have to know if there is a moving platform or not so under collisions in player. init I want to create self do let's just call it plat form by default this one is none however then inside of check contact we want to check if the player is standing on a moving platform in this we want to check on every single frame so by default I always when we start want to set self. platform to none and after that I am checking again if we are on a platform and that I'm checking with a for Loop for sprite in self. Collision Sprites however I don't want to have all of the Collision Sprites I just want to have the ones that are a moving Sprite meaning we have to isolate this Sprite somehow and we could do it in a couple of ways for example we could check for specific attribute like speed for example wouldn't exist inside of a normal Sprite however I do want to be a bit more explicit so I'm going to give this another attribute self. moving which I am going to set to true and now I can use list comprehension where I will create a Sprite for sprite in self. Collision Sprites and important you now have to add Sprites as well that way you're getting a list of all of the Sprites and then you can use them inside of the list comprehension and really important for this one is you want to this only if this Sprite has an attribute which you can check with has utter we want to check the Sprite and then moving with that we are only getting the moving platforms and on there we can check if Sprite do rect do Collide rect and we want to check against the floor rectangle that we already have if that is the case we know that the player is standing on a platform and and if that is the case I want to set s the platform to this Sprite that way the player will know what we are standing on that is information that we can use very well under the move method or well you could place it wherever you want but I think it makes the most sense there I want to add another method called platform move for this one we will need self and Delta time as well and basically all that we are going to do is first of all we will check if there is is a platform I.E if the player is standing on a platform and if that is the case we're going to get self. rec. toop left and we are going to add self. platform. Direction multiplied with self. platform dosp speed multiplied with Delta time basically if the player is standing on a platform we are going to move the player in the direction of the platform by the speed of the platform at our current framework r that way our player is going to move along with the platform after that we have to make sure that we are calling that so after move self. platform move and don't forget Delta time if I now run main.py and I hope I can get on one of these platforms this looks much better now the player is not choppy anymore although I cannot jump on this platform at all but I can do it if we're going left and right so we are nearly done this is already a major Improvement the last really major thing that we have to work on is that if the platform is going up and down we cannot jump and that is because inside of the move method we are kind of overwriting the direction so for example once the player tries to jump so we are updating the direction then the player might Collide immediately once again with a platform and and then we are canceling out the jump a really easy way to get around that is to get self. Rec do bottom and move the player up by one pixel so minus equal 1 and then this should be working and now for testing purposes I realized that getting on the platform inside of main.py is kind of difficult if you have the same issue simply open tiled and then move these things around just a little bit something like this go back to the code and run this again and now this is getting much easier and I can jump while the platform is going up and down that feels much better and the same thing is also going to work for the other platform so cool this is working pretty well very good we are nearly done with the basics of the platformer there are just three more things that I would like to add number one I want to have semi collidable platforms which are going to make the game feel much more interactive and all that really means is that the player can jump through some platforms While others are completely solid number two I want to add a hitbox so that we have a separation between how the player looks and what the player can collide with especially for the sord of the player this is going to make the game feel much better also for this stage we are going to add a basic player graphic but that's not really necessary it's just better to show what's going on fin finally we're going to add a camera that way you can actually explore the level on top of that there's one minor bug that I really want to work on and let's start with that right away here we are back in main.py and if I run the game we have the same thing that we have already seen a couple of times and all of this works reasonably well however if I jump against the up and down moving platform and it goes down then we get some weird behavior and this only happens if the platform is moving down so what happens here and that's the bug I want to fix basically the way you want to think about it is that if we have a platform that's moving down and then we have a player moving up I.E jumping and we have a collision between the two if that is the case the player should be moving down and move down at an increasing speed because of gravity however that doesn't happen because as soon as the player moves moves down just a bit the platform is also going to move down and as a consequence the player is going to move down just a bit but the platform will move down even more the result of all of that is going to be another collision at the top the end result is that the player is going to be stuck at the bottom of this platform that we can fix quite easily all of that happens inside of the player as a matter of fact we have already done something similar if we look inside of the move move method they on the platform once it's moving up we're giving the player a very small offset once he starts to jump that way we are making sure that the player is not glued to this platform and something similar we are going to do inside of the collisions basically when we have a top Collision then we want to check if has attribute if this bride is moving I.E with these two conditions we know that we have a top collision and the platform we are colliding with is moving then we want to give the player a bit of an offset self. wed. toop and now you have to add a small amount I think I went with six in the final project let's see how that looks so if I now Run the game and the platform goes down you can see we are always getting a bit of a gap between a platform from and the player now if you look closely this is kind of noticeable but in the actual game this will be much less noticeable and the player won't pay attention to it anyway you could add more code to make this effect more convincing for example you could add a timer to disable this kind of collision or you could work with the speed of the moving platform there are lots of things that you could be doing here but I want to keep it simple so I think this approach Works reasonably well all right cool that covers the bu with that we can work on semic collidable platforms that part is going to be your exercise and let me explain what I want to do inside of level. Pi we have a setup method and in there we are creating the helicopter platform all of that we have done in the last part these platforms should be semic collidable meaning that the player should only have a collision at the bottom there's no Collision on the left right or top between the player and the platform figuring that kind of thing out will require you to understand basically everything we have done so far but let me do a summary the exercise is going to be that I want you guys to make the moving platforms semic collidable I.E they should only be a collision at the bottom of the player also once the player presses down this Collision should be disabled for a short amount of time to work out all of this there are a couple of tips number one you should sted Platforms in another Sprite group I have called this one something like semic collidable Sprites the name here doesn't really matter just skip in mind that organizing all of these Sprites in a separate Sprite group is probably a good idea number two you can reuse quite a bit of the Collision logic that we have already covered in the player you will only need a small part of it finally you will also have to update other parts of the player class for example the floor contact and the move player methods are quite important and would need to change hi there I forgot something that is relevant to this exercise after adding the semic Collision platforms you will get some Collision flaws that will probably be confusing you fix those by wrapping the old W into an INF function and that's basically all you have to do I would recommend to do that right away and then go on with the rest of the exercise try to work this one out on your own and create semic collidable platforms should take you a bit all right let's get started inside of the level we already have two groups and I want to create a third one self dot semic cision underscore Sprites this will be just another Sprite group so pame dos sprite. Group after we have that when we are creating the helicopter platforms we do not want want to place them into the Collision Sprites we want to put them into the semi Collision Sprites simply with this setup the player doesn't have access to these Sprites anymore meaning if I run main Pi we can still see them but there's no Collision because they're not in the Collision Sprites anymore to fix that part when we are creating the player that happens here we have to add these platforms or rather this group as another argument so semic Collision Sprites and as a reminder once again the player itself is not in this group just like with the Collision Sprites the player just has access to them fundamental difference but anyway inside of player. Pi we have to add another parameter semi Collision Sprites also we have to turn this argument into an attribute so self do semi Collision Sprites is going to be semic Collision Sprites perfect with that we have access to these Sprites although at the moment they don't really do anything to fix that we will have to change quite a bit inside of the player the most important part is that I want to create a semic cision method no need for custom parameters in this one and the logic is going to be fairly similar compared to what we have done inside of the Collision method although it's going to be quite a bit simpler we still want to look at all of the Sprites meaning for sprite in do semic Collision Sprites on top of that we also want to check e Collision so if sprite. re. Collide rect and then self. rectangle so far we have done the exact same thing however now there's only one Collision that we really care about and that is the bottom Collision which means we can simply copy this line and paste it in there after we have that we can look look at the move method and call it right after self. Collision self. semi collision and let's see what we get if I now run main. Pi I can jump on the platform and we are not falling through anymore although you can see we have a bit of a bug if I stay on this platform and fall down we have a massive amount of gravity but other than that it's basically working and also the other platform that's looking pretty good and yeah that feels much nicer righty next up then inside of this method even though we are standing on a platform we are still increasing gravity constantly which we don't want to do to fix that all we have to do is set self. direction. y20 and now let's try this again I can jump on the platform we are not falling through it and we get the normal falling behavior and let's try this one as well and there we have a bit of an issue but other than that this part works so at the moment we have the issue that if the platform is moving down like that the player is hopping a bit the reason for that is that we are not applying the platform move method simply because at the moment the player doesn't think there is a platform and this happens because of check contact in there we are updating the platform and at the moment we are only looking at self. Collision Sprites we are not looking at the semic Collision Sprites to make that work let me create another local variable we can call it Sprites the value for this one is going to be what we already have so self. Collision Sprites Sprites and to that I want to have self. semicolons Sprites dos Sprites and I should be explicit if you add dot Sprites after the group you essentially get a list that you can add together and only because of these Sprites can we add these two lists together if you simply added two groups you would get an error but anyway now I want to Loop through all of these Sprites and if we have a floor W Collision then we want to assign a new platform that should also reenable this platform move method because fundamentally it simply cares about having a platform so let's try and if I jump on one of those we get the movement back and that looks pretty good and if I jump on the other platform I have to wait for it to go down and we are simply falling through the entire thing and that happens consistently so what what's going wrong now the best way to diagnose that is if I minimize all of the methods we can look at the update method and then print self. on Surface and now let's try this again and you can see by default when I'm just standing on the terrain floor is true however if I jump on the moving platforms floor is false also if I'm on one of those I cannot jump because we are not on a floor and if I'm on the other platform we also get floor as faults and at some point I'm falling through this platform the reason ultimately here is that the game thinks when we are on a moving platform that we are not on the floor and that in turn happens because of check contact we are only on the floor if we are colliding with the floor wrecked or if a part below the player collides with this rectangle the way around that is that we have to create one more list and let's call this the semioli rects and this is going to be another list comprehension I want to get sprite. rect for sprite in self. semic Collision Sprites and then we want to check that we are on a floor if this condition is true but we also want to check so or if our floor rectang angle as a Collide list with the semic Collide rectangles and the return value needs to be greater or equal to zero also there's another condition because this we only want to do if self. Direction doy is greater or equal to zero remember we only want to have a bottom Collision if the player is moving up we shouldn't have any collision with these moving platforms so with that let's try m.p again and we are still printing on Surface and now if I jump on a moving platform we getting floor is true which means we can also jump on it and that feels much nicer let's try the other platform and we are still falling through so something is not exactly right but we are making progress so at the moment we can at least jump on this platform and that was the one part of the tutorial that might have been a bit hard because the reason for it is if you look inside of the player the way to fix it is you have to wrap self. old. bottom inside of an INT function if we now run main.py again everything works just fine and this looks much better and we can also jump the other platform works pretty good let's try this again and yeah this looks fine sometimes P games seems to have problems with the Precision of floating Point values and usually once you add an INT function to it it solves everything as a matter of fact for better accuracy I would recommend to wrap all of the old rects into an in function let's do that in general like so and now if you run main Pi the Collision shouldn't be affected too much or at least it should work just as before and and now this works still just fine and that is looking good cool sorry about the last part that was a bit confusing but anyway with that we now have semic collidable platforms next up I want to be able to if we are on top of one of them if I press down then we should fall through them and to make that work we we will need a timer basically if I minimize everything again and get rid of the print statement we want to add a couple of things number one we have to add another timer I have called this one the platform skip timer once again it's a timer class and now we need a duration I went with 300 milliseconds and this time up we now have to activate which Happ s inside of input let's do it right below the input for the left key I want to check if Keys P game. Kore down I.E we are pressing the down key if that is the case I want to get self. timers and then the platform skip timer and activate it that is all we need inside of input next up we want to work inside of semic collisions because all of this should only be available if the timer is not active meaning we want to wrap all of this inside of one additional if statement if not self. timers and I call this one the platform skip timer only if this is not active then we want to do all of this and now if I run main.py and I am on one of those platforms I pr down and we are falling through it and that works consistently let's try the other platform and that also works just fine also I'm realizing there's one issue and you might have seen it before already if I jump on a platform and I just barely land then we're getting rid of the upwards movement pretty abruptly it's if I do it multiple times I think it's quite easy to see the way to fix that is also inside of this method we only want to set self. Direction doy if we are moving down so if self. Direction doy is greater than zero then we want to set it to zero and now if our remain Pi again this feels much better and cool I am quite happy with that that looks about right perfect we are making a ton of progress and that was a pretty tough exercise I hope you got at least quite far into it next up we're going to work on two simpler things and first of all I want to give the player a hit box and to illustrate why that is important let me update the player image at the moment we have a plain surface that we are filling with a red color it's a good start but it's also Fair fairly boring I want to replace that with one of the idle frames of the player which I have to import so py game. image. load to import an image and to get the proper path we will need like we have used in main from OS import join then we can use the join method to go up a folder then I want to go to Graphics in there we have the player in there we have Idol I believe I called it and in there we have a file name called 0.png just to double check here is the project folder at the moment we are inside of player. Pi from there we want to go up a folder then to Graphics in there we have the player and there we have idle and then there we have z.png that looks good also I want to get rid of self. image. fill and now if I on main.py we can see we have the player graphic or at least one frame of the player graphic and there you can see quite well that this looks very weird there's a ton of white space around the player and well we get the collisions even on the right side so what happens here basically what is happening is that this is our player graphic at the moment and the actual size of the graphic is this big and that size is important because later on when the player moves to the left we want to lip this entire graphic and then the shape would be something like this but the actual size of the graphic wouldn't be affected which makes our math a lot easier that being said though the actual thing that we want to show is this large and the actual Collision should only be this large meaning we want to have a hitbox that is excluding The Sword and parts of the head and the arm and to implement all of that we have to work inside of the player most importantly after we are creating the rectangle I want to create one more rectangle that I will call hitbox rectangle which is going to be self. rect and now we need the inflate method inflate is ultimately quite a simple method it simply takes a rectangle and then resizes it around a center point for example if this is our current rectangle with this Center Point if we at negative numbers into inflate is going to give us a smaller rectangle let's say something like this but it has the same origin Point as a matter of fact let me add the proper numbers I want to inflate this rectangle by 76 pixels on the horizontal axis and - 36 pixels on the vertical one that means that on the left side we are shrinking the rectangle by 38 pixels or 76 divided by two and on the vertical axis we are shrinking it by 18 pixels on each side or 36 in total and that way we are keeping the same origin point which is important because later on we have to make sure the hitbox and the actual rectangle are aligned that is important because basically what we are going to do the entire movement and collision Logic for this rectangle will be done via the hitbox rectangle and then after we have done all of the movement we are going to set the position of the rectangle to the same position as the hitbox rectangle basically we want to work inside of the move method at the moment we are always updating the rectangle but instead I want to update the hitbox rectangle and that we want to do for all of the rectangles when we are moving down we want to update this rectangle and also when we are jumping so basically every time we have self. w we want to replace it with self. hitbox wct also this has to happen inside of collision and semic collision so let's do it right away I want to check for sprite in self. Collision Sprites and then sprite. rec. cidere with self. hitbox rectangle and then we have to update all of these rectangles as well that belong to the player that should be it next up we have to do the same thing for the semic collisions and there we have at least slightly fewer rectangles those three should be it and now basically what we have to do is after we are doing the movement we want to set self. re. Center to self. hitbox rectangle do Center so in the most basic sense all that we are doing when we are creating the player we are creating a hitbox rectangle that is smaller than the original rectangle and this hitbox rectangle is going to be responsible for all of the movement and all of the collisions which is happening in here and after we have done all of that we are setting the center of the rectangle to the center of the hitbox rectangle this is almost going to work there's just one thing I forgot because we also have to update check contact because in there once again we are using the rectangle but this should always be the hitbox rectangle so all of the rects should basically be hitbox rectangle and now let's try all of this again the game doesn't crash that's a good start and now we have no horizontal collisions that's something we have to work on and those collisions also don't work so something is working working but we have to fine-tune things a bit and you might have already spotted the mistake inside of player. Pi what we are not doing is updating the old rectangle and this we are also not doing inside of the init method so this instead of self. rec. copy should be self. hitbox rec. copy and this line I want to do in the init method and inside of the update method and now we're trying all of this we have no collision between The Sword and the level and that feels drastically better on top of that if I try the platforms oh I realized I forgot one thing the hitbox rectangle does not move along with the platforms and that way we're getting the jumpy Behavior again that is easy to fix inside of layer I want to minimize everything again and move and work inside of platform move I want to update self. hitbox rec. toop left and then apply the platform movement and now if we try main.py I can jump on a platform and that looks much better let's try the other platform as well and that looks pretty good cool I can also jump that covers another part and I do realize I have changed lots of stuff inside of this class quite quickly but basic basically I simply replaced all of the rectangles with hitbox rectangle and the only place where we are updating the rectangle is here where we are setting the center of the rectangle to the center of the hitbox rectangle and all the other Collision checks are being done with the hitbox rectangle same for the movement cool and that covers another important part finally we can work on the camera that's going to become quite important because at at the moment we can only see a very small part of the level which obviously isn't ideal the way you want to think about it is at the moment we have a window that looks something like this I think the dimensions are 1280 by 720 although the specific numbers don't matter in there we have a player and we have some levels so there's some bits here we have some bits here and we have some bits here the part that goes further to the right we cannot see so anything that goes further than 1280 to the right is invisible now there's one limitation already and that is we always have the origin point in the top left this point will be zero and zero and this cannot be changed as far as P game is concerned we always have a window that starts up there goes up to 720 on the vertical axis and up to 1280 on the horizontal one and every element is drawn relative to that so what we can do is when we are drawing all of the level elements we don't have to draw them from the origin point of 0 and zero instead we could change this point to something else let's say negative 50 and zero that way we would be shifting everything a bit further to the left and see more of this part I.E we are basically drawing everything with an offset and if you make this offset relative to the player position then we are getting a camera that always follows the player and by the way for all of this I have made a dedicated video if you want to learn more about the details but basically the way you would approach it inside of level. Pi we currently have one Sprite group that contains all of the Sprites and the drawing logic of this group we want to customize and for that I want to create a new python file let me save this one as s groups. py and in there we want from settings import everything after that we want to create a class that I usually call all Sprites this one has to inherit from pygame dos sprite. group most importantly to get started we will need a Dunder init method without any custom parameters that has to initialize the parent class meaning super and thunder in it with that we basically created our own custom group that works exactly like an inbuilt py game Group which means inside of level. Pi we can from groups import all Sprites and then instead of py game. sprite. group use all Sprites the result should be the same if I run m.p that looks pretty good no change that's at least not a problem but we also didn't create a cam camera to work on that we have to first of all understand what is bright group actually does inside of the draw method and it's actually really simple by default when you're calling the draw method of a group you have to pass in a Target surface let's call it surface for now and then inside of this Method All That the group does is it Loops over every single Sprite that is contained inside of this group and this you can do with four Sprite in self if you call Self then the group is going to return all of the Sprites inside of it and once you have that all you really have to do is serve. blit with sprite. image and sprite. wct which means now if I run main.py we still get the very same outcome even though when we are looking at the run method this draw method is now completely customized we have overwritten the default draw method of a group one way to demonstrate that is inside of the init method we can for example create the display surface which is what we want to draw on anyway and that once again we get with py. display. getor surface and then use this display surface to draw on because of that we don't need the argument for the Target surface anymore and we can also get rid of it inside of the draw method we call it inside of level. Pi now I can run all of this again and it still works just fine so with that we have recreated the basic drawing logic now we can customize it and for that I want to create self. offset which is just going to be another vector and to demonstrate what this one is doing let me give it a custom value 500 and0 now that we have that inside of the for loop I want to create an off set position which is going to be sprite. rec. toop left plus self. offset and this new position is what we will use for the actual drawing position so in there we get offset position if I now run may not Pi you can see that we have moved the entire level 500 pixels to the right but everything else still works just fine and a really important thing to understand at this point is that we haven't actually changed any position of the actual rectangles in the game for the player and for all of the Sprites the rectangles have stayed exactly the same as far as they are concerned they are still in exactly the same position the only difference is when we are drawing the entirety of the game we are drawing it with an offset and that way we are getting a camera and because of that we can create create a camera without affecting anything else in the game all we have to figure out now is how to align this offset with the player position first of all for that we need the actual player position or more broadly I want to have a Target position and that we get inside of the level I want to pass in self. player. w. Center or I suppose we could be a bit more specific here this should be the hit box rectangle of the player in practice though both would work just fine but anyway I don't think self the player is available yet because when we are running setup we are only creating an instance of the player but we're not storing it that is very easy to fix self. player is going to be the player object and now we should have access to the player let's try M notp and this is still working just fine cool first of all I want to set the starting value of the offset Vector to 0er and zero which are the default values so if I leave it empty that's all we need and after that I want to get self. offset and update the horizontal part the value for that is going to be I want to have the target position but only the X part or index zero and since I want the player to always be in the center of the window I want to subtract window wi ided by two and just to explain what this window wi divided by two means if this is our entirety of the window we always want to have the player exactly in the center of this window which means on the horizontal axis we want to have exactly half of the window width in terms of distance between the center of the player and the origin top left point and this part is almost going to work if I now run made of Pi we get some very trippy Behavior the reason for that is that we have to invert all of this this should be negative if I now run this again there we go we have a camera that is working really really well and now we can see the rest of the level also just fine and the reason we needed negative numbers for this one is because the ferv the player is moving to the right for example the more leftwards we want to draw the level or if I draw all of this once again we have a basic level we have something here something here and then something larger here we have the player on this position and the window itself looks something like this if the player is moving further to the right then we want to draw everything else further to the left I.E in exactly the opposite Direction because of that we have the negative the same thing we want to do for the vertical part so self. offsety is going to be Target position 1 minus window height / it by two and then we should be done if I now run main. Pi we get the player always in the center of the window and that looks pretty solid so with that we have a basic platformer that works really well and there we go he there before we finish there is one thing I realized that didn't work perfectly well and that happens inside of the collisions so if you go to player and collisions earlier on for the collisions we added the integer because otherwise we got some minor bugs and I realized you have to add both the old RCT and the Sprite old RCT in an in function so let's do it right away and that's all you have to do and for now even without the in function all of this would have worked just fine however later on we will get some occasional funky behavior and adding the code like this fixes all of that now truth be told I don't know exactly why this is necessary in the first place but once you have it all of your collisions are going to work perfectly so we don't have to think about it too much but anyway with all of that we have finished the basic platformer welcome to the second part of this tutorial at this point we already have a basic outline of a platformer which works reasonably well but it doesn't look particularly good and that is what we are going to work on by the end of this part we will have a proper looking platformer that feels like an actual game most importantly for that we have to add all of the graphics that is going to be a major chunk and on top of that we are also going to add more interactivity by that I mean we're going to have some enemies we are going to have UI elements we are going to have some items and we're going to have a couple of traps quite a bit to work on and for all of that we will have to go through a few stages the first one is going to be all of the basic Graphics of the level that's going to be a fairly straightforward part however there is going to be one major bit that we have to be aware of for this entire part and that is importing Graphics if you look at the project folder fer and you open the graphics folder you can see that we have a ton of material to work with for example simply looking at objects we have a whole bunch of them and when you come to animations it gets even worse so for example under items we have one animation for every single item and then if you look at the player for example the player has a ton of states and every single state has a bunch of Fram as well I don't know how many Graphics we have in total but there are a lot and importing all of them manually is not going to be a pleasant experience so I am going to emphasize how to import files in bulk in pame during this entire section but for now that's not too important because there are a couple of things we can import quite easily because at the moment we can work inside of level. Pi in the setup method we already already have a for Loop to create all of the Sprites from this for Loop we are getting a surface that we are passing into the Sprite and then in this Sprite we are capturing it via a parameter and then assigning it to an image the only reason we can't see it is because we are filling this image with a white color if we get rid of this white color and run all of this again we already have a basic level and this is looking much better now we still have the Platformers you can see them right now or well you can at least see that the player is moving since they are black and with a black background they are not too visible but that's going to be a problem for later for now I am quite happy with this and just to make sure that we can see the moving platforms after we calling super. init I want to self. image do bill with let's say a white color and now we should be able to see them again and that feels a bit better and there you can see we already have the basic outline of the level on top of that we do have a couple of tiles that are very easily importable if you look at tiled we have a couple of tiled layers at the moment we only imported terrain but we also have FG platforms and BG BG by the way is all of the background stuff platforms are a couple of extra platforms and finally we have FG this one is a bit more difficult to see but if I zoom in FG for example is the grass that should always be on top of everything else importing all of those is actually fairly simple we simply need another for Loop let's call it for layer in and then I want to have a list with BG I want to have the ter Rin then I want to have the foreground and then I also want to have the plat forms after that this for Loop that we already had for the terrain is going to become an inner for Loop and then we can replace the terrain with the layer and now if I run all of this we get a whole bunch of stuff although we have to customize things so for example these platforms are collidable and they should be just like the moving platforms only collidable from one side so we should be falling through through them also the grass should not be collidable same with the background stuff this one should just be in the background behind the player we shouldn't be able to collide with it which means inside of this for Loop we have to customize a couple of things most importantly we want to have a local variable called groups and by default the value in there is going to be a list with self. all Sprites and then if the layer is terrain then we want to groups and append self. Collision Sprites after that we can check again if we have the platforms and if that is the case we want to attach semic Collision Sprites with that we can replace all of the groups we had by default with the local group groups variable and now we have a much better looking system and the platforms should already work and that looks really good now the moving platforms here are getting a bit annoying but well that's something you can fine tune this is just an experimental level so it doesn't have to work perfectly and I suppose one thing you could be working on is the downward fall so if I press down we are falling right through the moving platform if you customize the falling down timer that is this platform skip timer and we could try to set it to 100 and now this should be working better yep that feels better so once again experiment with all of this but that you could do in your own time as far as I'm concerned all of this is working reasonably well however there's going to be a bit of an issue and that is if I jump in there okay it actually works but the grass for example should be in front of the player and more generally I want to have control which elements are in front of other elements so at the moment all of this is only behind the player because we are creating it before we are creating the player if we switch that around the player would be behind everything else we can actually try that and run may not Pi if I know go behind the stuff the player is not visible anymore and obviously that's going to be a problem although I want to create a player afterwards so we want to have control over the drawing logic and for that if you look at settings. Pi we have a variable called Z layers and this is going to determine when things are being drawn for example the background will be drawn first then we have the clouds BG tiles path and so on and the later something is drawn it will always be on top of the element before so FG elements will always be drawn last hence they on top of everything else we just have to figure out how to use this system and for that first of all we have to work inside of groups. Pi because what we want to do in here is sort all of the Sprites and that we do via sorted sorted wants to have some kind of list that we're getting with self and then it wants to have a key which usually is a Lambda function where we have to specify one parameter which I usually call Sprite and then whatever the Lambda function returns is going to be sorted by the sorted function and the sorted function for that is expecting an integer that it is then going to sort from the lowest to the highest value which means we want to get this Sprite and then return a that index now this one doesn't exist right now in a second though we going to get that number from this variable as a matter of fact that's going to happen right now when we are creating all of these Sprites I want to be able to pass in one more argument and that is going to be the Z number which we don't have right now so let's create it and to get that I want to get the Z layers the dictionary we are getting from settings. pi and in there I want to get one entry for example I could get the BG tiles for now let's leave it like that so all of the tiles we are creating will be in the background although for that first of all we have to create inside of the Sprite one more parameter and that is going to be Z now this one should also have a default parameter I want to get Z layers and then Main simply because main is where most of the game is happening so this should be the default value after that I want to create an attribute self. Z is going to be Z next up we have to work inside of the player because this one will also need a z attribute let's put it right below self. image self doz is going to be and the player should always be on the main layer so this one should be main all right and with that let's try to run main. pi and you can see the player is on top of the grass but what should also be the case is that the player is on top of this background and this is going to work regardless of when we are creating things so even if I create the player before all of these tiles and run main P we should still have the player in front of it and that looks really good cool and now I can explain this Logic the way you have to think about it is that the player has a Sprite do Z which I believe is five yeah so player. Z is five and everything else has PG tiles so Z for them is going to be two and sorted is going to sort all of these Sprites by that number and the higher the number is the lat an element will be drawn and as a consequence that element will be on top of the earlier Sprites the system ultimately is n that difficult but I think when you get started it can be a bit confusing so I hope it makes sense but anyway now we can work inside of level. Pi because this Z number isn't correct yet I basically want terrain and platforms to be on the main layer however BG should be inside of BG tiles and FG should be in the foreground one way to approach that could be with a match case statement so I want to match the layer and then one case could be BG if that is the case that should be that layers and for that I do want to keep the BG tiles another case would be the FG and if that is the case I want to assign that to the foreground or F G and for everything else we want to have the main layer which means terrain and platforms are going to be the cases and both of those we can have with case underscore that's going to cover everything else if that is the case that should be main let's try all of this and now you can see that the grass is in front of the player kind of hard to see but there that's kind of more visible visible however more importantly all of this background stuff is behind the player and that still works perfectly fine cool so with that we have made some pretty good progress in terms of how we are drawing things that means we can minimize this for Loop and I also want to have the player after the tiles that isn't strictly a requirement it just feels better as a matter of fact we are now going to add all of the objects for that once again let's have a look at tiled here we are and at the moment we have a whole bunch of layers and the only one that I care about is objects so let me hide literally everything else because at the moment it's not going to be necessary we are already importing one object and that's the player but on top of that we have a couple of palm trees we have crates we have spikes we have the level finish flag and on top of that we have one object that is going to be a saw later on all of these things we want to import and that import is going to be a bit of an issue now you could use tile to get the graphics for example all of these palm trees are simply available via obj do image but that's not what I want to do and that is because if I open the project folder we are right now inside of graphics and there I want to go to levels there we have The Palms and there we have all of the different palm tree animations and that's the issue all of these are animated I don't want to have a single image I want to import a whole sequence of images for every single palm tree and that I do not get from til all of that we have to do semi-manually however what we can do is use another python file and that is support. p that's already in our project if you're open that one you can see four functions that can Mass import basically any kind of folder now for this tutorial I am not going to explain all of this in detail however I have made a whole separate video explaining how all of these functions work in that video I also cover how to import tile Maps but for this project that's not necessary basically the way you want to think about these functions is that they cover two important things number one they are platform independent we are using a join method to create a path dynamically that way for example for different operating systems you might need either a forward slash or backward slash for the path and join gives you the right one automatically on top of that a lot of them do Mass Imports let me actually go through all of them import image is the easiest one and This One Imports a single image all you have to do is specify a path you can also specify if there are alpha Val values and what the file format is going to be that being said though there are defaults where Alpha is true and the file format is PNG so most of the time you simply have to specify a path that's the easiest function and that doesn't do very much the next one is much more interesting import folder This One Imports all of the images inside of a folder and places them inside of a list the end result is going to be a list with surfaces that you could for example add animate through after that we have a somewhat similar function this one is called import folder dictionary it works more or less like import folder but the difference is that now we are getting a dictionary where we have the name of the file as the key and the actual surface as the value for import folder we just get a list with surfaces we don't know what the names are which a lot of the times we don't actually need but import folder dictionary retains that data finally we have import subfolder folders this one is going to be used for something like the player where we have one main folder and then lots of different subfolders for all of the states of the player if you look at Graphics in there we have the player this entire folder we want to import and in there we have lots of subfolders for example we have idle and in there we have more animations what import subfolder is going to do it is going to create a dictionary and the keys of this dictionary are going to be the folder names and then the values are going to be a list of surfaces for each of these folders once again if you want to know more about it check out the dedicated video but for now we can work inside of main.py and import a whole bunch of data first of all for that I want from support import everything and then since we are going to import a whole bunch of stuff create a new method let's call it import assets no need for custom parameters in this one and for now what I want to do is create self. levore frames this is going to be a dictionary in itself and for now what we want to import for example I suppose what we can start with could be the flag for that we want to look at a project folder we have Graphics then we want to go to the level and in there we have a flag a simple animation and this we want to import which we are going to do with the import folder function and then we will need a path we want to go uper folder then we want to go to Graphics after that we want to go to level and finally the folder we want to import is flag and after all of this let me print self. level from frames also make sure that you are calling import assets if I now run all of this we can see that we have a dictionary with a flag key and the associated value is a list with a whole bunch of surfaces and this is the power of these functions if we didn't have them we would have to run py game. image. load for every single file inside of this folder which would be a nightmare all right but now what I want to do is get these level frames into our current stage that part is quite easily done we simply have to add it as an argument self. level frames the one important thing here is make sure you are calling import assets before you are creating the current stage so next up then inside of level. Pi we have to create one more parameter level frames and those levels frames we want to pass into this setup method right away level frames for that inside of setup we will need another parameter level frames and now we can work with all of those at the moment we are only checking for objects if the name is player but there are quite a few more let's simply check the else case so we are covering everything but the player to get started in here we can actually start with an easy part if objname is in a list of barel or rate if that is the case and let me show what we are doing in til we are currently looking at these free objects where we have a barrel and a crate those are not going to be animated meaning we can import them straight away from til for that once again we can simply create a Sprite and then we need all of the Sprite arguments let me copy them actually so we need position surface groups and z now Z we can simply leave out because I like the default argument here all of these Sprites should be on the main layer then for groups I want to have a list with self. all Sprites and since these Sprites are supposed to be collidable also self. Collision Sprites position and surface then are also going to be fairly easy the surface is obj do image and position is going to be a list with obj dox and obj doy that should already be enough if I now run main. Pi we have a whole bunch of crates and that looks pretty good cool that means once more we can add an else statement for now in there I want to create the frames and those we are getting via the level frames and then I want to use the objname the way this is going to work objname we getting from til if you look at that for example if we look at the flag objname is flag the same flag we have used as the key inside of the level frames dictionary and at the moment I want to do all of that only if pal not in objname so for now we are going to ignore all of the palm trees because they're going to work a little bit different I cover them in just a second after we have all of that let's print the frames that we are getting and I'm pretty sure we are going to get an error let's run main. pi and we are getting a key error that pame cannot find the key saw inside of level frames and that should make sense if if we open tiled again we are at the moment ignoring all of the palm trees and if you look at them you can see for the name we have something like Palm small or Palm BG or Palm BG right all of these Palms are different kind of animations and the one thing that connects them all is that for the name we always start with palm however we also have a saw and we have floor spikes those we don't have inside of the dictionary right now meaning we have to import them we first of all have to get the saw that one is going to be an animation as well so I want to import folder and let me copy in the path that's going to make my life a bit easier we want to go up a folder then to Graphics then to enemies then saw and then animation and after that I want to have the floor Spike this one once again is import folder we go to the same enemies folder and then we import floor spikes if I now run main. Pi we're not getting an error anymore and instead we get a whole bunch of frames these frames we want to use for an animation but step by step at the moment we are still working inside of this if statement and the issue we have right now is that we do have a Sprite class but this one isn't animated meaning we need to create inside of Sprites dop something like a animated Sprite class I want to inherit from Sprite the class we have just created that way we can save a bit of code for that I want to create a Dunder init method and then we will need a position we will need some frames we will need the groups we should be able to specify the Z layers although this one should get a default value with it being on the main layer also finally I want to be able to specify an animation speed although this one also gets a default value which will be the animation speed variable inside of settings so this should be the default value and after we have all of that we can work on initializing all of this most importantly I want to call Super and then Dunder init that way we are calling the parent Dunder init method and in there we have to specify a position a surface and the groups also the layer position is going to be fairly easy because this one we're getting from the arguments right away same with the groups those we don't have to worry about and also Z is covered the one thing that's going to be a bit more difficult will be the surface for that first of all I have to create a couple of attributes and those are going to be self. frames and self. frame underscore index the value for both of those are going to be either the frames or zero and as a reminder self. frames is going to be a list with surfaces and from this list we are going to pick one surface via the frame index an easy way to do that to get started is when we are initializing the parent class I want to get self. frames and then use self. frame index for the indexing and that way we have an animated Sprite that isn't animated yet but at the very least it shouldn't crash which means inside of level. Pi besides the Sprite I also want to import the animated Sprite and by the way in just a bit we are also going to update the moving Sprite to be animated which we achieve by making it inherit from the animated Sprite but that's not too important for now when we are working inside of the objects I want to create an animated Sprite and for that we will need a whole bunch of arguments all of those and then the animation speed position is fairly easy we can simply take them from the object the frames we already have this is what we just created groups for now is simply going to be self. all Sprites and then just to keep it simple for now I want to ignore that and the animation speed now let's try all of this and I can see I have the spikes I have the saw and if I go further to the right then we can see the flag that looks pretty good the issue now though is that none of those are animated and for that we will have to work inside of animated Sprite a bit more although before we get to that I want to create another attribute self. animation speed which is the same value as the animation speed from the par parameters after that we want to create an animate method with self and Delta time and basically all that we are going to do is we are going to increase the frame index by self. animation speed and multiply this with Delta time Delta time for this one is fairly important we were to make sure that these animations play at the same speed on every kind of computer after we have that we want to up St self. image and essentially all that we really want to do is pick a new Surface from self. frames which means I want to get self. frames and then use indexing so self do frame index however this is not going to work for two reasons number one after we are running this line self. frame index is going to be a floating point value and we will need an integer for that I want to turn this number into an integer the next problem is that this number will keep on growing forever that's an issue because self. frames has a finite number of surfaces hence we are going to get an index error really quickly because this index is too large for self. frames to overcome that we will need the modulus operator where we want to get length and then self dot RS that way frame index will never exceed the length of self. frames and with that we are animating all we have to do now is to call the update method with self and Delta time all of that is already happening and then call self. animate with Delta time and now if I run m not Pi we can see we have animations that looks pretty good and if I go to the flag this one should also be animated and perfect that looks really good with the basic parts of the level out of the way we can start working on the player this one is going to be quite a chunky class although since we already have some really good Basics this shouldn't be too difficult let's Jump Right In once again we are starting inside of main.py and what we have to start with are the assets for the player this is just going to be another import I want to have a player key and the associated value is going to be import subfolders like what we have done for the palm trees the file path for this one let me copy it in is going to be onefold up graphics and player after we have done all of that I want to print self. level frames and then the layer entry if I now run all of this we are getting all of the player graphics and there are quite a few but ultimately and I already mentioned this we basically go by States for example one state could be the air attack another state could be hit or jump and associated with every state we have a list of surfaces that's the animation all of that we have to get into the player class which happens inside of level. Pi in this setup method because in there at some point we are creating the player now to keep this one a bit more organized I want to use named arguments the first argument is the position the second one was the groups we had the Collision uncore Sprites and finally we had the semic Collision Sprites I want to add one more argument and that is going to be the level frames and then the player Graphics after that we we can work inside of player. piy and first of all we will need let's call it frames at the moment our player image is simply going to be a single idle frame although that we don't want to use anymore instead let's organize all of this a bit better I first of all want to have the general setup part in there we are initializing the parent class then we are declaring that and that's all we need in there after that we are getting the image and for that like we have done in the animated Sprite this one we want to store the frames and create a frame index I can just copy all of this and we have self. frames and the frame index and just to make sure that this is working let me get rid of the graphic we have loaded in so far and instead get self. frames and now we need one of the animation States this could for example be idle and then we get a list of surfaces from which we want to get one surface via indexing which we get via frame index if I now run main. Pi we are getting an error that positional arguments follow keyword arguments that happens because I forgot to add the named argument frames in there now this should be working and there we go we basically have the same result we already got before however what we can do now is pick another state for example I think one was called attack if I run out of this we get the first frame of the attack animation all we could do jump and then we get the first frame of the jump animation let me return this to idle and now all we have to figure out is how to manage the states and how to do the actual animation part also we have to to figure out if the player is facing left or right meaning we have a couple of things to work on first of all I want to create two more attributes first one is called State the second one is going to be self. facing right the values for this one are going to be idle and R the default state of the player is going to be idle and we are facing right by default all of the graphics for the player are facing right and then when the player is turning left we are setting this to false and then when we are drawing the player we are going to invert it on the horizontal axis okay then finally I want to update this line just a bit instead of getting this idle I want to use self. state that isn't going to make a massive difference we are still getting the same result but it feels a bit cleaner after that we can minimize the dunder init method and let me create all the way at the bottom a Define animate method with self and Delta time we want to do something fairly similar compared to what we have done inside of the animated Sprite once again basically all of this although for the player we want to get self. frame index and then increase it by the animation uncore speed this value doesn't need to be randomized because we only have a single player although we do have to multiply it with Delta time after we have that I want to update self. image which is going to be self. frames and then we want to do something different compared to what we have done on this line for animated Sprite the frames were just a list of surfaces but in the player we have a dictionary where we first of all have to get self. State and after that we getting a list of surfaces only in the there can we get the integer of self. frame index with modulus of the length of self. rams and don't forget this also needs self. State let's try for that inside of the update method we have to call animate and since we have quite a few methods in there I think it makes sense to organize this a bit better we start with the general updates then we have the input Ander movement and after that we are going to get self. animate with Delta time let's try that one and we are getting an error that we are trying to add together a dictionary with a float let's see what's going wrong and the issue we have is that we are trying to update self. frames this should be self. frame index and there we go we have a player animation that's a start but we do have to refine a couple of things first of all if the player is going to the left all of this should be flipped for that first of all we have to know if the player is going left or right in the first place the best way I find for that is to Simply look at the input method because if the player is pressing right then we know we are facing right so this one should be true however as soon as the player is pressing left then facing right should be false because now we should be facing left after we have all of that we can work once again inside of animate because now we want to update self. image once again the value for this one is going to be self. image but only if self. facing right is the case if that is not the case then I want to flip the current image on the horizontal axis which I do with py game. transform. flip the same method that we have used for the spikes except now we want to flip it on the horizontal axis not the vertical one once again for that we will need three arguments the surface if you want to flip it on the horizontal axis or on the vertical axis in our case the surface will be self. image we do want to flip this one on the horizontal axis so true for this one but not on the vertical one so false here with that let's try main. pi and now if I go left we have inverted the pirate and everything else still works just fine that looks pretty good and already feels significantly better although we are always playing the idle animation for that I want to create one more method get uncore State no need for custom parameters and in there we're going to update the state that determines what animation we are playing for now I want to check if self dot on Surface and I want to check if the player is on the floor if that is the case I want to set self. state to idle but only if self. direction dox is equal to zero if that is not the case else the player should be running I think the logic here makes sense if the player's on the floor and self. direction is zero then the player is idling however if the player is on the floor and direction is different from zero then the player is running all we have to do is before we are animating the player we want to self. get State let's try may not P now and now the player is moving but only if we have horizontal movement although if we are jumping none of that is going to apply after that we can add an else statement where we want to check if the player is on the way wall or just generally in the air which we can do with if any and then self dot on Surface with left or self dot on Surface with right if either of those conditions are true we know that the player is in the air and colliding with a wall if that is the case self do state should be the wall however if we don't have any of these conditions meaning we are in the air but not colliding with a wall then self. State should be jump however that should only be the case if the player is going up if the player is going down it should be fall which means if self. Direction doy is smaller than zero I.E we are going up and we are jumping but if we are going down else then the state should be full and now I already spotted one mistake inside of any we have to add a tupal or a list let's try all of this now and we can still walk around on the floor but once the player jumps we have a jump and a fall animation and after that let's try a wall and that looks really good we have a proper wall slide let's try the other side as well and that looks pretty good we are making loads of progress now there are two more animations that we need to be able to play the ground attack and the Air Attack we do have the animation frames but we have no way to trigger either of them also at the moment we are only going to play these animations they're not actually doing anything first of all inside of the input we need to check for one key that can trigger an attack and let me add a bit more white space in here that feels a bit crammed I want to add one more if statement if keys and py game. Kore I went with the X key so whenever the player presses X we want to attack if that is the case I want to run a method called self do attack that method doesn't exist right now I'm going to create it right below input attack with self no other parameters needed if we are pressing attack I want to do most importantly get some kind of attribute that is called attacking and set this one to through this self. attacking doesn't exist right now so let's create it and I suppose we can do all of that under Movement by default it should be false after we have that I want to work inside of animate the important thing now is that for the animations whenever the player presses attack I only want to attack once after the the animation is done I want to go back to some kind of idle state which means I want to check if self. state is equal to a tag on top of that I want to check and self. frame index is greater or equal than the length of self. frames of the current state so self. state if that is the case self. State should go back to idle I hope the logic here makes sense if the current state is a tack we are checking if the current frame index is greater than the length of the attack animations and if that is the case we are setting the state back to idle however at the moment we don't actually get to this state being attack because remember the attack method only sets an attribute called attacking to true we do not actually update this state for that we want to work inside of get State first of all let's cover the state if the player is on the floor if that is the case we also want to check if self. attacking if that is the case so we are on the floor and we are attacking then self. State should be a tag and the other state we are only checking if that is not the case meaning it's going to be inside of an else statement something similar we are going to do if we are in the air so in there we are going to check if self. attacking and if that is going to be the case then self. state will be the aircore attack and only if we are not attacking meaning else we are going to cover the other states let's now try all of this and if I press X nothing is going to happen and I think I know why inside of the player when we are starting the tag we also want to set self. frame index to zero to understand that one imagine that we are just playing the game normally and frame index is doing something let's say at the moment frame index is five and then we are switching to the attack animation since frame index already is at five we are basically immediately at this stage and as a consequence we go straight back to an idle animation and we can never see the attack to overcome that we are setting self. frame index to zero so that way we are starting the attack animation from frame zero let's try main. Pi now and if I press X we get an attack animation that looks pretty good also if I jump and press X we get something not perfect yet also if I keep on pressing X we get something really weird you might have already spotted the mistake basically I can attack whenever I want but ideally there should be some kind of cool down that we can only attack let's say twice a second which we can do quite easily inside of self. timers I want to have one more entry I call this one the attack block timer which is going to be a timer with a duration of 500 millisecs now as soon as we are calling attack I want to activate this timer so self do timers and attack block dot activate on top of that I only want to allow all of that if the attack block timer is not active meaning if not self. attack timers do active basically the logic is going to be we are only able to attack if the timer for the attack block is not active however once we are attacking we are activating this timer meaning this method should only be able to run twice a second or every half second let's try all of this again and now I can keep on pressing X and we always get the full attack animation that looks really good and if I jump there something is going wrong so as soon as I jump we are attacking and that's not ideal the reason for that is that we are never setting self. attacking back to fults as soon as we are pressing it once it will always be activated the way around that is once again inside of animate we want to check all the way at the end if self. attacking is the case and self. frame index is greater than the length of self. frames with self. state if that happens to be the case I want to set self. attacking back to false and now let's try an air attack and we only get one attack that looks pretty good and also if I'm on the floor we only have a single attack and with that we are basically done these are all the animations we need for the player wasn't actually that hard and a bit shorter than I expected although there's one thing I would like to work on and that is if you look at the attack animation for the sword The Grass Is Always in front of it and that looks kind of weird so I'm going to set the grass to be behind the player that way we are solving all of that if you want to challenge yourself this could be a good exercise for you although it's not going to be a proper one basically we want to work inside of the level and the grass is always on the FG layer which we are checking for inside of this match case statement and all we really have to change is FG should be BG tiles if I now run may not pie again the sword is in front of the grass which looks much better now if you want to spend a lot of time on it you could separate the player graphic and the sword graphic so that the player is behind the grass and the sword is in front of it although that would be quite a bit more work in my case I am happy with this and we have covered another important part of the game for the next part let's work on the mve objects and we already have parts of that the platforms that are going up and down are basically done the only thing that we have to do is to animate them that shouldn't be too difficult but on top of that we are going to have a few more objects an easy one would be a boat that is just moving left and right but on top of that we have a saw and much more importantly we have a spike that is going to go in circles for that I will have to cover a bit of trigonometry but it shouldn't be too difficult let's get get started with the easier bits once again we are in the code editor and I want to work inside of level. Pi more specifically inside of setup and in there we already have a couple of moving objects for now the only thing that we are looking at are the helicopter platforms and that's a good start but I am going to have other objects that will move like the helicopter platforms as a matter of fact let's have a look at tiled the only layer that we really care about at the moment are the moving objects so let me hide everything else there we go the two things we have already seen in here are this object and this object both are called helicopter and they are just rectangles with a name and then a couple of additional attributes nothing too difficult but on top of that if you look at the bottom left we have a couple more rectangles and they have similar attributes but a different name they are called saw they are simply there for damage it shouldn't be possible to land on them after that if you look all the way to the right we have a larger rectangle and this one is called Bat although after that we once again have the same attributes the only thing that is noticeably different in here is this object this is called a spike and if you look at those attributes you can see that they are quite a bit different this is the one that we are going to animate with trigonometry and basically this point is going to be the center and we will move the spike around that point depending on what the radius is so the distance from this point is what we are specifying in the attributes but that's going to come much later for now don't worry about it instead I want to cover all of the other platforms and to get started with that one I first of all want to exclude all of the spikes which means if OB j. name is equal to spike then we want to do spikes that's going to come later so pass in there for now and else is going to cover well everything else and there first of all I want to get the frames which ideally I would get via level frames and then the obj do name for that we need to import some graphics if you look at main. Pi in the import function we have to import a few more things once again let me simply copy them in the first one is a saw and this one import folder Graphics enemies saw and animation after that I want to have the helicopter Sprites once again we are simply using import folder finally I want to have the boat images basically most of the time for this we are simply importing all of the images from inside of one folder and I think that's it let's try it actually and for the time being I'm going to comment out all the stuff in there but don't delete it we are going to need it later on I simply want to print all of the frames to make sure that we are covering all of the objects if I now run main. Pi we are not getting an error and instead we are printing all of the surfaces that looks pretty good that's a good start so next up we can work on the logic and for that I want to uncomment the first part of what we have done earlier although we don't need this if obj name is helicopter anymore but I do want to keep the if else logic part thinking of the speed we will need as well and then we will need the moving Sprite so not that much of a change to be honest however these moving Sprites are not animated also we always have the same group which isn't ideal and let's fix that part right away so right below the frame frames I want to have the groups and for the groups we only really have two options either we are in. all Sprites and self. semic collision Sprites and that is the case if obj do properties have the value platform being set to true for that let's have a look at tiled if you for example click on this platform rectangle there we can see platform is ticked and that in the game should mean that the player can land on it however the other objects like the saw don't have this platform ticked if it is not ticked we want to run something else and that is going to be a tual with self. all Sprites and all of these platforms that are not semic collidable should be inside of self. damage Sprites I.E later on they are causing damage to the player with that we have the groups we just have to pass them into the moving Sprites there we go on top on top of that I also want to pass in the frames into the Sprite although for that we have to update the Sprite class that is going to happen inside of Sprites dop all the way at the bottom we have the moving Sprite and first of all we have to give this thing one more parameter frames and this has to be the first parameter now we have to figure out how to actually use all of these frames and ideally we want to do something very similar compared to what we have done in animated Sprite as a matter of fact all of this is going to be so similar that we want to inherit from this class I.E the moving Sprite should inherit from the animated Sprite that means we also have to update the super Dunder in Hidden method first of all though let's remove the surface we have created earlier this one isn't necessary anymore and now we have to figure out the arguments for the parent class we want to initialize this animated Sprite and call this function with these arguments that being said I don't really care about that or animation speed the arguments I care about are position frames and groups we already have the start position and the groups those two can stay exactly the same the one thing that has to change are the frames at the moment we have a surface but this we want to replace with the frames which we can do very easily all we have to do is pass the frames in there after that we also don't want to fill this surface with a white color anymore and just to test all of this we should already have something and there we go we can now see the platforms and they are working although they're not being animated other than that though they're still working just fine and this makes the game feel much nicer also if you go all the way to the right there should be a boat there somewhere now there we go and this is also semic collidable so I can fall through and all of this is working very good the reason why this moving Sprite Now isn't animating anymore is because it has an update method and because of that we are overwriting this update method which is the reason why self. animate is not being called but that we can fix quite easily all we have to do let's do it all the way at the bottom we want to animate this Sprite as well and now we have animations for these platform and that looks very good The Source on the right also work perfectly fine so I am happy with that now if you go all the way to the right and look at the boat let me go really quickly there this boat doesn't animate oh well it kind of does basically inside of this import folder we only have a single surface and this we are looping over so we never go beyond the first image the reason why I went went with that is because all of the other platforms are animated and having the boat as the sole outlier would have caused a bit of extra work which I wanted to avoid cool so with that we have the moving platforms but I do want to do a little bit more let me actually demonstrate if you look at the source on the left I want to indicate where they are going ideally I want to have a couple of dots that show their path so those are the horizontal ones and then those would be the vertical ones for that one I want to check if obj do name is equal to saw on top of that I want to check if the I think I called it move Direction This is the local variable that we have created earlier to check if this moving object is moving left or right or up or down and at the moment we want to check one of these axes let's say we want to check check if this is going left or right if that is the case essentially what we're going to do is we have the rectangle that we are getting from til somewhere on there we are going to have this saw like this on top of that we want to place a couple of dots at a certain distance all throughout this thing something like this first of all we have to figure out the start position that's going to be the left side in the middle of this rectangle and then simply jump a bit further to the right at a certain distance let's start with why first of all for that we still have a start position that we also got earlier this is a tupo from which we want to get the first index meaning we are getting the Y value that if we look at the rectangle again would give us this center line remember the start position is objx and then obj y plus the height divided by two we start at the top of this rectangle and then jump down by half of the height after that I want to specify the left and the right side of this rectangle which I can also do quite easily I want to have the start position zero and then the end position zero once again numbers I getting from up there and finally all I have to do is for X in range I want to go from the left to the right at a certain increment let's say 20 pixels now at this point you do want to be a bit careful because for range we are going to need integers and we are not entirely sure if these numbers are integers or not to make sure that we have integers let's use the int function after that we want to create a Sprite for these dots to show the path of the saw we don't want to animate anything and for the Sprite we need a couple of arguments let me copy all of this and paste it in there first of all for the position this we have done a couple of times already objx and obj doy then we need a surface and that we are going to do in just a second for now let's simply write surface for the groups we simply want to have self. po Sprites and for Z I want to have Z layers and this one should be BG details okay the one thing we don't have at the moment is the surface this we have to import which we are going to do inside of level frames also I want to import this one right below the saw although the specific position does not matter we want to have a saw chain which is a simple image in the same folder as the main saw let me copy that string and then inside of this for Loop we want to use level frames with that key and that should already be a pretty good start let's try now and we have one dot not amazing but at least a start and I think I already know where the issue is we shouldn't be using objx and obj doy we want to use this X and this Y X and Y and now let's try this again there we go this feels significantly better and the s is moving right along that so I'm quite happy however there is a small issue and that is that we are placing the top left of the Sprite however Y is exactly in the center of the rectangle as a consequence when we have our path so this is the rectangle we have outlined in tiled and at the moment y goes exactly through the center line and we are drawing the Sprites basically like this whereas the correct position would be this so we want to move all of these Sprites up by half of the height which we can do quite easily we want to subtract level frames and then get once again the sord chain from that we want to get the height then divide that by two with that we should have a bit more of a center line which I think we yet that looks good that covers the horizontal part next up we are going to work on the vertical one all of that is inside of an El statement and basically we're going to copy most of that and then change some details most importantly the first value will be X for that we need to start position zero and from that we want to subtract half of the Sprites Whi so get whiff from that surface next up we want to specify a top and a bottom although in practice all we are changing is a zero to a one remember once again we already have a start and an end position finally in the end we want to have a for loop with four Y in range from top to the bottom at an increment of 20 pixels and then we want to use the very same numbers let's try all of that and that looks pretty pretty good let's wait for a second but I think this is going to work just fine cool very happy with that with that we have all of the frames besides the spike however there's one thing I realized I just forgot let's run main dop again and go all the way to the right where we have the boat this one doesn't work perfectly right now and let me try to jump on it so I can demonstrate the boat if it is going to the left should be flipping at the moment it's simply going the wrong way for that inside of til if you look at the boat rectangle you can see we have another attribute called flip and this for the boat is ticked that we have to incorporate into our moving Sprite basically if the ship is going to the left then we want to flip the surface that's all that this parameter is for first of all for that when we are creating the moving Sprite we want to add in one more argument which is going to be the object then the properties with flip that is going to give us a Boolean value that can either be true or false which we can capture inside of our moving Sprite first of all we need a flip parameter and let's say by default this one should be false that we also have to store inside of an attribute self. flip is going to be flip finally then after we are calling animate I want to check if self. lip if that is the case I want to update the image which I do with pame do transform dot flip we have done this a couple of times by now we need a surface then we want to get the axis for X and Y the surface and our case will always be self. image however for the other values I want to have a bit of flexibility at the moment we only really have one platform that can flip which in our case is the boat that can only flip left or right but later on maybe we also get some objects that flip on the vertical axis they are not going to be part of this game but you might want to add your own stuff so I want to have all of this being flexible the way I approach that is below flip we're going to create one more attribute self. reverse which is going to be a dictionary with X which by default gets a value of false and then Y which also gets a default value of false finally we want to dynamically update these values for example if we are moving on the horizontal axis then we want to update self. reverse and the x value and basically we want to set this to true if we are moving to the left so if self. direction. x is smaller than zero if that is not the case so else it should be false the same thing we can then do on the vertical axis I want to update self. reverse doy this one should be true if we are moving down so direction do y should be greater than zero after all of that we can go back to py game. transform. flip and then simply add self. reverse with X and self. reverse with Y let me copy it and there we go this should be enough if I now run m not pipe we not getting an error that's a good sign if I now get to the ship in time it should flip around and there we go this works perfect L well let's try it one more time we have to wait a second but it shouldn't be too long and there we go the ship is flipping again and the player can stand on it just fine that is working pretty well and just to make sure this is working for the other platforms as well let me take Flip or the up and down helicopter platform and now you can see as soon as the platform is going down it is is being flipped that's working pretty well although not actually what I want to do which means inside of tiled I want to untake this parameter cool with that we have all of the moving objects besides the spike and this one is going to be quite a bit different and well for all of that we will need trigonometry so let me outline what is going to happen let's start with the basic problem we have a center point and we want to rotate some kind of spiky ball around that IE this ball should move something like this where we always have the same radius from the center point what we have to figure out for that is essentially a triangle like this via this triangle we getting the X distance and the Y distance if we add those two numbers to the center point we are getting to the actual position of the spiky ball all of that happens at a certain angle once we have that system all we need to do to move the ball around is to update the angle and for that we are going to use trigonometry so let's get started here we have a right angle triangle and trigonometry helps us to measure these kind of triangles what right angle means by the way is that one side of this rectangle is 90° which usually is this side here but the specific side doesn't matter so this side is always 90° that is really important if that isn't the case then this is not going to work but in our case we will always work with right angle triangles and what trigonometry is doing is that if we know one angle besides the 90° so if we know this angle or or this angle and we know one side this could e be this side this side or this side then we can get all of the other angles and all of the other sides and this triangle has specific names the X side is called the adjacent the Y side is called the opponent and the radius is called the hypotenuse and we also have 90° I forgot that one now these names you don't have to use when you do the math but when you want to understand websites that talk about this kind of stuff you want to know these names which is why I am going to use them as well now let's talk about what trigonometry actually does here are two triangles they are basically identical so we have 90° on both of them and we have an angle of 30° on the left side the only thing that is different is the hypotenuse this is either 50 on the left side or 200 on the right side so in effect this second triangle is going to be much larger but that isn't really important for our purposes what is really important is that as long as the angle a is 30° the ratio if we divide the opponent by the adjacent will always be the same regardless of the length of the hypotenuse what that means let's say the opponent right now is 50 and the adjacent is let's say 25 if we divide one by the other so 50 / 25 we simply get 2 now at the same angle if the hypotenuse gets larger to 200 in this case obviously the opponent and the adjacent will also get larger however they get larger by the same ratio for example the opponent right now could be 200 and the adjacent would be 100 the specific number here once again does not matter what matters is the relationship between the two because now we get 200 divided by 100 which is still going to be two which means that this two here and this two here are the same which in practice means that the relationship between these two sides stays the same they can grow and Shrink as much as they want but as long as this angle is constant they will always have the same ratio to each other and that is super useful because we know that this number here responds to one certain ratio between these two numbers and this ratio is always constant for any angle basically what that means is all we have to do is if we know the hypotenuse we can get the length of o and a or the opponent and the adjacent all we really have to do is convert the angle to a ratio now to convert them we need a couple of special functions and those you probably know already they are called sinus cosinus and tangent functions and all that they are doing is that they convert an angle to a ratio there are three different kinds we can look at sinus or sin is the ratio between the opponent and the hypotenuse cosinus is the adjacent and the hypotenuse and the tangent is the opponent divided by the adjacent for a legend these are the sides I'm talking about and just as a reminder when we are talking about this angle here let's say for Simplicity this could be 30° this means for example for the sinus we have a relationship between the opponent and the hypotenuse and this relationship will stay constant as long as this angle here is constant and now you might be wondering how can you get sinus cosinus and tangent now you could do the math here but that's kind of Overkill literally usally every single calculator and python as well have these formulas included so you don't have to do the math for example here is the windows calculator and if I want to know what sinus of 30° are I have to type 30° then go to trigonometry and then here I have sin cos and tan or sinus cosinus and tangent if I click on sinus we get 0.5 this is all that we wanted to know with that we have the theory but if you are not very good at math this is probably still confusing so let's do all of this in practice and here's one example all the stuff at the top here is just reminders so we have the triangle with have the names of different sides we have hypotenuse adjacent and opponent and the one angle we always know other than that we have the formulas for sinus cosinus and the tangent on top of that what we know so far are these two bits of information we know the angle this one is 30° let me draw it in right now actually so we know this side here is 30° on top of that we know H is 50 so this H here is 50 I guess finally we also know that this other side here is 90° what we want to figure out is the adjacent and the opponent so how could we do that and well well this is going to be your exercise try to figure out what these sides are using these formulas here you will have to rearrange them a tiny bit but this shouldn't be too difficult at least I hope it won't be but pause the video and see if you can figure this one out all right the first question is which of these three formulas we should work with all three of them are using the angle so this isn't use of us now to choose one we basically want to go with the most information that we have so for example tangent a is really bad because this one doesn't have the hypotenuse which we do have so we don't want to get the tangent instead we want to get either the S curve or the cosine curve we actually are going to need both to understand why let me get started by calculating the opponent meaning we are talking about about this site here right now I want to get the opponent for this one I need the sign function inside of there I have my angle a and this is going to be the opponent divided by the hypotenuse this we can already simplify quite a bit for example we know that H is simply going to be 50 we can also get S A all we need is a calculator here we go I want to know what 30° are inside of a sign function and the result is going to be 0.5 that's all I needed meaning this entire thing here is simply 0.5 with that we know that 0.5 is going to be the opponent divided by 50 that is going to look like much easier math we can simply rewrite this and multiply both sides by 50 so multiplied by 50 I hope you know what this one means we basically multiply both sides by 50 let me do it in full actually we have 0.5 * 50 is the same as the opponent divided by 50 multiplied by 50 now with this on the right side we are dividing by 50 and multiplying by 50 those two cancel each other out all that is going to be left is the opponent this is all we care about and the opponent is going to be 50 multiplied by 0.5 which means ultimately that the opponent is going to be 25 this number up here is simply going to be 25 I hope this makes sense honestly once you tried a couple of times it is quite simple so next up we have to get the adjacent and if you couldn't do the exercise maybe try again now I hope it makes more sense but for this one we need the cosine because this one is including the adjacent for this one we want to get the adjacent this one is cosine of a and this one is the adjacent divided by the hypotenuse now the hypotenuse once again we know is going to be 50 so let me remove it right away this is going to be 50 for the cosinus of a let me move in the calculator I already have 30 in here and I want to go to trigonometry and get cosine this number isn't as clean we can just write 0.866 that's close enough meaning I can get rid of this cosine entirely and instead write zero 0.866 with that we have a much easier formula to solve all we have to do is multiply both sides by 50 so let me do it properly again I want to multiply both sides by 50 which is multiplying the left side by 50 and on the right side we're getting rid of this divided by 50 at the end we have a is going to be 0.866 multiplied by 50 that is a very easy thing to solve once you have a calculator let me do it on the side the solution here will be 43.3 not 100% correct but for our purposes this is easily good enough and this is going to be the result we now know that the adjacent is 43.3 and that is all we needed we have the X and we have the Y and with that we can get to the drawing although before we get to that I would always recommend to double check your map whenever you try to solve anything like this there are lots of websites that can do trigonometry for you the website that I really like to use is called trigonometry calculator in here you can simply add a couple of numbers and then get the results in our case we know one angle and one side in the bottom you can see what sides they're talking about in our case we know that angle a is 30° so angle a is 30° on top of that we know C is our radius and this was 50° and with that we get site a being 25 and site B 43.3 that's the numbers I came up with as well so the math here checks out with all of that out of the way let's once again look at the setup method and more specifically the only thing that we really care about are the moving objects and then obj name equal Spike if that is the case then we want to create a whole new class let's call it Spike now this Spike doesn't exist right now I'll fix that in just a second but first of all we will need a whole bunch of arguments that we can start with right away first of all we want to have a position and this is going to be what we have covered already a couple of times obj dox and obj doy that being said though this would be the top left of the object and I want to place the center of it to fix that I also want to add obj do width to X and obj do height to Y next up we will need a surface and the spike itself is not being animated so we will only need a single surface for that I want to have the level frames and in there in just a bit I want to have a Spike as a matter of fact this we can do right away inside of main.py in import assets I want to add one more entry that's going to be Spike and we want to import only a single image which we are getting from Graphics enemies spike ball and spiked ball once again I have been a bit inconsistent with underscores and spaces but well the path does work in the end after we have the surface we want to have a few more parameters first of all I want to have the radius which I'm going getting from obj do properties and the radius then I can duplicate all of this three times because I want to have the speed I want to have the start angle and I want to have the end angle all of those I get via the properties there we have speed we have the start angle and we have the end and to demonstrate all of this inside of til if you click on the spike ball you can see we have end angle platform radio speed and start Angle now platform for this one we are going to ignore we only really need the other parameters however if we run the code right now we would get an error because this spy class does not exist that we have to fix inside of Sprites I want to create a new class called Spike the parent class should be a regular Sprite and after that I want to have a thunder init method with self and then all of the arguments I just talked about we want to have a position we want to have a surface we want to have groups that's the one I forgot just now we want to have a radius a speed a start angle and end angle finally I forgot the Z parameter although I suppose for this one we could have a default value for Z layers and Main cool those are all of the parameters the one I forgot is groups let's fix that one right away fortunately that is easy to fix groups should Simply Be self. popres and self do damage spres that's about it next up then inside of the spike class we want to turn all of the parameters into attributes let's start with self. Center this one is going to be the position then we have self. radius that is going to be the radius self. speed will be speed let me paste in the last two attributes start angle and end angle on top of that we want to create a few more actually we want to have an angle and this by default is going to be self. start angle this is the angle we are actually going to update later on so basically what we are going to do when we are running the update method with self and Delta time we are updating self. angle by increasing it with self do speed multiplied by Delta time that way this angle keeps on updating and as a reminder we currently have a center point this would be a point like this from that point we want to create a triangle and then on the top right point we want to place the spike ball this would be the actual visible Sprite the position of the Sprite we are going to get via self. angle if we then add trigonometry we would also get X and Y which would give us the position of this point which means we want to call Super and then thunder in it with a position a surface groups and Z position is the only thing that's actually going to be complicated the surface we already have groups we already have and that we also have so position is what we have to work on and this is going to be X and Y and for that we will have to use trigonometry I want to create an x value and I want to create a y value let's start with Y actually so what we want to do we have a triangle and on this point we know the hypotenuse so H is going to to be our radius we have either the radius or the hypotenuse they are the same thing on top of that we have the start angle let's call it Alpha that's what we specified earlier and from all of that we want to get this y distance or this bit to be a bit more specific let's use the formula sin of alpha is equal to the opponent divided hypotenuse or in our case it is going to be y / r or in other words if we are rewriting all of this because we only really care about why we get the sign value of alpha multiplied with r and that is what we actually need to get this wide distance now to make all of that work inside of python to get the sign functions we need from math import sin Co and also we want to have radians by default sin and cosine work with radians not degrees so when we are working with them we want to convert our degrees into radians and that's what this function is for to get why we want to have the sign value of radians of self dot angle and this we want to multiply with self do radius if I bring up the formula again we are doing this part here that's the same thing however at this point you do want to be careful because at the moment this origin point for us in this formula is 0 and zero which isn't correct because we want to circle around this Center Point to get that we want to get self. Center and this is a tuple and we want to get X and to that we want to add the value we have just created and that is pretty much it after that we can do X and you can do this part as an exercise for yourself although the answer is actually quite simple we want to get self. center. Z and then the cosine of the angle also I realized for y this should be one because we care about the Y value but that is pretty much it now we have the X and the Y part with that we can run super Dunder in it and we should be good to go which means inside of level. Pi I also want to import this Spike and now run main. pi and there we have the spike ball in some position now this doesn't do very much at the moment but that we can work on next because at the moment inside of the update method we are simply updating the angle but after that we are not using it anymore to fix that we want to get our trigonometry parts once again and simply copy them in there since we are now updating the angle we are getting new X and Y values which we can use to update self. rect and basically we simply want to update the center to the X and Y value if I now run out of this again we are getting actual movement in a circle and that is looking pretty good if you look at s and cosine for a little bit they honestly aren't that difficult with a bit of practice they become fairly straightforward however we are not entirely done yet because I want to have more control over the movement of the spike once again if we are looking at tiled we have an end angle and a start angle and I want to make sure that this spike is moving between them now at the moment the end angle is -1 and I have used that as a short hand to move in a Full Circle however if this one is let's say 180 then the spike should go from zero 280 and then go backwards back to zero to implement something like this we first of all will need a few more parameters most importantly we want to have a direction which by default is going to be 1 and then when we are updating the angle this should be self. Direction multiplied with the speed and Delta time that way we can move this thing forwards or backwards also I want to have self dot let's call it full circle the value for this one will be true but only if self. end angle is equal to -1 if that is not the case else it should be false and after we have that before we are calculating the positions inside of the update method I want to check if not self. full circle then we want to check if self. angle is greater or equal than self. end angle if that is the case self. direction should be -1 on top of that we want to check if self. angle is smaller than self. start angle then self. direction should be one with all of that let's try the code again and now the spike should move back at some point and that looks pretty good and yeah now we get 180° movement which I like quite a bit more that covers the spike class however we are not done with the spikes entirely because at the moment we have a start point and we have the actual Spike but the player doesn't really know where the spike is going to go and to indicate all of that a bit better I once again want to have some kind of chains that connect the spike to the center point for that we can actually reuse the same class although first of all we have to import one more graphic I want to have a spike chain which is in the same folder and called Spike uncore chain inside of level. Pi then let me clean this up just a bit I want to create a four Loop let's call it 4 I in range I want to start from zero and then go up to obj do properties with the radius the step size should be 20 basically what we are going to do at the the moment we only have a single Spike the big one but what we can do inside of this four Loop is to create a similar object with a smaller radius which we are getting via this four Loop and since we can customize this surface we can simply create a spike class with a different surface and a lower radius and then they should all move along with each other which means inside of this for loop I want to create another Spike and for that let me actually copy all of this the position or the center position to be a bit more specific can stay identical Spike does have to change this one should now be the spike chain the one we have just imported the radios also needs to change because this we are now getting from the for Loop this one would be I or actually to be a bit more specific let's call it radius there we want to have the radius the speed the start angle and the end angle all can stay the same however for the groups I want these spikes to only be in all Sprites they should not hurt the player I think that'd be a bit weird finally for this bike I do want to set a custom Z value those should be in Z layers and then BG details that way we always have them behind everything else and now if I made a pie this feels quite a bit nicer and everything else works just fine righty and with that we have covered a really important part of this game now we have all of the moving objects one second while I was editing this video I realized I have made a mistake if you look at level. pi and when we are creating this Spike the position at the moment is wrong we want to place this spike in the center of the object position if this is the square we are getting from til obj dox and Y would give us the top left but instead we want to get this Center Point to get to that point we want to add half the width and half the height of the object I got halfway there I added the object width and the object height but I never divided either by two that fortunately is very easy to fix we divide both by two and now we should have the center of the object and this we also have to do for the smaller spikes so for both of them I want to divide them by two and now if you run M Pi we still get a workable result so that looks pretty good but now the entire thing is just a little bit more accurate next up we are going to work on the enemies and we will have two in the game number one is called to and this is a simple enemy that walks left and right the other enemy is called shell which is well a shell I suppose the special thing about that one is that there's no movement but instead if the player gets close to it then it shoots a pearl and that's basically it neither of these classes get too complicated so let's Jump Right In and once again we have to import a couple of things to get started first of all inside of import assets I want to import a few more Graphics or to be a bit more specific I want to have a key called tooth and then import a folder where we go to Graphics enemies tooth and run and just to show what we are getting if you go to graphics and then to enemies and in there we have tooth there's only a single animation inside of it where we have a couple of run frames that's literally all that this enemy is going to do it's going to run on a platform and then goes either left or right there's no other state I did want to keep all of this simple so with that we have all that we need next next up we have to work inside of level more specifically the setup method and in there I want to add another section that we can call enemies the setup for this one is actually incredibly simple all we want is for obj in TMX map doget layer by name the layer we want to look at is called n on this layer we only really have two kinds of objects and both have a specific name which means I can check objname is equal to to if that is the case I want to create an instance of a to class which is going to get a whole bunch of arguments we want to have a position we want to have a couple of frames we want to have the groups and we are also going to need the Collision Sprites the reason why we are needing the Collision Sprites is imagine we have all the tiles for our level level and it looks something like this I want to place to on a platform and then to is going to run in One Direction and once he reaches a cliff he should turn around until he reaches another Cliff at which point he should turn around again and keep on doing that forever for that to work we have to make sure that Toth has access to the Collision Sprites on top of that inside of til you have to make sure when you're building a level that you placing tooth on top of collision Sprites if you are not doing that you are going to get an error but for example in this case I want to make sure that Toth is running in this direction until he reaches this point and then he runs in the other direction until he reaches this point and then keeps on doing this forever with that we have the basic setup which means we can now create the tooth class which I want to do in a new python file and while we at it we don't need the supported P file anymore instead I want to create a new python file and then save it as enemies. I and there first of all we want from settings import everything and then we can create a class called to which is to inherit from pygame dos sprite. Sprite after that we will need a thunder init method with parameters for all of the arguments I just talked about let me actually copy them right over and once we have that we can pass actual arguments into them position as always is the easiest bit we simply want to have obj dox and obj doy for the level frames we now have our level frames with to for the groups we want to have a tupal with self. B Sprites self. damage Sprites and then we are going to need one more I called those self.to Sprites that way we can get easy access to all of this kind of enemy finally we need to Collision Sprites which we get with self. Collision Sprites nearly done we now have to create a two Sprites class that is going to happen inside of AIT there I want to have two Sprites is equal to py game. sprite. group also while we are here we can import from enemies I want to import to cool with that we have all of the setup out of the way which means we can call Super Dunder in it and then pass in the groups after that I want to store all of the frames inside of an attribute and while we are here I also want to create self. frame uncore index frames is going to be frames and the frame index is going to be zero Z after that we can create the image which is going to be self. frames with self. frame index after that we have to get self. wct which we're getting with self. image. getorf rectangle we only want to place the top left in whatever position we are getting this should be a good place to get started at the very least we should be able to see something although I am already seeing a mistake we need self first of all also the dot after the rectangle shouldn't be there but now if I run m. Pi we are getting an error that tooth object has no attribute Z that does make sense fairly easy to fix self. Z for this one is going to be zore layers and I always want the enemies to be on the main layer now let's try this again and if I go to the enemy position we can see we have tooth that looks pretty good doesn't do anything right now but that we can work on for that though we're going to need a few more parameters first of all I want to get a direction which by default should be either zero or negative 0 and this is going to move to left or right now to get either one of those I want to use the choice method which means I want to have a tup with one and negative one and pass the tupal into the choice method for that I need from random import choice on top of that I want self. Collision uncore rectangles I want to store all of the rectangles from the Collision Sprites which means I want to have sprite. Rec for sprite in Collision Sprites since the Collision for this enemy is going to be fairly simple we don't really need all of the Sprites all that we care about are the rectangles which means with this line we are making the entire class just a little bit leaner cool with that we have a couple of parameters but we don't do anything right now for that we will need update with self and Delta time and in there first of all I want to animate this class which we have already seen couple of times self. frame index plus equal the animation speed multiplied with Delta time after that self. image should be self. frames I want to get an integer of self. frame index with modulus of the length of self. frames let's try the animation if I now go to main.py and run out of this we have a run animation that looks pretty nice after that we can do the actual movement which we do with self. dox plus equal self. direction and then we need some kind of speed let's say for now 200 also multiply all of this with Delta time let's try of that now and there we can see the enemy is moving quite fast but that's not really the point now this 200 should be an attribute let's place it right below Collision rectangles self. speed is going to be 200 and now self. speed oh also you might have noticed that if I get the right game if to is moving to the left he is moonwalking which looks kind of funny but is not the intended experience basically if direction is negative then I want to flip all of the frames kind of like we have done for the moving platforms although for this enemy it's going to be quite a bit simpler all that we want to do is if self. direction is smaller than zero I.E we are moving left then self. image is going to be py game. transform. flip with self. image true and false for the horizontal and the vertical axis and now let's try this again again and I hope I get a game where to is moving left there we go now to is facing in the right direction although I suppose having all of this on a single line is a bit cleaner so basically what I want to do his self. image is all of the stuff we have just done but only if self. direction is smaller than zero if that is not the case else we just want to have self. image and with that we are saving a single line of code the last thing we have to do for to is we want to reverse the direction once we're hitting a cliff for that once again imagine that we have a couple of tiles like this and then a couple of other tiles on the lower level what we are going to do with Toth we are going to create two more rectangles one to the bottom right and another to the bottom left and as as soon as one of these rectangles doesn't collide with the Collision rectangles anymore then we know we have reached a cliff for example if this rectangle in the bottom right of the player is in this position then we know that to must be here facing a cliff and as a consequence tooth should be moving to the left for that I want to create a floor wrecked right which is going to be pame do f wct and now we need a top left position for this rectangle and a size the size is fairly simple I want this one to be one and one pixels for the top left I want to get self. rec. bottom right after that we can duplicate all of this and get the floor wreck left the top left of this rectangle should be the bottom left of the tooth enemy and then the size of the rectangle should be negative -1 and one so basically if this is to we are placing the bottom left this point and then go one pixel to the left and one pixel down this is negative 1 and this is one the outcome is going to be a rectangle after that we want to check if either floor wck right or floor wed left is not colliding with any of the Collision rectangles anymore or in other words we we want to check if floor wrecked let's start with the right side and Collide list we want to check a collision with a list of rectangles which we already have so on there self. Collision rectangles if this number is below zero then we know there's no Collision on top of that we want to check and self. direction is greater than zero if that is the case we know that we have no Collision on the right rectangle and we are moving to the right as a consequence we would want to set self. direction to ne1 however this would only cover one side and we can make all of this a bit more elegant which we can do with an aura statement and then on the next line I want to basically duplicate all of this and then cover the logic for the left side I want to check floor W left Collide list with Collision rectangles but then self. direction is smaller than zero if that is the case we know we are moving to the left and we are on a cliff on the left side if that is the case we want self direction to be a one or in other words we want to flip a negative one to a positive one which we can do with multiply equal minus one and well that's basically it this should cover the entirety of the logic if I now try mayor pi and jump on this platform we cannot touch tooth yet but other than that this is working pretty well let's try it a few more times but I'm pretty confident that this is working perfect with that we have the first enemy wasn't that difficult later on we will have to make some more updates but for now this is a really good good start which means next up we can create the other enemy that one's called shell and once again we want to inherit from pygame dos sprite. Sprite this one is going to be the slightly more complex enemy but let's go through it step by step first of all I want to run the dunder init method with self position frames and groups with those we should have everything we need to at least see this enemy first of all we have to call Super Thunder innit with the groups after that we want to get self. frames and self. frame index which like with to are going to be frames and the index zero on top of that we are now going to need a state which by default should be idle and to understand why we need that let's open the graphics folder again we want to go to enemies and there we have have the shell in the Shell we have two states idle and fire idle is a single image and fire is a short animation all of that we have to import and then control inside of the game and to make all of that work we want to get self. image is going to be the frames or rather self. frames but basically the same then self. State and then self. frame index next up we we will have to create self. rectangle which we get with self. image. getet fere on there we want to place the top left to the position and also before I forget again we have to create a z parameter which once again is going to be Z layers in main this should be enough to at least see the shell but we have to import the graphics first of all that's going to happen inside of import Assets in the game class let me copy that one in we want to go one folder up Graphics enemies and shell and really importantly we want to import sub folders like we have done for the player that way we are getting a dictionary with all of the folders as a key the associated value is going to be a list with surfaces which are going to give us our animation and after we have that inside of level. Pi in the setup method I want to check if obj do name is equal to shell if that is the case I want to create one instance of the shell for that let me copy all of the parameters I want to create shell with those parameters position is going to be this one frames are going to be the level frames with shell and for the groups we want to have self. Sprites and self. collision Sprites shell should be collidable I.E the player should be able to stand on top of it and I suppose I should mention the difference here is that to like the player has access to the Collision Sprites but itself is not in that group whereas shell is inside of that group although if I'm not trying to run this code we would get an error for two reasons actually first of all I am not importing shell from enemies so besides tooth I want to import shell however now if I try to run all of this we are getting an error that shell object has no attribute old W this one is required for collisions to work to fix that one after we are creating the rectangle I also want to create self. _ rectangle which is simply going to be self. rec. copy let's try all of that and we have collisions also the player can now jump on top of the shell and that looks pretty good however we have an issue we have two shells and each shell has one property called reverse for the shell I'm looking at right now this is not ticked however for the other one this one is which means I want this one to face to the left to make that work we have to update the dunder init method just a bit first of all I have to get the parameter let's call it reverse into this class which happens inside of level. Pi and I want to add one more argument which I'm getting with obj do properties and this one is called reverse which is going to be a bullan value which means we can use it quite simply inside of an if statement if reverse then we want to let's add a commment for Now flip all frames in self. frames or rather for now just in frames however if that is not the case else I simply want to set self. frames to the frames without making any changes and since we are now creating frames in there we don't need this frames anymore now we have to figure out how to go through this dictionary and then flip all of the surfaces the way I approach that is first of all I'm going to create self. frames although for now it's going to be an empty dictionary after that I get for key and surface in frames. items remember key is the name of the animation and surface I suppose I should call the surfaces is going to be our animation inside of this for loop I want to get self. frames the dictionary I've just created and then create a new entry with the name being the key and then after that I want to use list comprehension to flip all of the frames inside of surfaces this is the list with the surfaces we are working with and basically to get started I want to get surface for surf in surface that way we would simply copy the list but we want to flip all of these surfaces which we are once again doing with py game. transform. flip the other Arguments for that are going to be true and false that should already cover it if I now run may not Pi again that worked pretty well now we have a shell looking e to the right or to the left perfect on top of that I want to create one more attribute and that is going to be self. bullet uncore Direction which should not be inside of the for Loop if the shell is facing to the left then this one should be-1 however if it is not then it should be one and I suppose I don't have to explain in too much detail what this parameter does later on if a shell is facing to the right it should only shoot bullets in in that direction now that we have that we have to figure out if the player is close to any instance of this class for that first of all we will need access to the player which is going to be another parameter that we want to store as an attribute self. player is going to be player and then inside of level. Pi we have to pass the self. player in there as an argument and I hope on your monitor you have a bit more space but it might make sense to use named Arguments for this one I want to create another method called State Management inside of this method we want to control the state of the shell and it can either be in an idle state or in a fire state to trigger the fire State we will need three conditions imagine we have the shell and we have the player the shell should be shooting if number one the player is inside side of a certain radius so we need a radius number two the player should roughly be on the same horizontal axis so let's call this one a and finally the player should be in the forward facing direction of the shell I.E if the shell is facing to the right it should only be shooting if the player is to the right of it to make our life a bit easier for this one I want to create two vectors one for the player position and one for the let's call it the shell position for both of those I want to create a vector for the player this should be self. hitbox rectangle do Center this should actually be self. player. hitbox center for the shell this should be a vector self. rec. Center so at this point we have simply created two vectors with the center of the player and the center of the shell not massively important but this is going to make our life quite a bit easier first of all we want to check if the player is near and with vectors all we have to do is get the shell position and then get distance underscore 2 in our case this is going to be the player position if this number is let's say below 500 then we know the player is near and for now to test this let's print if player near then we want to print ler is near also don't forget to run this method which we're going to do inside of an update method self and Delta time and self. State Management if I now run made of Pi we get player is near but if I run away from it this does not update anymore hence it is working next up then we have to check if the player is in front of the shell and if the player is on the same level player. level is I think the easier one let's say in our case I want to make sure that the shell and the player are within 15 pixels of each other or in other words we should be able to go 15 pixels up or 15 pixels down if the player is inside of this range then we consider this variable to be true I want the shell position although I only care about the Y part from that I want to get the player position and once again I only care about why for example let's say the shell wi position is 200 and the player wi position is 190 if you subtract one from the other the end result would be 10 which should be inside of the range so an easy way to get started with this is we want to check if this number is smaller than 30 now this would be a good start and it would also work with 210 because in that case the result would be1 which is also going to be below 30 however now imagine the player white position is 300 which would be somewhere down here which would be very much outside of the range but in terms of math we would get 200 minus 300 which would be100 which is also below 30 hence with this system by itself the player would always be considered level if we are anywhere below the shell which isn't ideal the way around that is to turn the result of this subtraction into an absolute number that way this negative 100 would become a positive 100 and be greater than 30 next up then we have to check if the player is in front of the shell and for Simplicity for now let's imagine that our shell is always facing to the right on top of that we have once again a play and let's give them some values we only really care about X let's say for the shell this could be 100 and for the player X could be 200 essentially to know if the player is in front of the shell we want to check if the x value for the shell is smaller than the x value for the player and that's all we need at least as long as the shell is facing to the right so basically we want to check if shell position do X is smaller than player position. X however this we only want to do if self. bullet direction is greater than zero however if that is not the case we want to check the opposite I.E shell position. X is greater than player position. X and that is pretty much it with that we know if the player is near if the player is in front of the shell and if the player is on the same level that means we can update our if statement we want to check if the player is near and the player is in the front and the player is level only if all three conditions are met then we want to do stuff if I run the game player is near does trigger immediately however once I jump it does not trigger anymore and if I am on the wrong side of the shell it also doesn't trigger let's try the other shell and I can jump behind it that doesn't trigger anything but if I go to the left side of it we are getting player near so that is working perfectly fine which means if that is the case we want to update self. state which should now be fire also we always want to play the fire animation from the first index which means we want to update frame index and set it to zero also while we are here there should be a timer the shell shouldn't be able to shoot immediately we only want to shoot let's say every 2 seconds or maybe every 3 seconds for that we will need from timer import a timer and let's call this one the self. shoot timer which is going to be a timer with a duration of 3,000 milliseconds and then we will need another condition and not self. shoot timer. active once we are shooting I want to activate this timer meaning self. shoot timer. activate quite a bit but now we are getting the right state if the player is in front of the shell although one thing I forgot is we want to get self. shoot timer and updated that way it's actually going to work and once we have all of that we can work on the animations SL attack logic first of all we want to update self. frame index in the usual way plus equal animation speed multiplied with Delta time however next up we couldn't reuse the animation method we have used for the tooth class because once we have played the fire animation ones we want to go back to the idle State and for that we need to know if the animation is finished or not because of that we couldn't use the modulus operator anymore instead we want to check if self. frame index is smaller than the length of self. frames and then self. state if that is the case I want to get self. image and set it to self. frames with self. State and then integer of self. frame in index with that we should actually get already a basic animation and at some point we are starting again cool that looks pretty good and I think I should explain we are always updating the frame index and then we are checking if the frame index is smaller than the amount of frames we have inside of the list if that is the case we want to cyle through that list however once the shell starts to fire we are setting the frame index back to zero because of that all of this is going to play again I suppose what we can do as well is to cover an else statement if that is the case I want to set self. frame index back to zero and on top of that if self. state is equal to fire then I want to set self. state to the idle state with all of that we want to check an action ual fire animation and basically if you are looking at the graphics let me zoom in a bit at some point we want to shoot a pearl and this needs to happen on a specific frame this one to be precise if we are on this Frame of the animation we want to shoot a pearl and then this is moving to the right to make that work we have to check what frame we are on inside of the animation first of all we want to check if self. state is equal to fire on top of that we want to check if the current integer of self. frame index is equal to three also we have to be careful here because during the animation we could be on this index multiple times as a consequence I want to create one more parameter I'm going to call this one self. has underscore fired and I only want to be able to shoot a bullet if we haven't fired yet once we have done that let's say I want to print shoot a pearl and then set self dot has fired to R also at the end of all of this I want to make sure that self do has fired is back to false so has fired is false the last thing we now have to to do in the Thunder init method is we need to create self. has fired and set it to false let's try all of this and now we should be getting shoot Pearl every couple of seconds but only if the player is in front of a shell if the player is on the same level and if the player's in front of it which means for the last part we have to create the actual Pearl and that's going to be a whole separate class let's create it right below the shell class Pearl once again we are going to create a py game. sprite. Sprite the arguments we are going to use to create it are going to be a position groups a surface we want to have a direction and a speed I want to call Super and then Thunder init with the group groups self. image should be the surface and self. RCT should be self. image. getet fct and we want to place this Center and then use the position self. direction should be an attribute and self. speed should also be an attribute finally we need self. Z and I want the pearls to always be on the main layer this should be giving us a pearl but now we have to be careful because we want to create a pearl inside of the level class but the trigger to create a pearl happens inside of the shell which means we have to get some kind of code from the shell into the level. pi class that we are going to do via another method inside of level. Pi I'm going to call it create Pearl for this one we will need two parameters the starting position and the direction and then in there we are going to create a pearl although for that to work we have to import it so from enemies import Pearl and then we have to get the parameters for the Pearl position and direction we going to get via the parameters the groups are going to be self. all Sprites self. damage Sprites and finally I want to have one more group that is going to be called self. Pearl Sprites this group doesn't exist right now let's create it really quick I want to create let me actually duplicate this line self. Pearl Sprites after that we need two more things a surface and a speed the speed we can set to some value that we think looks good I'm going to go with 150 finally we will need a surface and this we're going to import inside of level frames we want to import a single image from Graphics enemies bullets and pearl all of that we are going to store under the Pearl keyword which means for the surface we want to get level frames and then get the Pearl and with that we have one function that can create a pearl and this function we want to trigger from inside of the shell for that to work we want to pass the function in there as an argument self. create Pearl and I think for the shell we need named arguments otherwise this will become confusing we have the position we have the frames we have all of the groups this one was called reverse then we had the player and finally we are going to get one more that we can call create Pearl that looks much cleaner next up then inside of the shell we will need one more parameter and that's going to be the last one create uncore Pearl which we want to store as a parameter self. create Pearl is going to be create Pearl and now when we are shooting inside of the animation so far we only printed shoot Pearl but now we can call self. create Pearl for which we are going to need a position and a Direction so position and direction direction is the easier part because for this one we have a bullet Direction and for the position let's simply use self. rec. Center however I am getting a parenthesis error that happens inside of level. Pi and that is because I missed a square brackets now let's try this again and after a shell is trying to shoot we get name level frames is not defined and you might have spotted the mistake already we only have level frames available inside of the setup method because we are passing it in there right away but that does not apply to create Pearl so this level frames we cannot use and I think the best way around that is inside of the init method I want to create a couple more let's call it frames I want to have self. Pearl surface which I am getting from Level frames and pearl let me simply paste it in there and after we have that we get access to the Pearl surface and now if for try main. P again we are getting one Pearl every couple of seconds let's try the other shell and we get the same result that looks pretty good however the pearls are not moving and well that's going to be a problem for that we have to work a bit more inside of the Pearl class finishing the class is going to be your exercise and there are four parts to it number one I want you guys to give the Pearl a proper starting position number two make the pearls move in the right direction number three is the Pearl should disappear once it hits an obstacle or the player and finally a pearl should also disappear after 5 Seconds that should be quite a bit to work on pause the video now and see how far you get let's get started with the proper starting position for a pearl because at the moment the Pearl starts right in the center of the shell not ideal to fix that we have to update the W position at the moment we are setting the center to the position which is at the center of the shell to update that I want to use a vector and for example move this thing 50 pixels to the right and 0 pixels on the vertical axis if I now try of this again this shell looks about all right however the other one is not going to be looking good because now we on the wrong side although that we can fix quite easily all we have to do is multiply this 50 with the direction let's try this again and that still looks pretty good let's try the other one and that's also looking pretty good neat next up we want to make the Pearl move that's going to happen inside of an update method with self and Delta time all that we really have to do is self. dox plus equal self. direction multiplied with self do speed and multiply it with Delta time let's righty up on and now the pears are moving and there should be another one soon and that's looking pretty good let's try the other one and that's also working next up and this is going to happen inside of the Pearl class I want to create a timer as a matter of fact later on I am going to create multiple timers so I'm going to create a dictionary called self. timers although with only one key for now which I'm going to call Lifetime this is going to be a timer with a duration of 5,000 and this timer I want to activate right away self. timers lifetime I want to activate it after that to make sure all of the timers are working I want for timer in self. timers do values and then timer. update with that the timers are running and as soon as if not self. timers with LIF time do active I.E we are activating this timer when we are creating the class or one instance of the class and as soon as this timer is not active anymore then we want to destroy the Sprite Which we're getting via the kill method let's try all of that and this Pearl should disappear very soon and there we go it disappeared let's try it again just to be sure we are getting another Pearl and cool that looks good finally then we want to make sure that the Pearl disappears once it collides either with the player or with a collidable Sprite and that we could do in two ways either we could pass the player and all of the Collision Sprites into the Pearl class which would be doable but kind of overkill for a single bullet kind of object so instead what I'm going to do is create another method inside of level. Pi which I'm going to call Pearl Collision in there I want to check for sprite in self. Collision Sprites and then I want to get pame do Sprite do Sprite toite for this one we are going to need the Sprite so a single Sprite that we want to check which at the moment is going to be one Collision Sprite this we want to check against the group of Sprites which in our case are going to be our pearls Sprites and finally we want to set our do Hill argument with that logic we are checking every single Sprite inside of the Collision Sprites if that Sprite has a collision with a pearl then we are destroying the Pearl all we have to do inside of the run method is call this self. Pearl Collision method and I want to make sure that I separate the update l iic and the draw logic let's try main. pi and the easiest way to check if this is working is in here and that looks pretty good the Pearl disappears right away although this is only going to apply to the level if a pearl hits the player then it is not going to disappear now that logic we could keep inside of the Pearl collision and if you have done that for now that is totally fine but later on I I want to have a few more objects that can hurt the player which means for all of that I want to have one more method and let's call this one the hit collision and there I want to check for sprite in self. damage Sprites after that I want to check if sprite. w. collide wct with self. player. hitbox rectangle if that is the case I want to print layer damage and on top of that if a bullet is colliding with the player then I want to destroy a bullet and by Bullet I mean Pearl for that we have to be able to identify this Pearl inside of all of the damage Sprites I think the easiest way of doing that is to give it one more parameter self. Pearl is going to be true and then inside of level. Pi in the hit Collision method I want to use has attribute with Sprite and Pearl and if that is the case I want to destroy this Sprite using the kill method and with that after the Pearl Collision I also want to get the hit collisions and now if I try all of that and we're getting hit we get player damage and the Pearl disappears and this works multiple times now at the moment we don't have proper data to store the player Health but that's going to come in the next part for now I think we have made a ton of progress and this section has gotten quite long all righty once again I have forgotten one part and you might have noticed what it is if we are running the game the trth enemy walks left and right just fine so that part Works however if we go to til and move him further down here we would expect this enemy to go up to this point and then to this point and bounce between the two that however doesn't happen because we only check for a cliff we do not check for a wall which means if I now save til and run all of this again we can see that partly the tooth enemy works but there at the wall he just walks right through the wall because we don't actually have Collision checks with the wall we just reverse the direction once we hit the cliff which works reasonably well but I do want to cover the wall case as well for that we have to work inside of the tooth class more specifically inside of reverse Direction and everything else we can ignore the logic for this part fortunately is going to be fairly simple imagine we have the tooth enemy so far we have a floor wck left and right those are a couple of rectangles to the bottom right and the bottom left on top of that I want to create another rectangle that spans the entire width of the enemy plus a pixel to the left and to the right if this rectangle collides with any of the rectangles inside of the Collision rects list then we also want to reverse the direction and that's about it it's not terribly complicated first of all for that I want to create let's call this one a wall rectangle for that we will need P game do F wct once again we are going to specify the top left and the size the size actually is the easier part for the WID I want to get self. rec. WID and then plus two pixels then for the height the number doesn't really matter so let's add a one in here after that we will need the top left for that I will get self. Rec dot top left however to that we have to add a vector in which we go one pixel to the left and zero pixel on the vertical axis that would give us the wall rectangle next up we have to check like we have done for the flow rectangles if there is a collision or to be a bit more specific for the floor rectangles we checked if there wasn't a collision for the wall re angle we want to do the exact opposite if there is a collision we know we have hit a wall but the difference in terms of code isn't that drastic we want to add the wall rectangle and then Collide list we want to check self dot Collision rectangles and if that number is different from -1 then we know there's some kind of collision if that is the case we want to turn around and that is it if I now run may not Pi all of this should work just fine so let's have a look and cool that works pretty well and back in tiled let me copy the enemy and paste him up there to make sure both cases are working then back inside of the Cod we can try all of this and that looks pretty solid cool with that we have slightly better enemy Behavior with the enemies out of the way we can work on interactivity meaning the player should be able to attack the enemies and the enemies should be able to damage the player on top of that all of the traps should damage the player and there should be a couple of items that heal the player or at the very least provide some money also to display the health of the pirate I want to display hearts in the top left and if the pirate collects coins then we should display a couple of coins for a short amount of time that's that is going to be a larger section because we have to cover quite a few elements let's get started with the items this is the easier part for that once again we are inside of main.py and we have to go to import assets because we need another set of Graphics we want to have another key called items and in there we want to import subfolders the way that one is going to work inside of Graphics we have items and in there we essentially have five animations so if I open diamonds we have a diamond animation the same for the gold coin and so on this is what we are importing after that we have to work inside of level. Pi more specifically in the setup method essentially in there I want to add another entry for the items and once again this is going to be for obj in TMX map doget layer by name the layer we want to look at is called items and for that one if you look at tiled we have an object layer called items on there we have all of the items should be straightforward also what we're importing here is if you click on one of these objects you can see that they all have a name we have a silver coin a skull a potion a diamond and a gold coin the name matches up with the folder that we have imported and that's important although that's the kind of system you have already seen a couple of times by now I hope it makes sense inside of this four Loop I want to create an instance of an item class which does not exist right now for that we have to work inside of Sprites and let me minimize everything what we want to create let me do it right below the animated Sprite for the simple reason that the item class should inherit from that animated Sprite after we have that we want to create a Dunder init method with self then we will need the item underscore type meaning do we get a gold coin a Sil silver coin a skull and so on after that we will need a position the frames the groups and that's going to be it for now actually although later on we will have to add a bit more after that we have to call the super Thunder init method to make that one work we have to cover the arguments from the parent class most importantly we need position frames and groups Z layers and animation speed I am going to ignore because all of the items should be on this layer and the animation speed shouldn't change hence the default value is totally fine for those and fortunately all three of these parameters are covered by the parameters of the actual class itself so once we are creating one instance of it we should be good to go let's try that actually inside of level. Pi first of all I want to import the item class for the arguments I want to go to the item class and I want to have these four arguments the item type is the easiest one I want to have objname for the position this we have already seen a couple of times obj dox and obj doy the level frames is also fairly easy although what you have to keep in mind for this one is that inside of level frames we have the key items and this one contains another dictionary and only in there do we have all of the animations the way we are going to access those is with level frames then we want to have the items and then in there we have another dictionary and this we can access via objname finally we want to place all of this at least for now instead of self. all Sprites and there we go we have a whole bunch of items that looks pretty good they don't do anything at the moment but that we can work on although before we get to that I want to cover one more thing inside of til we always have one cell and at least inside of tiled in the middle of the cell we have the item but that's not what we are importing into py game what we get from obj position so objx and Y is the top left and the only reason all of these items are in the center of a cell is because they have a special graphic with a ton of white space to each side the actual animation however doesn't do that which means if we imported all of that into pame the item would be in the top left which is not what we want we want to have a bit of an offset in each direction that way we get exactly to the center of the cell and to demonstrate what that actually means let me select one of these graphics and place the goldcoin right there if I now run May notp again you can see that the gold coin coin is kind of in a weird position which I don't really like to fix that part I want to go to level. Pi and then add to the position the tile size and divide it by two that however isn't enough if i r may not buy again we are still not exactly in the center that is because inside of Sprites when we are placing the item we are placing the top left however what we want to place is the center which we can offset right away self. rec. Center is going to be the position and now may not P the coin is exactly in the right position that feels much better all righty next up then I want to store the item type as an attribute so item type is the item type that is important later on to determine what kind of item the player has picked up although at the moment this isn't going to do very much for that to actually make the items do something we have to work inside of the level it will be somewhat similar compared to the Pearl Collision I want to create another method that I'm going to call Item Collision no need for custom parameters on this one and in there we want to check all of the items which at the moment we cannot do because inside of setup we have all of the items simply inside of all Sprites so there's no way to identify all of them easily but that we can change quite easily self dot let's call it item Sprites this group doesn't exist right now but that we can change inside of the dunder init method I want to create item Sprites with that we have easy access to all of the items first of all we want to check if there's any item in the level at all so if item Sprites only then do we want to check if there's an item Collision after that we want to check item Sprites that the player has collided with which we can get with py game. sprite. Sprite cite for that we want to check one Sprite Which is our self. player next up we want to check the collision between this player Sprite and a group of Sprites which in our case is item Sprites and then we want to tell pame to destroy the Sprite that the player has cided with which means this one should be true after this method has run the Sprite will be removed from any kind of group and then stored inside of item Sprites which means we can check if there are any item Sprites if that is the case for example we could print item Collision after that all we have to do is run this method right below self. hit Collision self. item collision and then this should work let's REM at p and we are getting item Collision that looks pretty good what is even better we could actually print item Sprites and this is a list of values from which we only want to get the first one and on there we have an item pipe now if we run main.py we get silver skull potion diamond and then we we also have a gold coin there we go this works perfectly well now with that we could start the actual interactivity in the game but there's one more thing to do mainly if you collide with an item the item simply disappears and that doesn't feel right also if a pearl hits the player nothing happens which I also don't like ideally there should be some kind of particle effect which we can create fairly easily I want to create a class called particle effect Sprite Which once again is going to inherit from animate Sprite we will need a Dunder init method with self a position rames and a group or rather groups to keep a bit more consistent here next up we will need a super ther inage method with the position the frames and the groups all of which we getting from the init method of the class itself also just as before we want to update this Center so self. re. Center is going to be the position that we are getting on top of that I want to update the Z position the particle effect should always be on that layers and FG it should always be on top of everything else after that we will need to overwrite the animate method although for the parameters we still need sell and Desa time so what we have to do inside of animated Sprite we keep on running an animation forever which works in general however for the particle effect we want a slightly different system we want to play the animation once and then destroy the Sprite Which means this logic it doesn't work anymore because this one never stops however the first line still works just fine this one we can just copy and now we want to check if self. frame index is smaller than the length of self. frames if that is the case we want to update self. image which is going to be self. frames with the integer of self. frame index however if that is not the case else I want to destroy the Sprite and that is pretty much it with that we have a particle effect although next up inside of main. Pi we once again have to to import a couple of Graphics what I want to import is this one a key called particle and then folder Graphics effects and particle if you go to Graphics there we have effects and this one only contains a single folder called particle and there we have a very simple particle animation that's the one that we want to use so now inside of level. Pi when the player collides with an item we want to create the particle Sprite effect or particle effect Sprite whatever I called it we first of all have to import it into the level. P class and after that we want to create one instance of it for which we will need a position frames and groups for the position we want to get the item which we already have then get the rectangle of the item and then place the particle in the same Center for the groups we simply want to go with all Sprites also I can get rid of the print statement finally for the frames we have a bit of an issue because the frames are only available inside of the setup method and the dunder init method they are not available inside of item Collision the same problem we had inside of create Pearl for which we created a separate attribute which we want to do again we have a pearl surface and right below I want to have a particle let's call it frames this is going to be level frames and the key for this one is called particle and that we want to use so inside of frames I want to get self. particle frames and that should be it if I now run m not pi and I collide with an item that looks really good let's try it with the gold coin Point as well and yeah we definitely have this one working the same thing we want to do now inside of hit Collision we already know if the player was hit by a pearl after which we are destroying the Sprite but on top of that we want to create a let me copy it a particle effect Sprite although for this one we have to update the position this should be sprite. rec. Center the other arguments are totally fine though let's try this one and now if the play gets hit we have a little particle effect that feels quite nice finally we want to cover the Pearl hitting a wall Collision for that we have Pearl collisions and once again we are running pygame Sprite Collide and this one is going to return the Sprite that has a collision which we can stall inside of an attribute let's simply call it Sprite that one is going to be a list and if if there's anything inside of the list we want to run some code essentially once again let me copy it from the items we want to get the first item from this list so Sprite Zero rec. Center then we have the particle frames and all Sprites that should actually be it although now this is going to be slightly difficult to test but this one definitely works let me jump again and yeah cool with that we have a couple of particle effects which make the game feel much better so finally we can start working on the interactivity and there are quite a few parts that we have to cover first of all the player should be able to attack the tooth enemy and also the pearls in both cases the direction of them should be reversed I.E if the player hits too then to should start walking in the other direction and if the player hits a pearl then the Pearl should reverse for for all of that I want to create another method that I'm going to call attack Collision in there we're going to check the collision between the sword of the pirate when the pirate attacks against the tooth enemies and against the pearls I first of all want to get all of the attackable objects let's call them Target and basically I want to get self. Pearl Sprites and then I want to specify I want to get the Sprites because with that I will get a list that I can add to another list the list I want to add is two Sprites and on there I want to get the Sprites again that is going to give me all of the hitable Sprites after we have that we want to run an if statement with three conditions the first one is if this target is colliding so target. rec. collide rect with self. player. rect and for this one I do want to highlight that we want to use the rectangle remember the hitbox rectangle does not contain the sword this one is only contained in the main rectangle of the player and this is the one that we want to collide with at the moment on top of that we want to check self. player. attacking that is an attribute that we already have I created that one ages ago so far we have only used this one for the animations but we can also use it for the actual attack logic so with that at the moment we are checking if an enemy is colliding with the player sword and if the player is attacking however that is not enough there's one more condition and that would be facing the target the reason why we need this one is imagine we have the player once again again and by default the player is facing to the right if we now have a condition where a Target collides with the rectangle let's say this one could be on the side and the player is attacking which would be fine but the player is attacking to the right direction but without the final condition even this enemy would be affected by the attack which would be very weird now this facing Target we have to create facing Target and for that once again we need to figure out if self. player. rect and we want to look at the horizontal Center if this one is let's say smaller then the target do wct do Center X if that is the case the player is to the left of the enemy we have the player here with some kind of X and we have the the enemy with a center and the important part is that the enemy Center is greater than the player Center so the enemy is to the right but this we only want to check if the player is facing to the right which we can check with an end statement because inside of the player we have self. player. facing uncore right once again we have used that attribute earlier for the animations but we can use it multiple times this would one condition but we want to cover another one with an or statement I want to cover self. player. rec. centerx is greater than target. rec. Center X and not self. player do facing right that should cover both sides with that we know if the player is facing the enemy or not with that we can finish the if statement and at this point we know that the enemy was hit by the player attack which means we can call target. reverse which is what we want to do for both the tooth and the Pearl but this method doesn't exist right now for that inside of enemies we want to create it and I want to create reverse with self and for now let's simply Print Pro for the two and for the Pearl we want to create uh the same method reverse that for now Prince Pearl next up before we forget we also have to call attack Collision which we're going to do right below the item collision and now we can see if I attack we are getting Pearl and if I attack the tooth we are getting too so we definitely detect the attack next up then we have to reverse the direction for either of them that is something we have already kind of done down here we simply want to multiply the Direction with negative one this would work but it wouldn't work consistently let me try actually if I now attack the tooth enemy it works this time but if I try it a couple of times okay this actually works reasonably well but it doesn't work perfectly the reason is when we are running this method we might hit the enemy multiple times I.E this condition might be true multiple times in a very short amount of time because of that the game might register two attacks within 10 milliseconds of each other which I want to avoid for that inside of the Toth enemy I want to create a timer let's go call this one the hit timer which once again is going to be a timer with a duration of let's say 250 milliseconds this timer we first of all have to update self. hit timer. update and after we are doing that I only want to reverse the direction if not self. hit timer do if and then once we have reversed the direction I want to activate the timer s do hit timer. activate that way the game shouldn't change fundamentally I can still attack the enemy this one works just fine but now it works just a bit more reliably next up then I want to work on the Pearl this one is going to work in basically the same way which means first of all I want to create a let's call it a reverse timer with a timer that has a duration of 250 milliseconds if that timer is not active meaning self. timers and reverse if that is the case self. direction should be multiplied with -1 and we want to activate the timer for that let me copy it and then activate it also I'm realizing this should be do active and it should be not active at the moment but other than that this should be it and now I can attack the pearls and they go in the opposite direction that works really well that means next up we can work on highlighting if the player was hit and that we already do to an extent inside of level Pi in hit Collision we are checking if the player was hit and if that is the case we are printing player damage but that obviously isn't enough for an actual game instead I want to get self. layer and then run a method let's call it get damage this one doesn't exist right now so inside of the player I want to collapse all of the methods and then create another one all the way at the bottom yet underscore damage no need for custom parameters and then in there we need a couple of things I suppose for now we can still print player was damaged let's try that and if the player gets hit this still works however if we have a collision with too we are getting multiple collisions and if that happened in the game the player would get basically infinite damage in a very short amount of time simply because we have we have a huge amount of frames to overcome that I want to create a timer inside of thunder in knit we already have a few timers let's call this one the hit timer which has a duration of 400 milliseconds and then I only want to run this print statement if not self. timers with hit is active on top of that if the player was hit I want to activate the timer so [Music] activate with that this one should still work just as before that's good and now if I get hit by tooth we only get one actual damage Point perfect that's a good start also while the player is damaged I want to indicate that the player currently gets damaged and for that I want to flicker the pirate the way we are going to approach that is first of all if self. timers and the player currently is hit if that is the case I want to fill the entire surface of the player with a white color and for that we are going to need a mask now I don't have time to explain masks in detail but once again I have made a complete video on it check that one out for more information but essentially we're going to create a white mask which we're getting with Pip game do mask and then from uncore surface this surface we want to look at is self. image this is going to give us a mask of the player or of the player image to be a bit more specific that means that every pixel on this image that is transparent will be black and every pixel that is not transparent will be white as a matter of fact I can demonstrate from this white mask I want to create a white surface which we get with white mask. 2core surface with that we have the silhouette of the player which we want to set as the image so self. image is going to be the White surface after that all we have to do after the animate method we need to call self. licker also I realize I want to check if self. Tim is hit is active and now if I remain not pie and the pirate gets hit we can see for a short amount of time we have the white outlines of the player along with the black pixels wherever we have a transparent pixel inside of the image this is what a mask does although in our case we only really care about the white pixels to get rid of the black ones we want to get the White surface and then set a color key with the black color and now if I of Pi the player is only white for a short amount of time after we get hit and this works both with the pearls and with tooth so that looks pretty good that's a good start although we can refine this I don't want to have a pure white color that is solid I want to flicker this a bit more for that we can once again use the signed function which I want to import from math import sign the reason why that is useful imagine once again we have the triangles we talked about inside of the spike class and we are getting the Y part via this sin function depending on the angle this one gets larger or smaller this is useful because if we have the entire circle with 360° we get a lot of triangles on this for example we could have a triangle right here and then Des sign value would be reasonably small however we could have another triangle that is much more up there and then we would get a much greater y value on top of that we could go negative meaning if we have a triangle down here we would get a negative y value and this always depends on the angle but if you added all of these values together you would get what is called a sign curve that bounces between the values of one and -1 and keeps on doing this forever that we can use basically inside of the flicker method I only want to flicker if the player is hit and if our sine wave value is greater or equal to zero which means and sin of py game. time.get uncore x if that value is greater or equal to zero only then do we want to flicker a player and now if we run this we are getting a whole bunch of flickers and this works multiple times let's try the tooth again cool that works well you can also customize how fast this flickering happens all you have to do is multiply the amount of time let's say If I multiply this with 100 then we get a faster flicker or if you divided it by 100 you can I think see a bit better what's going on now we have a much slower one but in my case I want to keep it at multiplied with 200 is what I think I Ed in the actual game I suppose we can stay with 100 that feels like a good neutral value but once again choose what you like the most here whatever looks best is what's going to work for you perfect with that we you can indicate that the player was hit and with that covered we can come to the actual part where we are storing and updating the health and the coins of the player for that part we already have a problem it might make sense to store the health of the player inside of the level or inside of the player and that might be a good start but it would cause a ton of problems really really fast because later on once we have an Overworld we are going to create new instance of this level class fairly often hence whatever value we are storing in there will not survive once we creating a new instance of a level instead we have to store all of that outside of the level or in some kind of class that we can access quite easily in my case I have created a whole game data class for that I want to create a new python file and let's call it data. piy all we want to do in here is create a class called data and by the way for this python file we don't need py game we simply use basic python to store all of the data there's no py game needed inside of this class I want to have a Dunder init method and then in there we have self. coins which by default is going to be zero and then self. Health which let's say it's going to be five at the start of the game this is going to be where we will store all of our data and then inside of main.py I want from data import the data class and then before we are importing the TMX maps and creating the level I want to create one instance of this class self. data is going to be the data we have just created after that when I'm creating the level I want to pass in self. data that way later on we keep the data inside of the game class and because of that the data does not change when we are changing the level first of all though inside of level. Pi we need one more parameter data which we want to store inside of an attribute self. data is going to be data that however still isn't enough because at the moment we are checking inside of the player if the player has been damaged so in there we want to update the data class for that to work we have to pass data into the player as well that happens inside of setup and more specifically inside of the objects we already have the player in there I want to assign it data to data or self. data to be a bit more specific next up then inside of the play ler we need one more parameter for data that once again we are going to store inside of an attribute self. data is data let's run main.py to make sure nothing went wrong cool the game still works just fine but now we have the data inside of the player and that we can update I want to get self. data and then if the player gets hit update the health part of it and reduce it by one to make sure that this one is working inside of the game class in the run method to be a bit more specific I want to get self. dat and he and print all of that let's run this and we're getting five four if it shoots again three and that looks good and if the player gets hit by two we're getting to two so this also works just fine and by the way if you don't like printing game information there's one more class you might want to check out and that is the debug class if you open this one it's not a terribly powerful class all that we are doing in here is we are getting the display surface then we are creating a surface and a rectangle with some kind of text information and that we are drawing the reason why this one is powerful if inside of main.py you import it from debug you want to support debug you can simply call debug although this has to happen before you call py game. display. update but if you now run it in the top left you can see the amount of Health that we have and this one still updates just fine and that makes it a bit easier to see what's going on in the game which I tend to prefer now this class isn't a necessity but it can be a nice utility feature anyway with that we get our health information on top of that I want to work with the items for that inside of the level I want to have a look at the items that we created all of those since the items are also supposed to update the player data we want to pass data in there as well so self. data as an argument after that inside of the Sprites in the item class we need one more parameter data which we want to store as an attribute self. data data is going to be data after we have that we want to create another method let's call it act fit and then we can check if for example self. item type is equal to let's start with gold if that is the case I want to get self. data and coins and increase this one by five after that I can duplicate this if statement change it to to Silver that one would update the amount of coins by one I can duplicate all of this again next up we want to check for a diamond this one would update the coins by 20 and finally we have the skull for which the player would get 50 coins finally there's one more item which is the poan this one targets the the health of the player and increases it by one that's all we need inside of this class afterwards inside of level. Pi once we have an item Collision inside of the item collisions we already get the item that we have collided with that's simply the first item inside of item Sprites which means I want to get item Sprites the first item and then active activate that one with that inside of the game right now we have four Health points but if I collide with a potion it goes back to five so that works perfectly well also we can now look at self. dat. coins Run This Again by default the player has zero coins but if I collide with a silver coin and a scalp we get 51 and if I get a diamond we get 71 and if I get the gold coin we should be at 76 and we are all right so with that we have all of the data from the level but we are not really displaying it we are simply using debug to show what number we have but for an actual game once again this doesn't feel right instead I want to create a UI which I'm going to store in a new python file ui. High also we have quite a lot of tabs open we don't need the enemies anymore and at the moment we don't need the groups either first of all inside of the UI I need from settings and import everything after that I want to create a class called UI inside of which as always we will need a Dunder init method inside of this one for now we will need self and we will need a font on top of that we will need a couple of frames those are going to be the hard frames later on that will show the health of the player although first of all we want to get self. displore surface which is going to be py game. display. getor surface with that we can draw on the display surface next up we want to get self. Sprites which is going to be a simple pygame dos sprite. group and after that we want to start a font inside of an attribute f after that there are two things that we fundamentally want to create number one their health or their hearts however you want to call it and number two the coins the hearts are going to be a bit more extensive so let's start with that one first of all for that we want to get all of the hard frames which I want to store in a hard frames list all we need for that are the frames and then I call this one hard now this one we don't have at the moment to overcome that inside of main. Pi in the import assets I don't want to do all of this instead of level frames because these frames should not be inside of the level they should be in the UI hence I want to create self. UI frames which once again are going to be a dictionary the only thing that we really have there is the heart which we are getting with import folder and then we importing Graphics UI and heart a very simple heart animation and that is all we need for now although when we are creating one instance of this class we want to pass in self. UI frames with that we getting the frames and from that we are getting the hard frames later on we are also going to get coin frames but that's going to be an issue for later for now to get started I want to call a method create hearts and by default I want to create five Hearts now this method doesn't exist right now so let's create it create hearts with self and the amount of hearts that we want to create fundamentally all we want to do is for heart in range of the amount of hearts and then we want to create a heart this is going to be a whole separate Sprite class which we can create inside of the UI python file class heart right below although technically this is going to be a Sprite so you could also place it inside of Sprites it doesn't really make a difference that being said though since the heart is going to be animated I want to inherit from the animated Sprite Which means in inside of UI I want from Sprites import the animated Sprite that is going to be the parent class of the heart after that I want to create a Dunder init method with self a position frames and groups all of these parameters we can use right away inside of super Dunder init to initialize the parent class and there as always we are passing in the position the frames and the groups that being said though the animation for this heart is is going to work just a little bit different but that's going to be an issue for later but now I simply want to create a heart for which we will need the free parameters position is going to be X and Y which we are going to cover in just a second after that for the frames we want to have self. hard frames and the group is going to be self. Sprites before we can continue we will need x and y y is going to be simply 10 pixels I should explain by the way if this is our window I want the hearts to be in the top left let's say one here one here one here and so on at the moment I have specified the Y part and we are always specifying the top left at the moment we have an offset of 10 pixels from the top for X we will need a bit more Logic the first heart should be 10 pixels from the left the second heart should be a bit further to the side and have a bit of padding to the next heart although to get started we could use the number 10 plus the hard multiplied by 50 hard in this case is going to be an integer that goes from 0 to four or in other words we're going up to five but we don't include five and this is going to give us a new x value after we have all of that we will still need for the UI class something like an update method or display method and there for now we simply want to call self. Sprites do draw and we want to draw on self. display surface also we want to update all of the Sprites so self. Sprites do update with Delta time after we have all of that inside of main.py we have to import this UI class so from UI import UI and then right before the data I want to create an attribute called UI for this one we will now need a font and a couple of frames the frames we already have but the font we don't we can fix that in just a second for now let's simply say self. font and after that self. UI frames now the font doesn't exist but that we can fix fairly easily before we are getting the UI frames self. f to create a font we need py game. font. font with a font type and a font size the font size in my case is going to be 40 pixels and the path to the font we're getting via the join method we want to go up a folder we want to go to Graphics we want to go to UI and in there we have a file called RuneScape uncore f. ttf and with that we should have a UI class and I realized for the data class the self. UI frames shouldn't go in there let's try all of this and this is working pretty well or at the very least the game isn't crashing after that inside of the run method after we are displaying all of the current stage I want to get self. UI and then up the entire class with Delta time if I now run this we getting a whole bunch of Hearts they're not updating with the health of the player yet but at the very least we have something but on top of that they also update continuously which I don't really like it feels too generic I know it's hard to describe it just doesn't look good now you could be a bit lazy and simply use a lower number let's say something like 20 now if we run this that feels quite a bit better and also if I don't run the debug method you can actually see what's going on now on the top left we have all of the hearts and that's a good start but I want this system to be a bit more flexible essentially before we are creating the hearts I want to get self. heart surface Wii which we get with self. hard frames we want to get the first index which is going to give us one surface and from this we want to get the Wii on top of that I want to get self. hard heading which I've have set to five this is going to give us the amount of space between the hearts and the five I have chosen because it looks good if you like another number just go with that and after that we do want to keep the 10 as the starting value but instead set of hard * 20 I want to get the heart and multiply it with self do hard surface Wii plus self. hard heading just for some numbers the first four Loop is going to give us a heart of zero which means all of this is going to be zero the second heart however is going to give us a value of one then let's say the heart surface has a width of 18 pixels and the hard padding is five so the result is going to be 1 * 23 that number means we are placing this hard 23 pixels further to the right than the previous heart and since the previous heart has a width of 18 pixels there's a gap of five pixels that should be all we need if I now run of this again that looks pretty all right if we increase the padding to something ridic ridiculous let's say 20 pixels We Get Much More spacing between them let's try maybe six to see how that looks yeah I feel that looks about right but once again play around with the numbers to see what you like next up though I want to work a bit more inside of the heart I want them to only occasionally animate we get that system to work I want to create another attribute self. active which by defa default is going to be false after we have that I want to create an update method with self and Delta time if the class is active then I want to call some kind of self. animate method with data time this animate method we're going to create in just a second we already have one inside of the animated Sprite class but in just a second we are going to create our own however if that is not the case so else then I don't want the heart to animate however while the heart isn't animated there should be some kind of timer and if the timer runs out the heart should start to animate again now for all of that we could use an actual timer but that's a bit of an Overkill an easiest system would be from random import Rand in and then inside of the El statement if Rand in let's say from zero to 2,000 I.E we are generating random numbers between these two values if that value is equal to one then we want to set self. active to R since that case is fairly unlikely it will take some time hence we get a timer now this timer is not perfect because it is not frame rate independent that being said in our case the difference between the frame rates shouldn't be that great so this system is basically all right after that we want to create a custom animate method with self and Delta time once again we want to update a frame index which we're getting from the animated Sprite parent class this one we want to update with animation uncore speed multiplied with Delta time and after that if self. frame index is smaller than the length of self. frames then we want to update the image so self. image is going to be self. frames with the integer of self. frame index however if that is not the case so we have finished the animation then we want to set self. active to BS and we want to set self. frame in index back to zero that should be all we need for now if I now run m.p again the hearts should only animate once in a while and there you can see we have the occasional animation but it doesn't happen constantly anymore which I think looks quite a bit better that covers the heart class and next up we can actually make this system interactive I.E make the UI work along with the data class all that we really want to do for now if the amount of Health inside of the data class changes then we want to change the amount of hearts that we have inside of the UI for that the data class needs to have access to the UI which we can do fairly easily when we are creating the data class I want to pass in self. UI for that to work we will need one more parameter UI which we want to store as an attribute right away UI is going to be UI so now we have another issue we want to call a method on this UI class if the value of Health changes and the way to approach that is by creating a Setter and a getter method which is quite a powerful system that you can use in Python although for that to work you have to understand decorators at least in a very basic sense once again I have made a whole separate video on the topic which can get a bit more complicated so I am not going to go into too much detail but basically what we are going to do the health we have so far worked with is going to become a private attribute it should only be available inside of this class which we indicating via an underscore although this underscore here is just a convention to tell other programmers that this attribute is only available inside of the class and shouldn't be available from the outside however now if I run may. pi and we're trying to use this one we get data has no attribute Health did you mean underscore health and well not really instead what we want to do is we want to replace this Heth with a Setter and a getter method those are essentially going to replace the health attribute first of all whenever we want to read the health attribute we want to create a property and then create a method that has the same name Health this this one is just another method so we will need a parameter with self inside of the method we want to return self. uncore Health whenever we are trying to read the health attribute we are returning the value of the underscore Health attribute as far as anything from outside of the class is concerned health and underscore Health are the same but internally in the class they are different and this would for example allow us to do literally anything else before we are returning the value for example we could print Health was red but that we don't want to do instead we want to create a Setter class which we do with at and then health. setter this once again is going to be another method that we're going to call Health this one will need one parameter for S and then another for the value inside of this method we are are actually updating the health attribute and we are setting it to the value now once we have all of that we have replaced the health attribute with a getter so we can read it and a Setter so we can update it and with both of those before or after we are updating the value we could run any other code but first of all let's see if this one is working in the first place and we still get no errors let me try to collide with it too that works pretty well and also we can read the value I.E we could print self. dat. health and run all of this and we get the very same outcome so that works pretty well however what we can now do is whenever we are printing the health I want to print Health was red and now if I run main. Pi we get health was red whenever we are printing the health but that once again is not the part that we care about instead what we care about is whenever we updating the health we want to call something on the UI self. UI and what we want to call is create underscore Hearts the method that we actually already have basically what we are going to do when we are calling the method we will create a new amount of hearts and before we are doing that we we going to get rid of all of the previous Hearts meaning before we are creating all of the hearts I want for sprite in self. Sprites and then destroy all of the Sprites that way when we have an update to the data class we want to create hearts with our VI value inside of UI then we are going to call this create heart method get rid of all of the previous hearts and then create new hearts with the right amount with that if I run main of p and we get hit one heart disappears and a second heart disappears and if I hit to another heart disappears cool so this one is working also when we are starting the game this create Hearts shouldn't be there let me remove it instead inside of the data class when we are initializing all of this I want to get self. UI and create hearts with self. uncore health with that inside of may. Pi we get the same starting amount although now we are keeping all of the actual health information inside of the data class cool next up we have to work on the coins which is going to be a fairly similar system as a con consquence this part is going to be your exercise when the player collides with a coin then display the amount of coins the player has in total for a short amount of time let's say one second pause the video now and see how far you get for all of this you could approach this in a few different ways and I hope you experimented but in my case I created first of all self. coin mount which by default is going to be zero also then we have self. coin timer which is going to be a timer with a duration of 1 second this timer doesn't exist right now so we have to import it from timer import timer after that we don't need the hearts anymore I want to create another method that I will call display uncore text for now I simply want to create a text uncore surface with self. font. render where we are going to display self. coin amount although this has to be a string so Str Str after that I don't want to analias the text and then we are going to need a color let's say for now I want to use white next up we will need a text uncore rectangle which we get via the text surface and then getorf rectangle I want to place the top left the position I went for is going to be 16 and 34 finally after we have all of that self. display surface. blit with the text surface and the text rectangle if we now call self. display text we should see at least some text and there we can see a zero and this one doesn't update at the moment so it's a start but not a deal to make that work better we have to update this coin amount whenever inside of data we are updating the coins for that I want to create another method let's call it show points this one is going to get one amount parameter in there I want to update self. coin amount to the amount that we get and after that inside of that Pi I want to turn the coins into an underscore coins and then do basically the same thing I have done for the health so let me copy all of this and this should be coins when we are reading the coins attribute I simply want to return underscore coins however when we are trying to update that value I first of all want to update the amount of coins to the value that we get but after that inside of UI I want to show the coins I think I called it yeah show coins and then pass in the value with that we are updating the value of self. coin amount and that in turn should update whatever we are displaying let's try this one now and I getting an error that I need to update all of this to coins. Setter so now let's try this again and if I pick up a silver coin we get one if I pick up the gold coin we should get to six and I die died let's try this again we should get 251 and we should get yep that looks good 271 meaning now all of the coins are being displayed properly that looks good but I don't want to display them constantly so inside of the game I don't think there's much of a point to always display the amount of coins that the player has you could do it but it doesn't feel right the way I get around that is as soon as we are showing the coins I want to start the coin timer so self. coin timer. activate and for that to work we have to update the timer self. coin timer. update in the update method after that inside of display text I want to do all of that but only if self. coin timer is active and with that by default we do not show the coins however if I collect one for one second we can see the amount of coins that we have and that feels pretty good on top of that I want to display a simple Graphics so we know that we are talking about the coins for that inside of main.py I want to import one more graphic that's going to happen inside of UI frames I want to import a coin graphic which is going to be as simple image and this we then have inside of UI frames so after that inside of ui. Pi we can still use those frames although we have to store them inside of a separate attribute let's call it self. coin uncore surface which is going to be frames and coin after we have that inside of display text we are first displaying the text and then I want to display the a coin graphic for that I will need a coin rectangle first of all which we get with self. coin surface and then get F wck we want to place the center in the bottom left position of the text rectangle which means we want to get the text rectangle and the bottom left of that after that self. display surface once again with the blit method self. coin surface and coin rectangle the difference isn't going to be major if I now pick up a coin we get a coin next to the number and I feel like the coin should be just a little bit higher for that we can use the move method to move this rectangle up by -6 pixels and now if I pick one up that feels a little bit nicer once again the more you play around with this the better it's going to look that being said I don't like the default white color and I have a separate value called 33 32 and 3D if you now R main.py we are getting a darker color that is going to work much better once we have a proper background we are nearly done the last thing that I want to do is if the player collects more than 100 coins we want to set the coins back to zero and increase the health by one for that inside of the coin Setter method after we are updating the coins I want to check if self. coins is greater or equal to 100 if that is the case I want to reduce the amount of coins by 100 so minus equal 100 and on top of that self. Health should be increased by one and then finally when we are showing the coins I don't want to get the value instead that I want to use self. coins now at the moment inside of our level we don't have more than 100 coins but that we can fix quite easily inside of tiles I want to duplicate the skull let's say a couple of times with that we can try all of this and now if I pick up two scals we get to six health and if I pick two more we getting to seven that looks pretty good and the rest of the logic works just fine and all of that was a longer part but by now we have an actually interactive game next up we can work on the sky and then we have an actual level we are nearly done with the level there's just one more major thing that we have to cover and that is the background for that we are either going to have a sky or we are going to have some kind of tile setup on top of that what we are also going to cover are some more additional elements like the water in the level or constraint for the camera so we cannot see outside of the level let's start with those two parts actually because they are fairly straightforward once again we are back inside of main.py and for this part I want to close the UI and the data python file because we are not going to need them instead I want to work inside of level. Pi and I suppose to get started we can work on the water because at the moment if I run main.py we don't really have any water which looks weird we want to look inside of setup and then minimize everything because we want to create another for Loop all the way at the bottom I want to create a water section all we need for that is for obj in TMX map and then get layer by name the layer we want to look at is called water what that is going to mean inside of til we we have the water layer and the water layer only contains a single object a larger rectangle on this rectangle we want to look at every single individual cell which means we want to start with the first row and then look at this cell this cell this cell until we reach the end of the rectangle after that we go to the second row third row until we finish the entire thing and basically on the first row we are going to have a wave animation something like this but on every other row we are simply going to have a semi transparent surface that way we are going to get a block of water that will be animated which means inside of the for Loop we first of all want to know the rows and the columns to get either of those we want to get for the rows obj do height and then divided by the tile size and this needs to be an integer the same thing we want to do for the columns it's going to be a fairly similar operation except now we want to do obj do wift with that we know how many rows and columns we have inside of the object after that we want to get four row in range rows and then four call in range columns that way we are getting every single cell inside of this rectangle after that we have to convert our columns into X and our rows into y positions the way that is going to work for X we're going to get obj dox and that is going to be the top left of the rectangle and to that we want to add the column multiplied with the tile size imagine that this is the entire rectangle that we are importing objx and obj doy would be the top left point to that position we want to add the column multiplied by the tile size the column is going to be an integer that starts at zero and goes let's say up to 10 for the first four Loop the value is going to be 0o and 0 * 64 is still going to be zero so we are going to have a tile all the way on the left side however on the next for Loop this value is going to be one so we are jumping 64 pixels to the right and then create create one tile here that kind of logic we also want to do for the vertical part let me copy it the only real difference is that this is going to be obj doy plus row multiplied by the tile size with that we have the position of all of the tiles next up we have to figure out if we are on the first row or on any other row which is fairly easily done all we want to check is if row is equal to zero if that is the case we want to create an animated Sprite for that inside of Sprites I want to get the position frames and groups along with Z the actual arguments we're going to add in just a second first of all though I want to create a regular Sprite for which I will need a position a surface groups and Z I.E I want to have a Sprite with these arguments although let me get rid of the default values now before we can start working on windows we have to once again import a whole bunch of Graphics that's going to happen in Main and it's going to be fairly similar compared to what we have already done inside of level frames I want to import two more things water top and water body water top is a whole folder with an animation whereas the water body is simply an image that is semi-transparent basically it's a semi-transparent blue image here is the graphics folder and we want to to look at the level in there we should have water and then in there we have the body that's what we're importing for the water body and the top part is going to be a basic wave animation that's all that's happening in here now we have the surfaces with that we can actually create the animated Sprite and the Sprite for the position for both of those actually we want to have a tupal with X and Y also for the group groups I want those to be in self dot all Sprites same for the Z parameter I want to be in Z layers and we want to get the water key if you look at settings we have water finally then we are going to need the frames and the surface for both of those I want to get the level frames for the water animation I want to get the water top frames let me paste them in and for the water body I want to have the water body part and that should be it if I now run main not Pi we can see that we have water and that works pretty well although it's not perfect because if you jump outside of the level we can see that this system breaks down quite fast that we're going to fix next but first of all this system is working reasonably well so I'm pretty happy with that and with that we can close the setup method we are basically done with it next up I want to constrain the camera to the level and that is going to mean a couple of things first of all it means that we have to constrain the player to the level itself I.E if the player leaves the constraints of the level we either want to constrain the player itself to the level or we want to end the level if this is our entire level with all of the tiles and the random enemies and the player being in there somewhere if the player Falls too far down then we want to kill the player if the player walks too far to the left then we want to constrain the player to the left side of the level and finally if the player reaches the flag somewhere here then we should finish the level also I suppose what we can do if the player goes too far to the right then we should also constrain the player in that direction we don't have to constrain the player at the top because the player cannot fly so that part should be okay for all of that I want to create a new method let's call it check constraint in there we want to first of all Left Right constrain the player and that is terrible spelling essentially what we are going to do if self. player. hitbox rectangle. left is smaller or equal to zero and if that is the case self. player. hitbox rectangle. left should be zero once we have that all we need to do is call this method self. check constraint and now if I run main. Pi once more I try to go all the way to the left at some point we cannot do that anymore and that feels all right cool so that's the first part next up I want to do the same thing for the right side so if self. player. hitbox rectangle. right is greater or equal than some number and this number we don't know right now although we can get it but that has to happen inside of the dunder init method all the way at the top I want to create some level data first of all in there I want to have self. levore wift and this we can get with TMX map. wift let me actually print what we're getting from that and while we are testing that I want to comment out this part if I now run my. Pi we are getting 40 that 40 means that our map has 40 columns so if you click on resize map we have a whiff of 40 tiles that part Works inside of tiled but for our level we want to have pixel width so we want to multiply all of that with the tile size and that is going to give us the width of the level which means I can now copy all of that and then uncomment this bit and check if the right side of the player is greater than the level width if that is the case self. player. hitbox rectangle. right should be equal to that point let's try that part and now I have to walk a bit further to the right and the boat is right there so now I should not be able to go further than this point and I'm not a bit hard to see but it's definitely working next up then I want to check the bottom border for that all I really need is self. player. hitbox rectangle. bottom if that one is greater then let's call it self. level bottom that point also doesn't exist right now if that is the case for now we want to print def later on if that is the case we are going to go back to the Overworld but since we don't have an Overworld right now that's not really an option but step by step first of all we have to create a level bottom that once again is going to happen inside of level data in fact it's going to be quite similar to the level Wii all we need is TMX map except this one is going to be the height which we want to multiply with the tile size let's try all of that and now if the player falls down we can see death so this part is also working just fine next up then I want to check the success State for the level which basically means that the player is reaching the flag that we can check with if self. player. hitbox rectangle and collide wct with let's call it self. level finish rectangle this one once again does not exist right now which we are going to fix in a second first of all though I want to print success so let's create the level finish rectangle and that we are actually not going to do inside of the dander init method instead we are going to do that in the setup method because when we are looking at the objects all of this somewhere in there we have the flag at the moment we are only checking if we have an object named player or anything else but on top of that I want to add another if statement that if statement is going to be if obj name is equal to flag if that is the case then I want to create the level finish rectangle which is just going to be a rectangle with a top left and a size the top left is fairly easy we want to have obj dox and obj doy for the size this doesn't get much more complicated we want to have obj do WID and obj do height and that is pretty much it now we can run main.py and once the player touches the flag we should be seeing success that looks really good cool so with that we can check the various constraints on the level that's a really important start and later on really important to connect the level to the Overworld although it doesn't make the game look that much better for that we have to re open the Sprite group or in other words we want to work inside of all Sprites that one we have stored inside of groups so that's what I want to open inside of this all Sprites class we are going to display all of the backgrounds simply because it's quite easy anything we are drawing before this for Loop will always be in the background which makes our life significantly easier for that though the all spreads class needs to know quite a bit more first of all we want to know the level width and the level height both of those we are already getting inside of the level so when we are creating all Sprites I want to pass in self. level wi and self. level underscore bottom level bottom and level height are the same thing the naming here isn't ideal admittedly just to make sure you understand if this is our level the level bottom is going to be this side which is always going to be the height of the level so those two numbers mean the same thing although in practice I suppose it can be a bit confusing anyway though with that inside of the level we know how wide and how tall the level is all of that we want to store as an attribute right away self. width and self. height are going to be width and height that is already important information information and next up we want to get from til some kind of info if we want to have a sky or if we want to have some kind of tiled background if you look at tiled we have one layer called Data this one also only contains a single object which is called Data itself and in there we have a whole bunch of parameters the only one we care about for now is going to be BG this one could be blue could be green and there are a few more options it could also be empty if that is the case the background should be a sky how that system is going to work in detail I will explain in just a bit for now just keep in mind that we are importing one value from this object which we also want to pass into all Sprites let's call it BG P which by default is going to be none next up then is inside of the level I want to create let's call it TMX level properties that is what we are getting from the object which I want to extract via TMX map get layer by name the layer we want to look at is called data and since this one only contains a single object we can use indexing with zero on that object we want to get the properties just to make sure that this is working I want to print TMX level properties let me run main. pi and there we are getting a dictionary the only value that we care about for now is BG which at the moment is blue that is important because if you open graphics and go to level there is a PG folder with tiles and in there we have a whole bunch of images these are the tiles that we want want to import for the background for example if tile specifies blue then we want to import this file for that dope first of all we once again have to import this folder in my case the key is going to be BG tiles and then we want to import a folder as a dictionary meaning we are going to get surfaces with the names of the files as a key all of that inside of a dictionary inside of level. Pi I want to check if TMX level properties. BG has a value if that is the case I want to create a BG tile and the value for that is going to be the level frames in there we want to go to the BG uncore tiles the one we have just created and remember this is going to be a dictionary so in there we can use indexing one more time with a keyword the keyword that we are looking for is going to be the background which means at the end of all of this our BG tile should contain a surface let's try and there we go we have a surface almost specifically the blue surface I just talked about this blue surface we want to pass into all Sprites and later on we are going to have a whole bunch of arguments for this class so I want to use named arguments right away we have width and height and after that I want to have the next argument would be BG tile which is going to be BG tile which will be the BG tile we have just created that being said if there's nothing inside of this background I still have to assign a value which we are doing with an ALT statement B tile can simply be done which means next up I can try all of this it's still working just fine cool after that I have a surface with a BG tile and this we want to use to draw the entire background meaning if this is our level we have a whole bunch of columns and rows this specific number does not matter what does matter is that for every single cell we want to display one of these PG tiles as a Sprite and the Sprite that we are going to use for that we're getting from the Sprite class this one which means I want to check if B tile exists in the first place and after that I want to look at every single cell inside of the level which we're getting with four column in range self Dot wift and then in there for row in range self. height this is fairly similar compared to what we have done with the water however I realized that my code setup isn't ideal because this width and height are pixel information inside of level Pi we are multiplying both with the tile size that way we don't get a number between 0 and 40 for the width instead we would get a number between 0 and 40 * 64 which is going to be way too large now to overcome that I'm going to paste in TMX map. wift straight into all Sprites and the same thing for TMX map. height then inside of the groups for the width and the height I am going to multiply both values with the tile underscore size that way we are retaining the proper size of the level but then inside of these lines I simply want to get the width and the height of the level via the grid so these numbers stay more manageable and after that I want to create an X and Y value for the position X is going to be column times the tile size and Y is going to be the row multiplied with the tile size after we have that we can create a Sprite simply a Sprite that we have already seen a couple of times and for that let me copy all of the parameters and also from Sprite import Sprite next up we have to get all of the arguments and those are going to be mostly familiar I think the position is the easiest part this one is just going to be X and Y the surface we also have this one will be the BG tile after that we have the groups and remember we are already working inside of a group the argument for this one is is going to be self finally we have the Z parameter and I want these PG tiles to always be first in line in this four Loop and since inside of settings all of the Z layers start at zero any negative number for Z in here is going to be at the beginning soga1 is going to always Place us behind everything else and that is pretty much it the only thing I have to do is fix my typo at the top this should be from Sprites inputs Sprite now let's try main. pi and there we go we have a background it doesn't yet work perfectly but if we are here is this looks much better on top of that if we are entitled we can change the background to green and then start main notp again now we have a completely different background and that starts to feel like a real level so if we are here not too bad although for this level we do want to have a proper sky so next up we want to constrain the camera so that the player cannot see the black background anymore all of that we are going to do in a separate method let's call it camera constraint essentially all that we have to do in here is at the moment we are always updating the offset to wherever the player is however if the player goes too far to the left then at some point we want to stop updating the X part of the offset or in other words self. offset dox is going to be self. offset dox but only if self. offset dox is smaller than zero if that is not the case else it should be zero so what does that mean to illustrate let me actually run self. camera constraint if I now run may. pi and go to the left at some point the camera simply stops going any further and the player also cannot go further so this looks pretty good and if you now simply look here or let's say down here it looks like the background is covering the entire background the basic logic is and remember the offset always runs in the opposite direction compared to the player so the further to the right the player gets the smaller the offsetx becomes because of that self. offset should be the offset itself if the number is below zero meaning the player is to the right of the origin point however if the player is to the left of the origin point then we want to set the number simply to zero and that way the camera sticks to the left side of the window once again the offset logic can be a bit confusing you basically always want to invert the camera to whatever the player position is this would be the left side but we have three more sides and to organize all of that I think the best way is to have a dictionary for easy access let's call it self. borders and there we have a left side that is simply going to be zero and then in camera constraint I want to replace the zero with self. borders and the left key that feels a bit cleaner because besides that I also want to have a right side I want to have a top side and I want to have a bottom side the right side is going to be self do whift and just to make sure that we are testing things step by step let me comment out the top and the bottom and instead we have to work a bit more with the camera offset once again I want to update self. offset dox this one is going to be itself self. offset dox but only if self. offset dox is greater than self do borders and we are looking at the right side if that is not the case else once again we want to have the same value in there and now if we are trying this we are getting some really weird stuff so what is going wrong and basically this number needs to be negative because once again remember the further the player is going to the right the further left the camera offset is going to be so whenever you're thinking of the right border you want to mirror the entire thing essentially or in other words we want to have the negative self. width now if we are trying all of this I can go all the way to the right and uh some point I shouldn't have the camera updating anymore and here I see a problem with the testing because the player cannot go further to the right than the level Whi that happens because of this check constrain but that's okay because in our case we want to stop the camera before anyway imagine that once again we have the level and the level ends on this side any that comes after is going to be a black background that we want to ignore which means if the player's on this position the player would be able to see all of this which we don't want this part should never be visible as a consequence the actual end part of the level should be much further it should rather be somewhere here so that the player can only ever see this part up to the end of the level to get that we want to add the window whift to the right border now if we're trying main.py and I can go further to the right at some point the camera should stop updating and there we go now I cannot go any further than this point or rather the camera cannot go any further and that looks much cleaner next up the same logic we are going to apply to the bottom because it's fairly similar we want to have negative self do height plus the window height and then inside of camera constraint we want to update self. offset doy which is going to be itself self. offset doy but only if self. offset doy is greater than self. borders with the bottom else it is going to be that value you let's try the level and if I fall down the camera stops updating and especially if we go back to the Overworld this is going to work really well finally then we will need a top argument and for that you could specify a zero that would technically work let me demonstrate if I duplicate this line we want to keep offset doy but only if it is smaller than the bord top and if that is not the case it should be the Top If I now run may. Pi we can never go further than this point so this part is also working just fine however sometimes you want to be able to jump further than that so you might want to disable the top border for that inside of til we have another attribute called top limit this by default is going to be zero but you could set it to another number if you want to have more vertical space face at the top to import that inside of level. Pi in the dunder init method we can specify one more argument that would be the top limit which we can get via TMX level properties the value we are looking for is called top underscore limit for that to work we will need a top underscore limit with a default value of zero and this value we want to pass into the top number so top limit in there since inside of til this value at the moment is zero this is not going to make any kind of change however we could set this value to something like a 100 run all of this again and now we have a bit more space at the top which if we had a background could work quite well however once again we are getting a black background ground which we do not want to see to fix that part we have to work inside of this logic specifically inside of the rows we basically want to now go from the top limit up to the height fortunately that we can do quite easily inside of the range function because in there we can set a start size and an end size the end size is still going to be the height of the level however the start size at the moment needs to be 100 simply because inside of tile the top limit is 100 once again remember for the offset we are going in exactly the opposite way which means in our case I want to have negative top underscore limit although this is going to be 100 right now whereas the height is only something like 20 I.E this one is going to work in pixels and this one is going to work in a grid to overcome that I want to divide the top limit by the tille size and then turn all of this into an integer that should already help a bit if I now run may. Pi we have a slightly smaller Gap the reason why there's still a gap is that the integer function is always going to round down so at the moment we have 100 pixels but we only drawing one extra tile whereas we are actually going to need two to fill the space and well the easiest way to fix that is simply subtract one from there and now we are filling the entirety of the space and that feels quite a bit nicer cool so now with that we have the background also we have the camera constraint this one we can minimize we are not going to need it anymore and let me clean this one up just a bit this feels nicer now I want to have a sky if we don't have a BG tile which means there should be an else statement and then we can create the sky first of all in there I will have to have a couple of Graphics I will need self do what is called a large cloud and I want to have self. small clouds which is going to be a list of surfaces and for all of that we already know how to import Graphics that always happens inside of the level frames I want to have two more entries for the cloud small and Cloud large once again Cloud large is going to be a simple image whereas the small clouds are going to be Graphics inside of a folder once again if you look at the graphics folder we have Graphics then we have level and in there we have the clouds we have a large cloud and we have small clouds and they are three different kinds that we could be using those we want to get into this all Sprites class for that inside of the level we have to pass into more arguments or I suppose to keep this one a bit more condensed I want to have clouds which is going to be a dictionary where we have the large Cloud which is going to be level frames and large uncore Cloud I believe I called it actually the opposite Cloud uncore large and after that we have the small clouds which is going to be level frames and let me copy it Cloud small after that inside of groups we will need let me do it before the parameters with default values I want to have the clouds and then the large cloud is going to be clouds large and the cloud small are going to be clouds and small let's run main. pi to make sure we get the Imports and we didn't get an error so that looks pretty good although we don't actually get to this part the way we are getting to the sky is inside of tiled you want BG to be empty that way we are getting none for BG tiles I.E if you look at level tiles you would get this BG tile n which we are then passing into this BG tile which is then in turn not going to run this if statement and instead we're getting to this part which means if I run main. Pi we are back to the black background not ideal but that we can work on we at the very least have a couple of Graphics but first of all I suppose we should go step by step and start by drawing the actual sky and for that first of all I I want to know if we should be drawing a sky or not for which I have another attribute called self. Sky the value for this one is going to be the opposite of BG tile if BG tile has no value then none is going to turn this value to true however if we have some value for B tile then it would evaluate to true so all of this would become false or in other words if we have a BG tile then self. Sky would be false but if we do have a BG tile then self. Sky would be true and if that is the case before we are drawing all of the Sprites I want to check if self. Sky then I want to run a method let's call it self. draw sky that method doesn't exist right now so let's create it draw Sky most importantly we want to get self. display surface and fill the entire thing with a color and for that I have a pretty fine color that simply looks good let's place it in there we have DD C6 and A1 if I now run m. Pi we're getting a very plain background color that's at the very least better than a black color after that we want to draw a horizon line or rather I want to have a horizon position for now let's place this one at a position of 500 and let me explain what we are going to do once again we have all of the level at the moment we're filling all of this with the background color which is a decent start but not actually what I want Instead This background color should only cover the top halfish while the bottom part should be water with some kind of horizon line between the two The Horizon position we are specifying is going to be the position of this Center Line that separates the sky from the water or in other words it is going to be the distance from the top to when we are starting with the water and I think 500 might be a little bit much let's go with 400 so we can see it a bit better what we can do with that number is for example we can create a c rectangle which is going to be simply a pame do F rectangle I want to speci ify the left the top the width and the height for this thing the left is really easy this one should always be at position zero and the top should be the Horizon position the width should be the window width and the height should be the window height minus the Horizon position I suppose what is important to explain for this one is that this C rectangle is always going to cover the same bottom part of the window and it is not going to move along with the level we basically put a blue rectangle in the background and that covers the water that's really all we need once we have that I simply want to py game. draw. rectangle on self. display surface with a specific color which I want to paste in we want to have the hex code 92 A9 CE after we have that I want to have the C rectangle rectangle and now if I run main. Pi in the background we have the blue color however if I jump this one moves along with the player which is going to look really really strange the reason for that is that this rectangle completely ignores the camera none of this affects the rectangle so we're moving the entirety of the level but this background is always constant but we can fix that fairly easily because the Horizon position can be affected by the offset all that we really want to do is add self. offset doy to it and then we are good to go if I now run may. Pi this thing doesn't change anymore or in other words it moves along with the rest of the level which feels significantly better also at this point this number shouldn't be set in the code it should be set inside of tiled fortunately it is because in there we have the horizon line this is also what I want to import that is fairly easily done I want to add one more named argument the horizon line the value for that one is going to be our TMX level properties and in there we have the horizon uncore line after that inside of the groups I want to have another argument the horizon line which we want to turn into an attribute right away self. horizon line is going to be the horizon line and finally we can replace the 400 with self. horizon line and now I can run m.p and we get the position we have specified inside of tiled that's a good start and the game does look significantly better but it's still not perfect we are missing clouds although first of all I want to accentuate the Horizon a bit more meaning I want to create a couple of Horizon Lines or rather we actually only need a single one which I can get with pame DOT draw doline I want to draw on self. display surface for the color once again I have a default value which is going to be this one F5 F1 de finally we need a start value an end value and a line thickness the start value is going to be zero for x and the Horizon position for the Y value for the end we want to have the window whiffed and then once again the Horizon position for y finally for the thickness let's go with four and now if I run main. Pi you can see we have a white line separating the Sea and the sky which is the Horizon that looks pretty good meaning now I can minimize draw Sky we are not going to need it anymore now for the clouds we are going to have two separate systems one for the small cloud and one for the large one if this is going to be our level for the small clouds we are simply going to create a bunch of Sprites that start all the way on the right side something like this and these are always going to be clouds which are going to move to the left and once they hit the left border of the level we are going to destroy them however for the large Cloud we have one large image and this one is tilable which means we can have one large Cloud that looks something like this and then another large Cloud that looks something like this and all of those large clouds we want to move by the same speed in the same direction in fact here's the graphics folder and the small clouds are very simple images they don't really do very much however if we are going to the big cloud you can see that this is a large Cloud that has a left Edge and the right Edge and we can put large clouds right next to each other that way we are basically going to create an infinitely long Cloud to get started let's create some attributes for the large Cloud that's the system I want to start with we are going to have have self. large Cloud speed which I want to set to 50 and then I also want to create self. large Cloud X which by default is going to be zero finally we will need one more parameter and that are called self. large Cloud tiles the way you want to think about this one is once more we have the entirety of the level but the large Cloud can only occupy a small part of that let's say something like this will be occupied by the cloud but we want to cover the entire width of the level with a cloud we have to know how many large clouds we need in this example we are going to need three large clouds and then once we have these three large clouds we want to move all of them in the same direction by the same speed that way they always stick together and it looks like one coherent Cloud the value for this one has to be an integer and we basically want to get self. wift so the level Whi we have specified earlier this we want to divide by self. large Cloud the one we imported earlier although from that we want to get the WID if I print that number self. large Cloud piles and run main. Pi we get at the moment we only need a single Cloud to fill the entire level background and that should make sense because if I open the file and look at the dimensions this thing is 1,792 pixels wide it is quite a large image whereas our entire level is 40 * 64 pixels so 2560 pixels the large clouds at the moment would mostly cover the entire window but once again since the in function is going to round down we are always getting the number that is slightly too small to fill the entire window but that we can fix quite easily by adding plus one and in my case I have added plus two just to have a bit of extra space at the end to make sure that we are not cutting off the main Cloud because that would look quite weird but anyway with that we have all we need to display the large Cloud we want to create another method Define draw large cloud for the parameters we will need self and Delta time the reason why we need Delta time is we want to move self. large Cloud X and increase it by self. Cloud large Cloud speed multiplied with Delta time now at the moment this would make the number larger I.E we would be moving to the right but the direction we do want to control I suppose for all of that we can create a self do Cloud uncore Direction which should be Nega -1 by default and this one we want to have both for the large and for the small Cloud so I'm going to place it all the way at the top now for that I want to have self. Cloud diction multiplied with the cloud speed and Delta time that way we are moving the large Cloud X number and then I want to run a for Loop for cloud in range self. large cloud files we want to draw as many large clouds as we have tiles meaning we want to run self. display surface and then blit the value we always want to place self. large Cloud now the position is going to get a bit more complicated so let's store it inside of a separate XY variable we need X and Y or rather I think what is going to make a bit more sense we want to place the left side and the top side of the cloud and the top part is going to be easier so let's define that and the left side for now is going to be zero as a matter of fact let's add zero for both of them and then call self. draw large cloud with Delta time if I now run main. Pi I have realized that we don't have Delta time inside of this draw method that fortunately is quite easily fixable we need Delta time when we are calling the draw method and then inside of the level we want to pass in Delta time as well if I now run main. Pi we can see we have the large clouds in the background and once again they are moving along with the camera or rather they don't move along with the camera so they're just static in the background and don't do anything but at the very least we have something so next up we can refine these numbers because for that I want to get self. horizon line the number we have specified earlier but this by itself is not going to be enough if they run may not P now now the clouds you can see them all the way at the bottom so if you look at the bottom right there you can see one part of the cloud remember once again we are specifying the top part of the clouds and the horizon line is the top of the water meaning we want to place the bottom of the clouds on this top position to get that we want to specify one more attribute and that is going to be large Cloud he and while we are here we can also specify large Cloud width both of those numbers we're getting from self. large cloud and then get underscore size we want to placed the top of the cloud on the horizon line minus self. large Cloud height and also these numbers should be inverted we are getting the width first and then we getting the height that's a silly mistake to make if I now run main. Pi we are getting the clouds kind of in the right position the one issue we have now is that they do not update with the camera but that we can fix fairly easy easily all we need is self. offset doy and now we have the clouds exactly in the right position that feels much nicer although this only covers one AIS we also need the left side for that one we will need self. large Cloud X and then plus self. large Cloud WID m multiplied by the number of clouds that we have that's the number we are getting from the four Loop to all of that we want to add self. offset dox and now if made. Pi the background clouds are moving that looks pretty good and also if I go further to the right you can see all of them and now we have to wait a little bit because the system isn't perfect at some point the clouds are simply going to to stop there we go since we only created free clouds at some point the clouds simply stop and we can see how this entire system is going to fall apart for that we want to add one more condition we basically want to check if self. large Cloud X is smaller or equal to the negative large Cloud width then we want to set self. large Cloud X back to zero essentially if this is the level again we are setting one point a bit further to the left this is going to be the width of one cloud and as soon as we have one Cloud reaching this point all the way to the left then we are restarting all of the clouds in the origin position and since this cut of point is the width of one of the clouds this should be seamless although that's kind of hard to demonstrate I suppose what I could be doing is I can set the large Cloud speed to 500 and now if I go to a point all the way to the right we can see the clouds pretty well and you should not be able to see any kind of transition marks and I think that is looking pretty solid cool although the cloud speed should just be 50 with that we are drawing the large clouds next up we can work on the small clouds so small clouds I want to work inside of Sprites again because there's one more class that we have to create all the way at the bottom I want to create a cloud which is just going to be a Sprite This Cloud I then want to import groups. Pi so after Sprite I want to have a cloud and then for the small clouds I want to do two things number one I want to create a timer that creates a cloud every 2.5 seconds this should have a random speed and always go to the left on top of that there should be lots of clouds by the default so when you start up the level there should already be a certain number of clouds this part is going to be your exercise meaning for the exercise I want you guys to create the cloud class and the timer by default when the level is starting there should already be a whole bunch of clouds on the screen simply choose a number that you think looks good on top of that the timer should create a new Cloud every 2.5 seconds pause the video now and see if we can figure this one out I suppose we can get started with the cloud class first of all we will need a Dunder init method for that we will need self a position a surface the groups is z argument although the default value for this one could be Z layers and we have a clouds layer if you look at settings there's clouds after that we have to call Super Dunder init where we have to pass in the position the surface the groups and the Z argument after that I want to specify self. speed which is going to be a random integer and this Rand int function we don't have right now so from random import Rand int I went with a number between 50 and 120 on top of that I want to have a self. Direction which is going to be -1 after that I want to have an update method with self and Delta time all we want to do in here is self. Rec dox plus equal self. direction multiplied with self. speed and multiply it with Delta time on top of that while we are here we want to check if self. re. right is smaller or equal to zero if that is the case we want to destroy the Sprite the logic for this one is if the cloud reaches too far to the left then we want to get rid of it if we didn't do that at some point we would keep on spawning clouds and that eventually might cause a performance problem it's not strictly necessary but it's generally good practice anyway with that we have the cloud so next up inside of groups we are already importing the cloud which means once we are starting all of this we want to create a couple of clouds and let's say for cloud in range 20 all we want in there is to create one instance of a cloud for that we will need position surface and groups groups is the easier part this one is simply going to be self besides that though we will need a position and we will need a surface let's start with the surface for that we want to use the choice method because we have a list of small clouds those we want to pick from self. small clouds although I think at the moment we don't have the choice method meaning from random I want to import choice that is going to give us the surface finally we will need a position and for that once again I want to get a random number so we want to import Rand in now in the position we want to specify Y X and Y and let me draw the issue we have here this is going to be our level and on the bottom we have all of the water I want the clouds to only be inside of this area on top of that when we are creating the clouds at the start of the game the clouds should be somewhere between this left side and this right side meaning we have two constraints let's start with x I want to have a random integer between zero and self. WID for y I want to have a number between self do borders and the top of the level up to self. horizon line that should be all we need although we want to get rent in there now let's try all of this and there we go we have a whole bunch of clouds 20 might be a bit much also we're not generating any new clouds so anything that comes afterwards looks a bit weird but at least we have a start however let me run this again and you can see we have this huge gap all the way at the top here the reason why we are getting that is when we are creating a cloud we are always placing the top left inside of the position we are going from the top all the way to the horizon line placing the top left isn't ideal because we might cross this this horizon line which we really shouldn't do to fix that when we are creating the cloud I want to update self. wct I want to set the mid bottom to the position that's going to make quite a difference and now we are using the space much better and I think that looks quite nice cool this is going to cover the default case next up we want to have a timer that creates a new Cloud every 2.5 seconds for that we will need the timer class from timer import timer and after that for the clouds we want to have a self. cloud timer the duration for this one should be 2,500 however I want to add more arguments in there because remember the timer can run a function once it runs out and it can be repeated for getet that we want to first of all specify a function we want to run self. create Cloud make sure to not call it after that we want to set a third value to true and just to demonstrate if we are opening the timer class we can set a duration a function and repeat we didn't need that earlier because we have default values for these parameters also for all of this to work I want to activate the cloud timer right away so activate also inside of the draw method before we are doing anything else I want to get the cloud timer and update it all we need now is another function that's going to be called create out no need for arguments in this one all we really want to do is create one instance of a cloud this surface we can also copy because this one is going to stay the same as a matter of fact let me copy the position as well because we can keep parts of that the Y part to be more specific this one doesn't need to change however the X part does need to change because we want to now create a cloud all the way on the right side of the window meaning we want to get a random number from the width of the level up to let's say self. wift do 200 pixels just to get some random numbers in there and with that I should be good to go although I am seeing that I messed this one up this should be equal to timer with these numbers now let's try may. pi and we do have some clouds if I go all the way to the right we should be getting more clouds continuously right now the clouds might be behind the large Cloud so we do have to wait a bit there are some more clouds and there you can see we are creating more clouds and this happens a bit too far to the left let's move the value a bit further to the right but other than that this works pretty well so basically when we are creating this random value we want to get self. with let's say plus 500 and then up to 600 and now the second attempt now this should look much better now we keep on getting clouds that come in from the right I think this looks about right I think the timer is a bit too slow but that's something you can fine tune it's not too important and something fairly subjective either way this function or Rob by this method is working just fine and with that we have finished the level if I now run all of this we have all of the important parts and we can jump around we have a background we have enemies we have platforms this is actually looking really good and everything else still works just fine on top of that what you can do at this point is when we are creating the TMX Maps so on this line where we import om. TMX you can import other files for example if we import 1. TMX run all of this again then we get a completely different level but everything else still works just fine so let me try to attack an enemy and we have Pearl all of that looks really good besides that we have two TMX and when we are running this one I realized there is an error that all Sprites object has no attribute Cloud timer let's have a look at that really quick if we're looking at groups and the issue right now is we are only creating a cloud timer if we have a sky however if we don't have a cloud timer attribute further down there we are trying to update a cloud timer that doesn't exist fortunately that's easy to fix we want to place this Cloud timer and only update it if there's a sky if I now run main. Pi we can see the whole level and that feels quite a bit better and there we have a whole another level that once again works perfectly fine later on we are going to combine all of the different levels although for now I will stick with Omni TMX but play around and if you look at til you could design your own levels as well the way I would recommend you to approach that so here we are in om. TMX and if you want to create your own level you basically save this file under save as and then you save it as whatever level you want for example if you want to have a new level then you could add level six save it and now you can customize this thing to your heart's content whatever you want to put in there you can also resize the map under map and resize map and then just put in whatever you like with that set up you can reuse all of the layers and simply rearrange stuff which I think is the easiest way to create your own levels but anyway this covers the entirety of the level the last thing that we have to work on is going to be the Overworld that's going to come for the next part to finish up this tutorial we have to create the Overworld and this is going to connect all of the various levels on top of that we want this thing to be somewhat Dynamic meaning we only want to display some of the paths the ones that have already been unlocked other than that we have to create a movement system and an input system but none of that is going to get too complicated first of all though let's get started by creating the basic outline of of the Overworld we are back inside of the code and I want to create a new python file that I'm going to save as Overworld dopy there we're going to store all of the Overworld logic first of all we need from settings and import everything after that I want to create a class called over world no need for inheritance on this one but we will as always need a Dunder init method for that we will need self we will need a TMX map we will need access to our data class and finally we will need to have some let's call them Overworld uncore frames once again for the Overworld we're going to import all of the graphics inside of the game and then pass them into the Overworld class first of all I want to create self. display uncore surface which we get with py game. display. getor surface also data should become an attribute so self. data is data after that I want to create a couple of groups later on although for now I simply want to have self. allore Sprites which at the moment is going to be a py game. sprite. group via this all Sprites we are going to draw the entire level and later on we're going to customize this even more to create a camera once again first of all I want to create a self. setup method into which we are going to pass the TMX map and the Overworld frames now this method doesn't exist right now so set up with self TMX map and the Overworld frames for now just to make sure this is working let's add a pass in here and then inside of main. P create the basic Overworld first of all for that we want from Overworld import the Overworld class and then inside of the Thunder init method we want to create one instance of this Overworld and at the moment our current stage is going to be the level but that is not what we want anymore I suppose for now I could simply comment it out and then get self. current stage and our current stage should be one instance of the Overworld class for that we're going to need a couple of arguments and let me copy all of the parameters first of all we will need a TMX map and that we don't have at the moment we only have the TMX maps for the levels but that we can fix quite easily I want to have self. TMX over World which we can import via the method we have already used for the level let me copy it and basically now if I once again open the project folder I can go to data and in there we have an Overworld folder which only contains a single file that is what we want to import meaning we want to go to data we want to go to over world and then we want to import over world. TMX and in case you are wondering this is what this TMX map looks like most importantly for now what we're going to work about are the two tile layers where we are going to import all of the background and then we have a couple of objects like the grass the rocks and the palm trees for now that's all we are going to need later on though we are going to have paths and nodes nodes are going to be the pathways to a level for example if I select this one you can see in the attributes we have two we are going to stage one on this note and we can only go down down and if we do that we are going to stage one which is going to be this one and from this point we can go left right and up and if we enter the level we are getting to stage one I will explain the system in much more detail later on for now you don't really have to worry about it the only thing that we are going to need is main top and objects first of all though we want to pass TMX Overworld into the Overworld class after that we need data and that is quite easy self. data finally we will need some Overworld frames which we are getting from import assets and now I can minimize everything else and then create self. over World frames which once again is going to be a dictionary for now I want to import two things in there the palms and the water background both of which are just a folder which are going to give us an animation nothing fancy here this is what we want to import so I want to import self. Overworld frames with that we have everything we need so next up I can minimize import assets and then we have to look at the run method because at the moment we are trying to run the well run method on the current stage but the Overworld doesn't have such a thing although we can fix that fairly easily we want to Define a run method with self and Delta time although for now pass and there is going to be enough if I now run all of this we can see that well we can't really see anything but that is the intended experience because inside of Overworld we are not drawing anything yet for that inside of the setup method I first of all want to get all of the tiles which means I want for layer in and this is going to be the the main layer and I want to have the top layer I should actually demonstrate at the moment the only thing that we care about is Main and top and if I zoom out main is basically the main part of the islands and top is stuff we always want to have on top of the main layer because of that it's quite important that we first call Main and then top because of that the top layers will be created after the main layer hence they're going to be on top if we didn't do that the entire thing would look a bit weird but anyway after that we can do for x y and the surface in TMX map and then once again get layer by name the layer we want to look at is the layer we are getting from the parent for Loop also don't forget we are now working with tiles so you want to add Do tiles at the end after that we simply want to create a Sprite this Sprite that we already created inside of Sprites this one to make that work I want from Sprites and import the Sprite class after that once again we will need a whole bunch of arguments the position we have already covered a couple of times we want X and Y however be careful this X and Y is a grid position whereas for the Sprite we want to have a pixel position meaning we want to multiply of those with the tile underscore size the surface we already have that we're getting from the for Loop groups is also easy we only have a single one self do all Sprites finally for the Z layers I want to have zore layers and then inside of settings I want to place all of those tiles on BG tiles let's paste it in there and then we should be good to go next up we have to call self. all Sprites do update with Delta time and self. all Sprites do draw on self. display surface now if we are running this we can somewhat see what's going on at the very least something is going to work since we don't have a camera at the moment this isn't going to work perfectly yet next up we can work on the watch for that one we want to fill the entire map with an animated Sprite that is going to display the water the one we have just imported if I find it really quick inside of import assets this water is a sprite animation which we want to play and for that I want for call in range TMX map. width after that for Row in range PMX map do height finally we can create an animated Sprite once again that we have already created inside of the Sprites class so I want to have this one with all of those arguments although I don't need the animation speed which means inside of the Overworld I want to import the animated Sprite and then pass in these arguments X and Y we can simply copy from the Sprite we have already created this one isn't going to change except X is going to be the column and Y is going to be the row RS we are going to get from Overworld frames and in there we want to get the water once again the one we have just imported groups is easy self do all Sprites and finally Z I can copy the Z layers from the Sprite although we do have to change the key I want the water to be in the background so BG layer if I'm running all of this we are getting something so we definitely have water but we cannot see the level anymore and you might have spotted already why at the moment we simply have a Sprite group we don't sort all of these Sprites by the Z layer as a consequence since we are creating the water after we are creating the other layers the water is going to be on top hence in of groups we want to create another Sprite group I want to minimize the all Sprites and then create a class called I called them World Sprites which once again has to be pame dos sprite. group for the inheritance we want to create a thunder in it method with self and we also want to pass the data in there I guess we can do it right away also we have to make sure that we are calling super thunder in nit although without any arguments also Al while we are here self. display surface is going to be pame do display. getor surface self. data should be the data none of that is too important what we rather want to do is update the draw method with self and I suppose for now we can also set a Target position very similar compared to what we have done inside of all Sprites all the way down here in fact while we are here I can copy the offset part s because those are going to stay the same and then in the dunder init method self. offset is going to be a basic Vector later on for the camera this part is going to become really important although for now we are only going to use the camera to show a bit more of the level what is much more important I want for sprite in sorted and then look at self then use a key to sort all of the Sprite via the Z position for that I will need a Lambda function with one parameter the Sprite all I want to return is Sprite do Z after that I want self. display surface. blit sprite. image and sprite. wct that should be a good start if I now go back to the Overworld and import from groups I call this one the world Sprites and then use these word Sprites as the group and while we are doing that we also have to pass in the data also we now cannot use the display surface inside of the draw method anymore instead we will need a position let's for now go with zero and zero now I can finally run M pi and now we can see the level on top of the water on top of that what I can do now inside of the groups I can use the offset to draw all of this with a well offset this is going to work exactly like it worked inside of all Sprites I want to create an offset position which is going to be sprite. rec. toop left plus self. offset although truth be told we could combine these two lines fairly easily all I really want to do is get the top left of the current Sprite rectangle and then add the offset we don't really need a whole line of code just for that but now if on m.p we are offsetting the entire thing although not in the right way let's say inside of all Sprites do draw I don't want to have zero and zero I want 1,000 and 800 now let's try. pi and that feels significantly better later on when we have a player this system can become more flexible but for now I'm quite happy with this next up then we can start working on the objects there have three objects that we have to import so if I look at the objects we have a whole bunch of pal trees we have some grass objects and finally we have a couple of rocks in the background all of those I want to import and if you look at their names we have an object called grass for the grass then we have a palm tree and finally we have somewhere in there is Stone as well importing all of that is going to be your exercise like we have done before I want you guys to import The Palms the rocks and the grass Sprites however you have to be aware that the Palms are animated meaning you have to get the frames for this animation instead of the surface that you're getting from til but try to figure this one out on your own and display everything pause the video now and see how far you get first of all we will need four obj in TMX map get layer by name the layer that we want to look at is called objects in there we basically have two options we have a palm tree which is going to be animated that one we can isolate with obj do name is equal to home if that is the case we want to create an animated Sprite but that we can do later the other case so else is going to be grass or stone neither of those are going to be animated meaning for them we can create a simple Sprite where we will need a position a surface groups and a z layer which is going to be the same for both of them the position will be obj dox and obj doy the surface is going to be obj do image groups is going to be self do Sprites although I just realized the Z layer has to be different for the two of them but that we can fix quite easily I want Z to be there Z layers in any case but then for the string I want to create an F string where we are adding a single value that value is going to be BG details but only if obj do name is equal to Grass if that is not the case else we want to be on the BG tiles layer that should be all we need if I now run may not Pi we have the grass and all of the Rocks now when it comes to the layers you will have to play around with what looks good it took me quite a bit of experimenting to see what worked best next up we want to work on the palms for that we will need an animated Sprite with a position frames the grops and a z layer the position is the easiest part that's the thing we have already seen a couple of times groups is also easy because we only have one self. all Sprites what is that layers I want that layers and the palm trees should be on the main layer finally we will need the frames and those we already have because inside of main. Pi in Overworld frames we have a palm key meaning for that I can basically copy what I've done for the water we want to get Overworld frames but not the water we want to have the Palm after that I can run main pi and we have a whole bunch of palm trees that looks pretty good although I want their animation speed to be a bit more randomized which we can do quite easily because there is an animation speed attribute inside of animated Sprite for that one I want to have a random integer let's say between four and six for that though we will need to from random import Rand in after that if I run main. P all of the palm trees are animating a bit more randomly which I think looks much better and with that we have set up the entirety of the Overworld wasn't actually so bad and the main thing that you basically have to learn about pame is that you always want to create a basic setup with Sprites and animated Sprites and whatever basic objects you need once you have all of those in place though it becomes really easy to set up even more complex levels next up then I want to minimize all the stuff we don't need anymore I want to create I call those the nodes and the layer basically a node is going to be one of these circles that are going to lead the player to a level all of these nodes are then connected by a path that we are going to create later for now it's not too important what I want to start with is create all of the notes and then place a player on one of them I will need for obj in TMX map. getet layer by name the layer we now want to look at is called notes and basically we are importing all of this where we have an object in a certain position and this one has a couple of attributes first of all in there I want to create create all of the nodes which I'm getting via an if statement objname is equal to node if that is the case I want to create what I called a node class which is going to be a Sprite meaning we want to work once again inside of the Sprites and let me collapse everything because we want to create a new class called note which needs to inherit from pame dos sprite. Sprite after that we will need a Dunder in nich method with self we want to specify a couple of things now first of all a position and a surface along with the groups after that we have to call Super Thunder init with the groups next up we want to set self. image to the surface we are getting and we want to get a self. rectangle now for this rectangle I want to get self. image . getet F rectangle but now we want to place the center and for the position let me zoom in when we are getting the position from tiled we always get the top left but we want to place a circle that's going to look something like this which should be around the center point of the tile we are importing meaning from this top left we have to go to the right and down by half the width and height of this cell which means I want to have a tupal and for X we want to have position with zero plus the tile size divid by two the same thing we want to do for y with the one difference that this should be position one after that we will need one more thing and that is self. z which we can get from Z layers and I want all of this to be on the path layer which I don't think we have used yet but it's this one righty with that we are getting a node and that we want to use inside of the Overworld for that we have to import the node and then add in all of the arguments let me copy them position surface and groups position is going to be obj dox and obj doy inside of a tubal surface we don't have yet that's going to come in a second but groups I want to have self. all Sprites so next up we will need surfaces for that I want to look inside of main.py because all of the Imports happen inside of Overworld frames what I want to import in there is going to be a larger file we want to import a folder as a dictionary from Graphics Overworld and path let's have a look at that one here we have the project folder and we want to go to Graphics in there we have an Overworld and then we have a path now all of this might look a bit weird but those are later on going to become the path the only thing that we care about for now is going to be the node to reach that one we want to get the Overworld frames in there we have a path and then inside of that dictionary we are getting a Noe file that should be it if I now run may not Pi we can see all of the noes that looks Prett pretty good however I want to pass a bit more information into them and for that let me use named arguments we want to have a position we want to have I believe I called this one the surface yep surface and finally we will need the groups after that I want to import let's call this one the level which we can get from obj do properties on that thing we have a stage attribute once again instead of tiled we are currently importing this attribute for example for the first level the one we are starting on this one this is going to be zero for the next one it is going to be one after that we have two and so on on top of that we want to pass in the data which we get with self. data after that inside of the Sprites and also by the way we don't need to play anymore at least for now so let me close it that way we have a bit more space I want to get the level and the data both of which will need to be an attribute self. level is going to be the level and self. data is going to be the data now this by itself isn't too useful however what we can do now if I open the data class we can add one more parameter let's call it unlocked level which we can set to zero at least for now after that inside of the groups when we are drawing all of the Sprites I want to check if we are on the path layer which I can check with if sprite. Z is equal to zcore layers and we want to check if we are on the path layer if that is the case we want to to do something else however else we simply want to draw all of the Sprites however if we are on the path layer which means for now we are looking at all of the nodes but we don't want to draw all of them we only want to draw the ones with the level that is below our currently unlocked level below or equal to I should say which means I want to check if sprite. level is smaller or equal to self. data Dot and now we can use the parameter we have just created unlocked level only if that is the case do we want to blit this particular node but then the blit method is the one we have already used we can simply copy it and now if I run m. Pi we can only see the first node the one with level zero however if we increase the un loock level to let's say three then we can see more of the notes later on we are going to change this number depending on what we are doing inside of the level for now though it's a good way to test if the entire Overworld system is going to work after we have that I want to create the player icon which is going to be another Sprite let's create it here class I call this one an icon which has to inherit from pame dos sprite. Sprite for that one we will need a thunder init method with self a position groups and Frames I want to call Super Dunder init with the groups after that I want to create an image and I want to create a rectangle for the image we want to get self. frames and self. frame index the value for those is going to be frames and zero we have seen this a couple of times by now also we will need self. state which by default is going to be idle after we have follow of that the image we are going to get with self. image is going to be self. frames with self. State and then we getting a list of surfaces from which we want to pick via self. frame index and I suppose I'm jumping ahead a little bit first of all what we want to do if we go to the Overworld folder there we have a folder called icon and there we have a bunch of States if we look at the idle one we can see this is the player standing Idol if we look at down we have a down walking animation and if you look at left or right we have a left or right walking animation all of this we first of all want to import and then animate through inside of the icon class should have gone step by step here so let's start with step number one we want to import all of these surfaces which we can do with a another key called icon and then import subfolders we want to go a folder up Graphics Overworld and icon that's all we need after that we are going to create a player inside of the for Loop where we are going to create the nodes simply because when the Overworld is being created we want to have the player on one of those nodes depending on what level is currently unlocked so we want to have another entry for the player I want to check if obj do name is equal to node and on top of that I want to check what the current level is that we don't know at the moment actually because inside of data we only store what the currently unlocked level is but on top of that we will need a self. current uncore level for now let's set this one to Z after that once we have a note I can check what the stage of this note is that I am once again getting from obj do properties and Stage I can actually just copy this one I want to check if the current stage of the node is equal to self. dat. current uncore level if that is the case we know we are on a node and the level of this node is equal to our current level which means the player should be on this Noe hence we want to create an icon if that is the case which we also want to store inside of an attribute self. icon is going to be icon then next up inside of the Sprites we have to get the parameters for the position I want to get obj dox and obj doy however this would once again be the top left but I want to place the player right in the center of this and for that I want to add tile size divided by two groups is going to be easy self dot all Sprites and the frames are going to be the Overworld frames for the icon after we have all of that at the top we also shouldn't forget to import the icon and now I can run m.p and we're getting an error that icon has no attribute Z that makes sense because inside of the icon we will need a z parameter and we will need a rectangle self. Z is easy it's simply going to be Z layers and we want to be on the main layer after that we will need the rectangle that we can get with self. rect being self. image. getet F rectangle where we want to place the center to wherever the position is after that let's try main. pi and there we can see we have the player in roughly the right position on top of that inside of data if I set the current level to a one then we start on the first level or first level node and if I set it to a two then we are adding up there also now that we have an icon we can update the camera we don't want to have a static position we want this to be self. icon. rec. center now if I run all of this again we can see that the player is right in the center on the correct note however we have two problems number one is that the player is quite far down I would rather have the player a bit further roughly up here on top of that you can see that this palm tree is behind the player and that I really don't like it looks super weird to fix all of that we have to work inside of groups. Pi because in there we have to sort all of the Sprites again in another way I want to split the drawing logic into two parts where first of all we are drawing all of the background and wow that is spelled background and after we have done all of that we want to draw the main layer now the background we have mostly covered for that we can reuse the stuff we have already written although with one modification I want to check if Sprite doz is smaller than Z layers and Main only if that is the case do we want to do all of that if I now run may not Pi again we can only see all of the layers up to the main layer not including the main layer for that we will need another for Loop and the main for Loop we basically want to copy although the sorted method needs to be updated the way you want to think about it and to explain that let me comment it out and then set this to smaller or equal to the main layer because of that we can now see the main layer again but we still have the issue with the palm tree that we have to work on and the way to fix that kind of issue is to sort all of the Sprites via the Y position which means imagine that this palm tree at the moment has a wi position of 300 the Y position of the player on the center is going to be 350 and the Y position of this palm tree is going to be I don't know 400 these numbers we can get quite easily simply when we are creating the Sprite we can get the number from the center however then we want to use that number to order the Sprites meaning we want to Simply sort the Sprite via the white position and then this Sprite will be drawn first then we have this Sprite and then we have that Sprite which means that this Sprite since it was drawn last will be on top and implementing all of that logic is honestly not that difficult basically what we want to do for sprite in sort itself that is going to give us all of the Sprites but then when we have the Lambda function to sort it we do not want to get sprite. Z instead we want to get sprite. rec. Center y and that's all we need now we are sorting all of the Sprites via the Y position after that I want to check if Sprite dot Z is on top of the main layer also this needs to be that layers and Main if that is the case once again we can use the blit method like so if I now run all of this we can see that the player is behind the palm trees which is really all we needed in in my camera video I explain the logic for all of this in a bit more detail so check this one out if you are interested next one then we can work on the offset for the player so once again and let me place the player in an easier to see position current level zero that should bring the player in a bit more of an open field so that looks good but I want the player to be a bit further up so that for example the player would be something like this at the moment it just looks like the player is too far down to work on that we could work inside of the Sprites but in there this would be kind of awkward because we have the image and we have the rectangle which is going to give us the entire Sprite and we don't really want to mess with those what is going to be much easier is to give this icon some kind of attribute to identify it self. icon and set this one to True with that inside of the groups we can identify the Sprite if has attribute I want to check if the Sprite has an attribute called icon which only the icon is going to have if that is the case I want to do something however if that is not the case I want to draw all of the other Sprites however if that is the case I still want to use the blit method but then I also want to add an extra offset to it which we do by adding another Vector with zero the number in my case is -28 if I now run main. Pi the player is really nicely in the middle of the node although inside of the icon class we haven't made any change simply inside of the camera draw logic whenever we have this icon we always drawing it a little bit higher which just looks a whole lot nicer cool and with that we have the whole setup for the level next up we can work on the paths in this part we are going to add the movement to the Overworld on top of that we're going to create a connection between the levels and the Overworld which means by the end of this part we basically have a finished game there are some minor bits we still have to add but those really aren't going to be any kind of problem let's get started with the movement first of all I want to start inside of til because in there we have a layer called paths and this is the only thing I want to look at for now so if you look at this you can see we have a whole bunch of triangles those are going to be our path the way you want to think about it and let me show the notes as well the notes we have already seen for example this note would get us to the first level or level zero inside of the game from this note I want to have a path that goes this way and this way and then finally ends on note for level one that is what the triangle is for effectively we're going to import the corner points of it those are then going to give us the path that the player will follow for all of that inside of the Overworld we want to work in the setup method and that has to happen before we are creating the nodes and the player you will see just in a second why but basically I want to have another section that I'm going to call paths and there first of all I want to create a for loop as we have done multiple times already for obj in TMX map get layer by name the layer we now want to get is called paths now these objects are going to work a little bit different compared to what we have already seen so for example we already have seen obj dox obj doy and things like that but something that I haven't used yet is obj do points if I print that one to show what we get I can run the code and then after second we are getting a whole bunch of points or rather we are always getting a tupal that contains points that we can look at those are the points that Define the shape that I have created and tiled I.E the points I have just talked about so for each of these objects we have these points here although these points I do want to store which I'm going to do inside of a PA list for that I am going to use list comprehension for now let's say I want to have P for p in obj do points with that we are essentially making a copy of the list but that's not exactly what I want to do instead I want to have a tupo with P do X and P doy what I should mention obj points is going to give us a list with Point objects and inside of each point object we have a couple of attributes like p.x or p.y if I am extracting all of that I can print the position and now we are getting a list with a couple of position two builds that's a good start but I want to have a little bit more if you look at tiled once again we have the node in the usual position and ideally I want the path to start in the center of that node but that is not what we have at the moment we always start in the top left of this note and this happens for all of these nodes if I zoom around you can see that the paths always start and end on the top left of all of these nodes that is for a reason because with that system I can always snap to the grid which makes my life much easier to arrange these points in pame however we do have to adjust for that which fortunately isn't too difficult all that we really want to do is to add the tile size and divide it by two that way we end up in the center of the cell also I really want to have integers for all of this after all of that I am getting a list with position T bus that start in the center of the node and then go to the center of the target position or Target node rather on top of that I want to capture a couple of properties two in particular one for the start of the path and and another for the end of the path both of which I can get via obj do properties because each path has two properties one for start and then another one for the end and as always if you look at tiled and select the path we have an end attribute and a start attribute this is what we are currently importing and after I have all of that I need to store these points more generally which I'm going to do via a dictionary that I'm going to call self. paths at the beginning it's simply going to be an empty dictionary but then inside of this for loop I want to get self. paths create a new key with the end of the path the value is going to be a another dictionary where we have the position which will be the current position for this path or the position points besides that I also want to have a start entry with this start level after we have all of that I can print self. paths and then run all of this again and now we are getting a dictionary with all of our paths to illustrate what is going on here let's have a look at this one entry we have a starting point on level zero and the end point is going to be level one the positions are going to be 672 and 800 then on the next point we are going to 672 and 980 meaning we are only going down after that we are going to 992 and to 928 the only value that changes is X meaning we are going to the right this is exactly the path to go from here to here after we have that we want to work inside of the nodes because in there we need to know what kind of paths are available from each node intitled I can explain all of this a bit better imagine we are on this starting node for stage Zero from this point we have to know in what direction we can go in and for that we have the other custom properties for example for this node there's only one direction down and if we do go down we go on path number one which is going to lead us to this end level if if we end up on this level we have quite a few more options we are still on stage one but then we can go up right or left if we go right we go to stage number three and if we go up we go to stage number two however if we go left we go on stage number one but in Reverse which means we end up back up here so these directions we now have to import and then get them into the node now for that I first of all want to clean up the data let's say I want to create let's call this one available paths for that I want to create a dictionary where we will need a key and a value so let's say 4K and value in obj dot properties and we want to look at all of the items let's print what we're getting from that so print available paths if I now run all of this we're getting a dictionary where we have down left down right at some point we also have up and that's a good start but we do not want to have something like this stage in there to get rid of that we only want to get the value if K is in the list with all of the directions so left right up or down if that is working then we only get the available directions that information we now have to pass into the node I suppose inside of the node we can simply call it paths and we want to assign it the available paths after that inside of the Sprites let me minimize everything because we at the moment only care about the nodes and there we want to have another parameter for the paths which we also want to store at as an attribute self. paths is going to be paths with that inside of each note we know which direction we are going to go which is then in turn going to become important for the icon because this is the object that will actually start moving the way that is going to work back inside of the Overworld we first of all want to get some kind of input that can be another method input inside of that method we want to check all of the Pressed keys so py game. Keys doget pressed for now the only thing that I want to check is one key input if keys and py game. Kore down if that is the case I want to move the player from stage zero to stage one or rather from the note for stage zero to the note of stage one to be a bit more precise to be able to check if this is the case we first of all have to know what node the player is on to achieve that I want to create another attribute if self and I call this one the current node we first of all want to check if the player even is on a node I.E the player could either be on a node or walking if the player is walking we shouldn't accept keyboard input at all anyway but then if we are on a current node we only want to accept keyboard input if we are pressing down and self. current node. cancore move with down so I hope the logic makes sense here we only want to move the player or allow keyboard input in the first place if the player is on a node and if the node allows the player to move down now for that first of all we are going to need a current node and then the current node needs to have have a mve down method let's start with the current node after we are running the setup method I want to get self. Curren node now to get the current node I simply want to get the node that the player is standing on but that is a bit of an issue right now because we don't have easy access to all of the nodes but that we can fix by adding another group besides self. all Sprites I want the notes also to be in self do node Sprites this group does not exist right now so all the way at the top I want to create self. no Sprites which is going to be py game. sprite. group after that I want to at the moment simply get the note with the level zero that I'm going to get with note for a note in self. node Sprites but only if node do level is equal to zero that is going to return me a list with one item from which I want to get the first index that way we get one Noe that has the right level and after that we don't need to setup method anymore we have to give the note a can move method and we are simply talking about these notes here so these notes need to have a cancore move method with self and a Direction all we really have to check is if the direction is in a list with self. paths and then we only really care about the keys on this one essentially paths is what we have created earlier when we have run the setup method with all of this where we have gotten the available paths those are stored inside of self. paths inside of the node and now when we are running can move we are passing in a direction to check if we for example can go up or left or right or in our case if we can go down if this down is inside of the keys of self. paths then we want to return through and now I have added a ton of code so let's actually check if all of this is working I want to print can and move down also we have to call the input method which I want to do all the way at the beginning of the run method self. input let's try main. pi and we're getting an error that this should be py game. key. getet pressed next attempt the game doesn't crash and now if I press down we get the message can move down however if I'm pressing any other a key we don't get anything else so that looks pretty solid and I do realize at this point the logic does get a bit more advanced but ultimately all that you really have to understand is that when we are running the dunder init method we are inside of the setup method create a whole bunch of nodes that happens down here each of these nodes is going to get available paths and then the value is going to be the relevant path that we can walk on all of that we are then passing into the node and then inside of the node we are checking via the can move method once the player gets input if this direction is indeed inside of this dictionary if that is the case [Music] then we at the moment simply print can move down but obviously we don't just want to print something we want to make the player move which we can do via self. move with the direction in this case down that method doesn't exist right now so defined move with self and a Direction first of all in there I want to get a path key which is going to be our self. current node and on there I want to get all of the paths we once again want to get the direction we have just used and since we have checked if we can move in this direction we know that this direction does exist inside of the paths let me print what we are getting print path key and now I run main. pi and I press down we're getting one however at this point you really want to be careful because if I show the type so what kind of data type we have for this variable we are going to get class string we are not getting an integer what we are getting is a string that happens to look like an integer but it's ultimately still a string so if you look at tiled once again and one of the nodes and we could look at any of these left right or up custom properties all of these are strings and you can see this in particular for this one for left we have one r that means we want to go on path one in Reverse for that system to work I have set all of these custom properties to be strings and to make sure this doesn't cause a problem down the line I want to get the index zero and turn all of this into an integer that way we are still getting the same result so if I press we are now getting an integer although if we don't show the type we should now still see just a one and there we go now we get the right path however we also want to know if we're going to reverse the path or not for that I have another local variable pathore reverse this by default is going to be true but only if self. current node. paths with the current direction and then we always want to look at the last entry inside of the string meaning -1 if that value is equal to the string R then we want to reverse the direction I.E this one should be true however if that is not the case the value should be false with that we know the key of the path we want to get and if that path should be reversed after that we can finally get the actual path I want to finally get self. paths the one we have created earlier all of this we can identify all of the paths simply by their end key which means self. paths is going to get an indexing with path P although from all of that we are going to get a dictionary from which we only really want to get the position on top of that I want to get all of the points but only if not have reverse if however we do want to reverse the path then there should be an El statement where we we are getting all of that except we are reversing all of that which we can do with semicolon semicolon and ne1 that's the easiest way to reverse a list in Python and with that we are getting a path let me print the path and run main. Pi if I now press down we are getting a list of values I want a player to go to this point once we are reaching that point the player should go to this point and once we're reaching that point the player should go to that point and once we are reaching that point we should be on the next node for all of that I want to work inside of the icon which means I want to get self. icon and then call a start move method with the path next up then inside of the Sprites I want to work inside of the icon at the moment we if we are going to run the code are going to get an error message because we do not have a can move method with self and a path but now if we are going to start moving I want to first of all update the rectangle center of the player to the first position of the path meaning path and zero after that I want to store the path itself although for that I want to create an attribute let's call it self. path and we can put that all the way at the top that feels a bit better organized by default this value is going to be none however once the player starts moving then I want to set self. path to the path we are getting via the parameters but we don't want all of the path because remember if we are getting three points for example with this being our starting point this being the corner point and this being the Final Destination the starting point we don't really need anymore because this is where the player starts so from that point we simply want to move down and we don't really need this point in the first place at least after we have set the center of the player to that point as a consequence when we are setting the path we only want to get from index one all the way to the end after we have that I want to run a method called self. find under score path and that might sound a bit weird but let me explain once again imagine we have three points that we want to move along with the player starting on this point by default the player has no idea in what direction to move in that is what fine path is for it is going to tell the player icon in which direction to move so in this case we want to move down we figure that part out let's create the method I want Define find path no need for custom parameters in that one and first of all I only want to run logic in there if we have a path inside of the icon after that I want to check if self. re. Center X is equal to self. path and then I want to get the path with zero and zero to visualize what that means let me print self do path once we're doing all of that also let me add pass in there just so we don't get an error if I now run m.p and I press down we are getting an error that the icon object has no attributes St to move ah that is because I call this method can move instead of start move let's try this again I can press down and now we are getting a list with points in the path and what we are doing at the moment is we are checking the current horizontal center of the player against self. path with zero so we getting the first entry and then in there we are checking the x value the one with the index zero I.E the 672 that would be the exposition of the next node and to understand that once again imagine we have our three points at the moment we want to check which which direction the player can go in and one way to figure that out is if the current position of the player and the next node have the same Exposition then we know they are on the same line which means that the player can either go up or down that part we don't know yet but at least we know that we can move on the vertical axis in there we can set self. Direction which is going to be a vector that needs X and Y X is always going to be zero because remember we can now only move up or down hence the direction for X should not do anything and Y should be one but only if self. path with zero and now one so we are checking the Y position of the first point in the path if that point is great greater than self. rect do Center y then we know that the first point in the path is below the player so the player should move down however if that is not the case so else then we should be moving up and after we have that I can run the else statement and in there we are going to move on the horizontal axis the logic in there is going to be reasonably similar so let me copy all of this but now we only want to move on the horizontal so y should be zero but X should be one but only if self. path Z and zero is greater than self. re. Center X I.E the next point in the path is to the right of the player then we should go to the right if that is not the case else we should be going to the left that is going to give us the direction the player should be moving in also just as a safeguard if we do not have a path then I want to run an El statement where self. direction is going to be a vector without any movement so the values for this one should be zero and zero which we're getting by default also at this point we should specify a self. direction Vector in the init method we should always have a direction even when we are just creating the class cool but with that we can find the direction which by the way might have been a better name for this function but doesn't really matter after we have it though I can minimize it because now we can run the update method with self and Delta time essentially all that we want to do is if self. path so if the icon currently has a path then we want to set self. re. Center by self. Direct Direction multiplied with some kind of speed let's say self. speed multiplied with Delta time now speed we don't have yet for that self. speed is going to be I went with 400 and now inside of main. Pi if I press down the player moves down although the player never stops moving down and eventually this gets kind of silly but at the very least we do have a start the issue at the moment is is that the player doesn't respect the corner we are simply running fine path once and the player will always move in that direction so at the moment we have the starting position of the player the next point in the path and the final point in the path since the first point in the path was right below the player because of that the player kept on moving in that direction but we only want the player to move up to this point and then move to the right for that we will need another method it I call this one the point Collision without any custom parameters the logic for this one is going to be if self. direction is going to be one I.E at the moment we are moving down and on top of that we want to check and self dot rect do Center Y is greater or equal to self. path and then we want to get the next point in the path which is always going to be path zero on this point at the moment we only care about why so if the player is moving down and then exceeding the next y point in the path if that is the case we want to set self. re. Center y exactly to this point so self do path with zero and one and after that we want to delete self. path with zero and after that we want to run self. find path once again the way you want to think about that and I hope you're not entirely lost yet we once again have the three points and at the moment the player is moving down however at some point the player is going to exceed this point which is going to trigger this function because of that we are going to to set the player back to the center of this point that is this line after that we are deleting the point we are currently on so this point from our path list finally we are running find path again so we are checking the direction from this point to this point I.E the player should start moving to the right although to make sure that this is working we also have to call it I want to run self. Point Collision and now inside of main. Pi I can press down and nothing happens and I also realize why this should be self. Direction doy now let's try this again and now we're getting an error but at the very least we know that this function is doing something we are setting the center of the player so a point with X and Y to only a single point we want to instead assign Center y to that point next attempt if I now go down and now the player is moving in the right direction although the player never stops once we reach the Final Destination that is going to be fixed once we get all of the points inside of the point Collision I suppose first of all we can start with the up movement I.E we want to check or and let's put all of that on another line I want to copy all all of that and then check if self. Direction doy is -1 and then if self. rec. Center Y is smaller or equal to self. path 0 and one if that is the case once again I want to set the vertical center of the player to this point delete the next PATH point and then find A New Path direction that would cover the vertical part next up we can cover the horizontal bit which is going to be if self direction dox is going to be one and self. rec. centerx is greater or equal than self. path with zero and zero or we want to do the very same thing except now we are going to the left and we want to check if the player is below that point if either of those conditions are true then we want to set self. re. Center X to self. path with zero and zero we also want to delete the next point in the path so delete self. path with zero and finally we want to find a new path with that let's try May notp again and now the player stops when we are going to the right so this condition is true then we are at some point exceeding this point and we are deleting the final point in the path after that find. path since the path is going to be empty this if statement is not going to trigger anymore and the consequence will be that self. direction will be an empty Vector hence the player is not going to move anymore so we are definitely making progress however if I now run main.py again this is still working but if I now press down again we are once again starting on stage zero that is because because inside of the Overworld we are never updating the current node although that we can fix fairly easily I want to get the current Noe no need for custom parameters in this one and basically all I really want to check is I want to get all of the notes since those are Sprites I can run pygame dos sprite. Sprite to light with self. icon self. node Sprites and Bs I simply want to get all of the notes that the player icon is colliding with if we do have nodes then I want to update self. current node to the first item that the player has collided with now for this system you have to make sure that the notes are always far apart so the player can never collide with two noes at the same time although I think in practice that's not really going to be a constraint if you have two notes right next to each other something weird has happened in your Overworld but anyway after we are running the input method I also want to check self. getet current node if I now run may. pi and then I press down I should now not be able to move anymore and I indeed can't that is because the down node only has left right and up directions none of which we are checking for at the moment and for that inside of input we have to add the other directions I can actually just copy all of this and then I want to check if the player can go left if that is the case we want to self. move left next up we want to check right and the player can only move to the right and then move the player to the right finally I want to check the up Direction and then move up if this is the case also I realized we have to update this direction there should be K left K right and K up now let's try all of this and if I press down and up and left and right the player can move to all of the nodes and that works reasonably okay however there are still a couple of problems for example if I move and press again we can jump around quite a bit and that shouldn't be the case the issue we have right now is that we never disable the current node once the player starts moving hence even if the player goes down from the first node both of these conditions can still be true we need something to disable that kind of input once the player is moving all of that we can get with and not self. ion. path we only want to allow input if the player is on a current Noe if the player currently is not moving if we have the down key pressed and if the current node allows it let's now try all of this and now we get much better movement and I can press whatever I want while the pirate is moving we only move if the player is on a node and other than that this is working really well so I am quite happy with that part and I do realize all of this gets quite complicated you probably have to go over it a couple of times we are moving around a ton of data but anyway next up I want to animate the player icon for that I can minimize everything else and we already have all of the frames for the player we also storing them meaning we have to update the state and then animate through all of the frames to get a new image that part is going to be your exercise I want you to animate the player icon pause the video now and see how far you get I want to create another method that we can call animate with self and Delta time there we want to get the frame index and increase it by the animation speed multipli it with Delta time after that we want to update self. image with self. frames the frames of the current self. state and then we want to get an integer of self. frame index with modulus of the length of self. frames and the current self. state that way we are animating Forever This animate method we now want to call with self Dot animate and Delta time make sure here that animate is not inside of this if statement although I guess in practice it wouldn't make much of a difference but anyway let's try all of this now and once the player is moving nothing's going to happen and that is because we are never updating this state we are always on the idle State and idle is a plain image it's not really an animation to fix that we want to get the state and basically what I have done in here I always want to set a default state of idle but after that I want to check with a couple of if statements if we want to update the state for example if self. direction is equal to Vector with one and zero if that is the case self. State should be right that we can cop copy because next up I want to check if this value is -1 which case the state should be left we can copy all of this one more time and set this to zero and one in which case we should be going down and finally the last one is going to be negative one or zero and Nega 1 in which case we are going up and to clean all of this a bit up I want to keep the if statements on a single line that I think is a bit more readable and now before we are animating the icon I want to run self. getet state that should animate it and now we are getting a walk animation that is looking really cute so I am quite happy with that that covers the animation and that covers most of the path logic although there's one really important part that I haven't covered yet and that is at the moment inside of the data class we have unlocked the level up to three but in main.py if we're running the game we can go to all of the levels and that shouldn't be the case to get around that when we are figuring out inside of Sprites in the note class if the player can move in a certain direction we should do two checks number one if the direction is inside of this node in the first place but on top of that I want to check another condition and that is self. paths so the paths we already have and on there we want to get the direction we are moving in and from that we want to get the first value and just to make sure you know what's going on let me print self. paths and Direction and and comment out the other stuff if I now print what we are getting and run main. Pi I can press down we getting one for this one then we getting a two if I go back we get two R and three and so on we are essentially getting the path keys and we want to check if that value is smaller or equal to self. dat do unlocked level however once again remember that that what we're getting from here is going to be a string that way we can sometimes add an R to reverse the path which means we only ever want to get the first value which will always be a number and then convert all of this to an integer and now if I run main. Pi I should still be able to move up to these nodes but I should not be able to go to the right anymore after this point and I can't I cannot go down or right but I can still go left and up these notes are all fine on top of that if I go to data and set the unlocked level to one I can run main. Pi I can go to the first level but now I cannot go right and I cannot go up but I can still go left and with that we can unlock certain levels so with that we have all of the notes and we can make the player move however the entire thing is still looking a bit weird because we do not have a path for that inside of the overworlds I want to add one more method that is going to be let's do right below setup I want to create the path Sprites no need for custom parameters and in there we are going to need a couple of steps first of all we have to get the tiles from the PA and once again let me explain the logic we have a starting point with the player then we have another point and finally a starting destination and along those points we are going to have a whole bunch of Sprites there could be one Sprite here another Sprite here then another Sprite here and after that we are going to the right which means at the moment we have to convert this path into actual tiles that we can work with and that's what we're going to do in this part after we have all of that we can place the actual path tiles so that we have a path that looks something like this but that's going to come later ultimately what I want to create is a local variable that I called path tiles this will be a dictionary that at the moment is going to be empty but what we're going to create in here is for each level for example for level one I I want to have a list with all of the tile positions which are all going to be vectors so we can have a vector with the grid of 20 and 21 then another Vector with 21 and 21 we are going one column to the right although for now that thing is going to be empty what we now have to do is look at all of our paths which we can do with for let's call it path ID and data in do paths. items just to make sure you know what's going on let me print the path ID and the data also we have to make sure that we are calling self. create path Sprites inside of the dunder init method if I now run may not P we are getting the path ID along with some data points where we have the position so all of the points of the path and then the starting point Point that's all that we are getting from this for Loop now in there first of all I want to get the actual path which we are getting from data and the position after that I want to get the start node and the end note that is important because once again we have our path points and we want to get all of the tiles along this path however we also have to include the actual nodes for the start and for the end those are important to get all of the path unfortunately those aren too easy to get and to make that a bit easier I want to create another dictionary let's call it notes this is going to be a dictionary where we get note. level and then the associated Val value is going to be a vector with I suppose this could be the note and we want to get a grd position this one we don't have yet but we can create it fairly easily and then for note in self. node Sprites also this should be node level we want to create a dictionary with all of the nodes and then get the grid position for that inside of the note we have to create another attribute which is going to be self do GD position this is going to be a tupal with X and Y or actually this should be column and row to get either we want to get an integer with the position zero and divide it by the tile size and for the row we need the same thing except position one with that we can get all of the nodes inside of the level and from that I want to get for each path the start node and the end node which I can get via nodes and then the start node I can get from data and in there we have the start path the end node is simply going to be noes and then the path ID remember inside of self. paaths the key or the path ID for each of the paths is going to be the end position for that path which we're going to use to assign the end node while the start node we have inside of data and start so this we can use as well finally with that we can get our path tiles and then create a key with the path ID the value for all of those is going to be a list and the first value will be the start node while the last value so path tiles and the path ID is going to be the end node and then between the start node and the end node we want to append all of the other notes which we're doing via a four loop I want to get four index and points in enumerate PA so at this point I can finally look at the three points points and convert them into tiles on a grid something like this so far we already have the start point and the end point this is what we specified here on top of that we have the path tiles so we have an entry inside of a dictionary in which we are storing all of this all we have to do now is get all of the middle points we are always going to look at a whole bunch of points inside of the path from those points I want to get a start value and an end value both of those are going to be vectors meaning Vector for the points for the start point and for the end point I want to create another Vector although now for the end point we want to get the next Point inside of the path which we can't get via the four Loop but what we can do is get the path and then in that IND + 1 although this is not going to work by itself because of this index + one we are going to get an index error meaning we only want to do all of that if index is smaller than the length of the path minus one so with that we have a start point and an end point next up I want to get the path Direction which is going to be the end point minus the start Point divided by the tile size so what does that mean once again imagine we only have two points which are sharing the same Exposition but then for the first one we have a y value of 100 and for the second one we have a y value of let's say 250 that means our start Vector so this one would have a value of zero and 100 while our end Vector would have a value of 0 and 250 is going to give us a new Vector with zero and a 150 finally if we divide all of this by the tile size we're going to get the value of zero and then in this case 150 by 64 would be 2 something let's say 2.5 roughly now this Vector is incredibly use useful because it's telling us that from this point we want to create one tile here and one tile here and then we reach another point that for now we can simply ignore basically this point is telling us that from the current point we have to go two tiles down to get to the next Corner first of all though I want to create a start tile which is going to be another Vector where I'm getting the integer of start and Z divided by the tile size for x and for y this would be another integer with start one divided by the tile size so this is going to be our start point and after that we want to check what happens in our path direction if we have a path Direction with some y values then we can get another let's call it Direction score y this value is going to be one if path deer do y is greater than zero and if that's not the case else it's going to be -1 so at the moment we know from this point that we have to go down by two tiles from a certain start position this we have to communicate to python so if the Y value inside of this Vector is positive we want to go downwards and if that's not the case we want to go upwards and those two should be together and after that we can create a for Loop for y in range we want to go from here y to the integer of path Direction dot y plus Direction doy all in the direction that we have specified and once we have that I want to get the path tiles with the path ID and a pend the start tile plus the vector with zero and the y direction okay and I'm pretty sure at this point you are quite lost and sorry about that this part gets a bit complicated to explain we start all the way at the top where we are going through all of the points inside of the path and we also getting the index and there we only want to run a code if a certain condition is true I.E if the index is smaller than the length of the path minus one that is important because we want to get the start point and the current end point of this path which we want to store as a vector so points and then Vector path index plus one that way we're getting the current and the next point we have a point here and a point here and we want to get their relationship which we are going to get via the next variable path direction we are subtracting the end point from the start point so we know we are going down and this we want to convert into tiles that we know we have one and two tiles to go or to be a bit more specific these tiles should be here and here while the start tile is the tile we are currently on this would be this one this would cover all of that part and I hope that makes a bit more sense after we have done that at the moment we only really have one start tile this bit which we're getting from this start tile but then after that point we want to use the path Direction the one we have specified and then create actual tile from that I.E we first of all want to know if we are going up or down in our case we want to go down and then we going to run a for Loop where we are going to go in our case from the number one and then we are going to go to the integer of path deer y plus path deer pa. Y in our case was y + 1 so we would go up to three but not included since we are starting from one we would only get the numbers one and two which is exactly what we want we want to create this tile and this tile finally we want to specify the direction so do we want to go down or up in our case we want to go down and once we have all of that we can create the actual path tiles basically we are using the start tile then adding more points to it next up we want to do the same thing for the horizontal axis which means if our path Direction has X values then we want to get direction. X and this is going to be one if path direction is greater than zero else it's going to be NE - 1 and then we want to have 4 x in range direction. X in path deer do X Plus deer X and deer X we also want to add path tiles path ID then append the start tile although now this should be X and zero we essentially going to do the very same thing that we have done for y except now it's going to be on the horizontal axis and after we have done all of that we want to attach the end node and with that we have a whole bunch of vectors and that covers this part or at least I hope it does next up we can create Sprites from all of that I want to go through all of the path tiles we have just created with for key and path in path tiles do items and ultimately I want to create a path Sprite where we will need a position a surface a group and then the level that we want to unlock let's start by creating this path Sprite in the first place inside of Sprites I want to create another class let's call it path Sprite this is going to be a really simple Sprite we can simply inherit from the Sprite parent class and then Define thunder in it we will need self position surface groups a level and finally a z parameter although actually we don't need that because the path Sprites are always going to be on the same layer after that we have to call Super Thunder init with the position the surface the groups and finally for the Z argument I want to get Z layers with the path the only other thing that we are going to need is self. level and this one should be the level we getting from the parameters we're getting a path Sprite that we have to import p Sprite and now we can use this one first of all though this path is still going to be a list of points all of which we can get with four tile in path after that we can get the tile and that is going to be a vector which means for the position we can get tile dox and tile doy but keep in mind for these tiles we are working inside of a grid to convert all of that into pixel positions we want to multiply it with the tile size also let me use named arguments so all of this is a bit more readable for the surface for now I simply want to get pame do surface with a size of tile size and tile size the group is simply going to be self all Sprites and the level is going to be the key the key we are getting from the path tiles that should cover everything although there's a typo this should be groups and now let's try all of this there we can see a path and if we set data with unlocked level to four then we can see a whole bunch more levels and they still work really well now at the moment the path is entirely black but that we can fix reasonably easily for now though I'm quite happy with this I should also mention for the path Sprite this level parameter is really important because inside of the groups when we are drawing all of the stuff on the path we are checking if the level of the Sprite is below the unlocked level this applies both to the nodes and to the path Sprites if the level is below the unlocked level then we are not going to show it cool so with that we can get the path Sprite next up we want to figure out what kind of tile we want to show to make that a bit easier I want to wrap the path into the enumerate function that way we're getting an index and a tile basically what we are now going to do we are always going to check three tiles a starting tile a mid tile and an end tile and we want to know what the relationship between these tiles is for example are they all vertical are they all horizontal are they in a corner and so on for that first of all I want to ignore the first and the last tile meaning if index is greater than zero and index is smaller than the length of the path minus one that way we are ignoring the very first and the very last item inside of this path we are not going to need those anyway first of all then I want to get a previous pile which is going to be the path that we are working with and then index minus one and from that I want to subtract the current tile after that I want to get the next tile and this is going to be the path with index + one also minus the tile minus the tile here is important because I want to get the relationship between these tiles if the middle tile has a vector position of let's say four and five and the tile that came before it has a position of four and four if we subtract one from the other what we have done here for example then we are going to get a vector with a value of 0 and -1 which is going to tell us that the previous tile was above the current tile after we have all of that we can come to the actual comparisons for example I could check if the previous tile dox is equal to the next tile dox if that is the case I know the tiles are all on the vertical a AIS which means from inside of main.py when we imported all of the path Graphics we can choose one of those although once again the Overworld frames are only available inside of the setup method that's not too much of an issue before we are calling self. create path Sprites we can create self. pathore frames which are going to be the Overworld frames with the half next up then for the surface I want to have self. have frames and I want to pick the vertical key from that and I suppose just for testing purposes at the beginning I want to create a local variable with a black surface this surface we are then going to overwrite with these conditions meaning when we are creating a path Sprite the surface should get a Surface also this path Sprite should be inside of this if statement and now if I'm trying m. Pi we can see all of the vertical paths work just fine down there this one also works well cool so that's a good start next up we can check an L if statement that if we have all of the horizontal tiles meaning the Y for next tile and previous tile is identical I want to get the surface except now the surface should be horizontal let's try that and that's already looking significantly better which means all we have to do now is get all of the corners that part is going to be a bit more labor intensive but I think it's not too bad the only thing we have to be careful about here is that the corner could either go this way or it could go this way for example if we have a corner that goes like this we could have a previous tile that is on the left so X would be -1 and then also we have a next tile where Y is going to be negative one that would be one condition but we could also flip those two around where the previous tile was on top and the next tile is on the left we have to cover both of these conditions which means I want to check if previous tile dox is equal to -1 and next tile doy is equal to -1 as well or and now I want to copy all of this I want to check if previous tiley is -1 and next Tilex X is1 if that is the case I want to assign a new Surface which once again we are getting via the path frames and the key for this one is top left let's try this and let's find one of those Corners okay it's right behind the palm tree but I hope you can see we are getting the right kind of tile that is actually the only time we are using that kind of corner okay bad example let's do another condition where we want to place the bottom right corner which is going to be the exact opposite compared to top left which means all we have to do is change all of the minuses to a plus and if I run this one now we can see up there we have a much nicer looking corner and if we go around okay I need more corners in this game that would make my life easier but this one is definitely working so we know the logic is sound next up I want to check if we are placing let's say the bottom left corner for that X is going to be -1 for the pre previous tile and for the next tile Y is going to be one then we have to invert all of this as well and for that the next tile is going to be negative one let's try this one now and there should be one more Corner that looks pretty good finally we have the last condition and that is going to be this one where we are inverting the X and the Y in both cases also this one should be top right now if I run main. Pi we have all of the corners and that looks pretty good so yeah I'm quite happy with that and finally if none of these conditions apply and they should all be an L if case I do like to add one kind of safety condition where we have some kind of tile in the end that makes sure that if something goes wrong with the math we have some kind of tile that is probably going to look okay for that I want to get self. path frames with the horizontal tile the one we have placed all the way at the beginning now if the MTH is going wrong this still wouldn't look ideal but it beats having a black surface but anyway after that we don't need the black surface anymore and now if I run main. Pi all of this is still going to work just fine so that looks pretty good also what we can do inside of data I can unlock all the levels I think up to six I've made and now we can explore all of the levels I think we only have five in total actually but it doesn't really matter for the logic anyway at this point we have all of the Overworld that looks pretty good that covers the entirety of the Overworld which means we can close the import assets method and now what we have to figure out is how to connect all of the levels with the Overworld for that first of all I want to import all of the TMX maps at the moment we only have level zero which is also the om. TMX file but that I don't want anymore this should be z. TMX after that I want to copy all of this a couple of times because I want to have level one two three four and the associated level is going to be 1 2 3 and four once again let's have a look at the project folder inside of data we have all of the levels and this one goes go up to six all of this is what I want to import although level six is the one I just created all the other ones I made earlier and the one that I forgot is going to be level five with 5. TMX so with that we have all of the TMX Maps that's a good start but that still doesn't help us to switch between a level and the Overworld to fix that part I want to create another method that I am calling switch uncore stage where we are going to need self we will need a Target and then the level we want to unlock so an unlock parameter which by default is going to be zero essentially in there we are going to check if the target is going to be a level if that is the case I want to update self. current stage to whatever the level is going to be although for now let's ignore that part and simply add pass in here but essentially to explain this a bit better we're going to pass switch stage into the Overworld and into all of the levels and then from I ofos we could call it which is then going to bring us back to this game class and finally via switch stage we are going to either assign a level class to the current St stage or the Overworld to the current stage and if we are not going to Target a level then we know that we want to create the Overworld hence self. current stage should be the Overworld or rather one instance of the Overworld class although once again this we are not going to do just yet let me keep pass in there what I actually want to start with and for that I want to disable the current stage and uncomment the current stage we created earlier if I now Run the game again we have a completely new level and that still works perfectly fine so that's a good start however now once the player reaches the end of the level then we want to call switch stage for which we have to pass this method it into the level class also make sure to not call this function we want to pass the function itself not the return value into this class after that inside of the level and let me minimize everything we are going to need another parameter switch uncore stage and then we want to store the function in an attribute so switch stage is going to be switch stage finally we want to work inside of check constraint because there we're checking the bottom border and the success state for now let's work in the bottom border so if the player Falls too far down then we want to go back to the Overworld we want to run self. switch stage we want to Target the Overworld and for the Target level the one we are going to unlock I'm going to set this one to1 we do not want to unlock any new level which we're going to indicate via a negative one after that inside of main do PI we are running this self. switch stage or more specifically this L statement and for now let me simply test if this is working I want to print the Target and I want to print the unlock parameter let's try this and if I fall down we can see we have Overworld and and ne1 so this is running indeed which means we can update the current stage to create the Overworld and for that I am kind of regretting deleting the Overworld from earlier but anyway when we are creating the Overworld we don't need that much data let me copy the parameters we want to create all of that TMX map is easy this is self. TMX Overworld data is self. data and the Overworld frames are going to be self. Overworld frames the ones that we are getting from import assets those let's try all of this again and now if the player falls down we have the Overworld and we can still move around so that is looking pretty good however at this stage inside of data I want to set the unlock level to zero that makes it so that if we die we are stuck on the first level note that looks good however inside of the level if the player reaches the flag or the level finish rectangle then we also want to switch the stage however we want to unlock the level that we have assigned as the level unlock if you look at tiled we have our data object and in there we have a level unlock at the moment we are inside of z. TMX and this one unlocks level one although to get that inside of the dunder init method I first of all want to store it inside of an attribute all of that we are storing inside of TMX level properties after we are getting that dictionary I want to create self. level unlock which is TMX properties with the key level uncore unlock and after we have that we can pass this as an argument into the Overworld next up then we can once again work inside of switch stage all I really want to do in there is if the unlock value is greater than zero which means we can get self. data and then update the unlocked level and set it to the unlock value that we we are getting however if that is not the case else we know that the player has failed and as a consequence we want to update self. data. health and reduce it by one that should actually work already if I now run all of this look at the health we have currently five hearts and if I fall down this is being reduced to four so that's working pretty well on top of that entitled I'm going to move the pirate all the way to the right so it's going to be really easy to finish the level if I now run all of this I can finish the level and we have unlocked the next Noe we can also go back however there's no way to start a level from this point but that's okay we have covered at least one direction next up we want to go from the Overworld back to a level to make that work we have to pass switch stage into the Overworld fairly easily done we have to pass self. switch stage in there and then work inside of overw world. Pi first of all we have to create another parameter switch uncore stage which we also want to store as a parameter switch stage is going to be switch stage after that I want to work inside of the input method the key I now want to check is if keys and py game. Kore return if the player is pressing enter Then I want to self. switch stage to the level which once again is going to bring us back to this method in which we want to create an instance of the level class for that we will need all of those parameters although we do have to be a bit careful here because at the moment we are always creating the first level the other arguments are totally fine though and by the way this mistake also happens inside of the init method which I guess it's fine but it should be more flexible we shouldn't always start with index zero we want to start with the first item inside of this list which we have to find inside of data we want to start with self. current level or unlocked level by default they are the same to get that value I want self. data and then the current level which by the way I also want to do when we are creating the level in the first place also before we are continuing when the player is pressing enter I have to assign a new value to the current level which means self. data. current level is going to be self. Curren node. level that way we know what level we are and we are storing it so once we are going back from a level to the Overworld we can place the player in this current level position let's try all of that and once again the player starts in the level if I now hit this part we are getting an error that there's no K return that's a simple typo we just need an uppercase K next attempt once again I want to jump to the flag and now this is working I can press enter and now we are in another level I can try to fall down once again and now we are back to the Overworld also while we're here let's try the old level this one is still working and yeah this is working quite well and at this point we have finished most of the game there are still a couple of things that I would like to add later on but for now none of of them are too important and I think this part got really long so if you got to the end of it well done that was a tough [Music] section all righty at this point we are basically done with the game the last thing in terms of code that we have to do is if the player runs out of Health we want to close the game that should be very easily done on top of that we also have to add some audio files which isn't going to be a problem either so before we start what I do want to cover is here we have the project folder and so far we basically ignored the audio folder but if you open that one we basically have a whole bunch of files not that many all of that we want to import and then play at a certain time Starlight City by the way is the background music but that's not too important for now I simply want to work inside of the main file and by the way I closed everything I didn't need the only file FES that we really need at this point is main.py level. pi and player. pi and first of all inside of the game class I want to add one more method that one is going to be check game over no need for custom parameters there's nothing complicated we are going to do in here we simply want to check if self. dat. health is smaller or equal to zero then we want to quit the game which we do with pame do quit along with sis. exit the same thing we have done in the run method down here on top of that before we are running anything I want to self. check game over oh and while I'm testing all of this inside of the init method the first level should be the Omni level just to make sure we have all of the elements at one time let's not try all of this and I think the fastest way to dive would to jump on the spikes and there we go the game ends once the player runs out of Health not the most elegant mechanic but for a simple game this is still totally fine cool with that we have the game logic itself done the last thing we have to add are the sound files and that is going to happen first of all inside of import assets because we have to import just a few more things all of the audio files I want to store inside of an attribute self. aore file like with all of the graphical assets this is going to be a dictionary for example one entry in there could be the coin sound this coin sound we are getting with py game. mixer. sound and then we will need the join method for the path I want to go up a folder then I want to go to the audio folder and in there we have coin. wav that would be a good start next up I have to get this audio file somewhere into the the level and then play it at the right moment first of all these audio files we want to pass into the level when we created which happens on this level so after the level frames I also want to pass in self. audio files and really important now we are creating the level twice once inside of the dund NIT method and once inside of switch stage most specifically here in both cases we will have to add self. audio file after we have that I can minimize both of these methods and then in the level after the level frames we will need audio uncore files after we have that inside of the D init method of the level class I want to add another section let's call this one sound or I suppose audio but basically the same thing first of all I want to create a coin sound which we're getting from the audio files and then the coin entry so next up we have to figure out when to play it and that fortunately is really easy we basically always want to play the sound when there's an item Collision which is going to happen if this if statement triggers meaning after that I want to get self. coin sound and play it if I now run all of this that sounds good cool so with that we have the coin sound although this coin sound might be just a little bit too loud to fix that self. coin sound. setor volume and in there we need to value between zero and one with one being 100% of the volume which is the default and I want to set this to let's say 0.4 let's try this one again and the item sound is a bit less loud as always this is is a very subjective thing so choose whatever you think is best in fact finishing up all of the sounds is going to be your exercise I want you guys to finish up the sounds and then you are done with the project see how far you get first of all we have to import more audio files in my case I am going to copy all of them in so you don't have to watch me type basically what we need is all of this it sound for the attack jump damage and pearl they all in the same folder and you always deal with wav files next up then inside of the level we now have all of the files inside of audio files and we have to figure out when to play them and there's one that we can play in the level right away that is the damage uncore sound which we're getting from audio files and damage also while we're here I want to reduce the volume of this thing just a bit with set volume and set this one to 0.5 this is going to be a sound that we want to play whenever the player gets damaged and that we know from inside of the hit Collision because in there we are already running player damage on top of that I want to get self. damage sound and play that one if I now run main. Pi again we are getting a sound let's try it again cool that works now this sound if you hit something else is kind of working but you might want to add more audio files also with the spikes not amazing but well it kind of works righty next up I want to work on the Pearl sound so the sound that will be played whenever a pearl is being created that first of all needs to be an attribute inside of the level class Pearl sound is going to be audio files the entry for this one is Pearl and that part is super easy because we have an entire method to create a pearl whenever that happens we also want to play the sound which means self. Pearl sound and play that one let's try out of that and we are getting a sound so with that we have all of the Easy Sounds or well the super easy ones the other two sounds for the attack and the jump need to be inside of the player for that we have to work with the setup method because in there I want to pass in the audio files and let me minimize the Thunder nit method and then work inside of setup first of all in there we also need the audio files after that when we are coming to the player that is happening here I want to create two more attributes I want to have an attack sound which we're getting from audio files and a tack after that I want to have a jump sound which we're also getting from audio files except the key for this one is jump and then inside of the player we will need two more parameters for attack sound and jump sound both of those we want to store as an attribute meaning all the way at the bottom I want to have the audio section self do attack sound is going to be the attack sound and self. jump sound will be the jump sound perfect and after that I want to minimize all of the methods so things are a bit easier to see and let's start with the attack that is going to happen inside of the attack method inside of this method we can play the attack sound all we really have to do is self. attack sound dolay let's right up on and if I press X we are getting an attack sound this is working if I'm on the floor and if I am jumping cool besides that we have the jump sound that I want to work on inside of the move method and there we are checking if the player is currently jumping although we have two different kinds of jump a jump from the floor and a jump from the wall for both of those we want to play the jump sound meaning we have to call self. jump sound and play it in both of those cases let's try that sounds good let's try a wall jump and that's also working and I suppose the one thing you want to be careful about here is that this jump sound shouldn't be inside of this if statement if it was the player could for example play the jump sound if he was in the air already which I really don't want to do but anyway with that we have most of the sounds now you could be adding a few more but I think this video has gone on long enough so I don't want to stretch it too far that being said there's one more file that I do want to add this would be self. BG music the file for that one let me simply copy it in it's going to be py game. mixer sound and we are importing Starlight city. MP3 this is the only background music that we are going to play and to start it I want to work inside of the dunder init method all the way at to bottom I want to play self. BG music with one argument negative one if you insert a single argument into the play method then you're defining the amount of loops and if you add a negative one in there then you are looping this file indefinitely and with that [Music] [Music] cool I think that is working I suppose there's one last thing that we could be doing and that is to set the volume of the background music and that we have already seen self. BG music and then set underscore volume I suppose this one we could set to 50% try this [Music] again and that also sounds about right and once again choose whatever numbers you like and well with that we have finished the entire tutorial so I hope all of that was useful and well I'll see you around