Creating Space Invaders in Pygame/Python

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
in this tutorial we are going to create space invaders in pygame and this can be either in a modern style or with a retro overlay i will cover both now this is a slightly more advanced pygame tutorial so i assume that you already know the basics however if you don't or you want a refresher i have made an extensive pygm tutorial that would prepare you perfectly for this video so check this one out if you're a bit lost and this is only going to be the first advanced pie game tutorial and there will be quite a few more the next one is going to be a mario style platformer with pirates so get subscribed if you don't want to miss it also you can check my twitter for updates on what i work on and let me know in the comments if you would like to see a game in the future but with all of that covered let's go into the game and i hope you enjoy alrighty so let's get started with the first part of this game and for now we are only going to set up the main window and the class that is going to contain the logic of the entire game so for now we're not really going to draw anything we're just setting up the actual game so let's jump straight into pygame and let's have a look at this and in here i already have a couple of things open most importantly we have the basic pie game setup nothing fancy happens here then i set the display width and height and use those to create a display surface and that one we call later down here to limit the frame rate and then in the actual game loop we are checking for the escape button we are drawing a background color and then we are drawing anything that was drawn in the game loop so this entire thing right now is essentially the most basic setup for pygame that doesn't do much yet so if you execute all of this you would get a blank window with a darkish background so really not much is happening here and this code isn't going to change all that much because most of our game logic happens inside of a class and all we do in here is create an instance of the class and run one method of that class in the game loop however there is one change i would like to make and that is i want to place this entire code inside of an if statement that if done the name is equal to the string thunder main and if i execute this code we are not going to get any kind of change and the reason why i am using this if statement is because we are going to work with multiple files so there is a very small chance that you might execute some code you don't actually intend to run and this if statement prevents that entirely so it's essentially a safeguard that we didn't necessarily need but it is good practice so now we have our basic setup now with that covered i want to create a class and this i code game because it contains the game logic and for now this class is going to have two methods one is the init method that for now doesn't do anything so i just add pass in here and then i also want a method called run and this one also gets a pass so we don't get an error message so let's talk about these two methods init is the usual initiate method of a class and we are essentially going to add lots of sprite groups here for example the player has a sprite group the obstacles and aliens are sprite groups each and stuff like that and that is essentially going to be the framework of our game and then in the run method we are going to update all sprite groups and we are going to draw all sprite groups so this run method is essentially the main part of our game so in the if statement i want to create an instance of this game and then in the game loop i want to run game.run and that way we can add all of our game logic in this game.run and write the actual code inside of this class and this is a nice setup to keep our actual game loop clean and have all of the logic inside of the class and that should make running the entire game much simpler and already with that we already have the basic framework for our game now obviously right now it really doesn't do all that much but that we can start working on now and the first thing i want to work on is the player and the player will have to do a couple of different things it needs to show an image of the player itself it needs to be able to move it needs to be constrained to the screen so we can't move outside of it and it has to be able to shoot a laser and to recharge which basically means there's a timer between shooting a laser so the player can't shoot continuously and all of this is going to happen inside of a sprite class so we keep the entire code organized and have one class for the player so let's jump straight back into the code and let's have a look at this so here i am back in my main file and i want to create a new one so let me create a new one and let me save it and i'm going to call this one player.pie and in here first of all i want to import pygame and next up i want to create a player class and this one has to inherit from pygame.sprite.sprite and in here as always we need the init method needs self and for now nothing else and first of all we need super dot init very easy to forget but very important and now like any other sprite class we are going to need self.image and we are going to need self.rect and both of those are very easy to get for the image itself i just want to import an image so pygame.image.load and filepath is going to be this one so we are going to the parent folder and there's a folder called graphics and in there there's an image called player.png and i want to convert alpha this image and now for the rectangle i want to grab myself dot image and get the rectangle and i want to place the mid bottom and where i want to place it is going to be a position that we are going to get when we initiate this class so effectively this position here is going to be a tuple that we pass in here and already this is going to be the most basic setup for sprite class and now you should be able to call this class inside of our main file and this could actually be a really good exercise that i want you guys to do two things first of all add one attribute to our game class that is called player and this player is going to be a group single that contains the player sprite and then in our run method i want you guys to draw this class and this should be somewhere on the screen it doesn't really matter where ideally put it somewhere on the bottom alright so first of all i want to create self.player in my game class and this is going to be pygame.sprite.group single and in here we have that our player although in my case i first want to create a player sprite and this is going to be player with a certain position let's say for now i want to place it right in the middle of the screen so 300 and 300 however if we were to run this player we would get an error because this doesn't exist in our file yet because we have to import it and well all i have to do is from player import player so we want to import this player file here and from this file we want to import this player and once we have that i want to put this player's bright inside of my group single and now we have a group single with one sprite inside so inside of our run method we can call self dot player and i want to draw this and i want to draw this on the surface screen which is going to be our display surface and now let me run the code to see if this is working and indeed there we can see our player so this is working quite well but now obviously we want this player to be at the bottom of the screen so instead of placing it in the middle in the y axis this should be 600 or even better this should be screen height and the x-coordinate should be screen width over two and now let's run this again there we go now our player is in the bottom middle of the screen for the simple reason that we have first gotten this green with and divided by two so it's going to be half of the screen and the bottom of the player is at this position here at 600 and since this is going to be the bottom border this puts our player also at the bottom border this is already a pretty good start so with that we can start working on the player input and this also happens in the class and let's call this one get input it needs self as usual but nothing else and in here we first of all want to get all the keys and for that we need pygame dot key dot get underscore pressed and now we can check different keys being pressed so if keys highgame.k right so we're pressing the right arrow down and if that is the case i want to grab self.rect and move it somewhere to the right and the amount i want to move it i want to defined in self.speed so when we initiate the class i want self.speed and let's say for now we can put this down to a five so whenever we're pressing the right button our player rectangle is moved to the right so the entire thing is being moved to the right now along with that we also want to do the same thing for pi game dot k left and this time it would be self.direct minus equals self.speed and with that we would have at least for now all the input we are going to need and next up i want to create an update method and in this update method for now we just call getinput and this needs to be self.getinput so now if i go back to my game class i can call self.player.update and this should be working so let's try and it is not working for the simple reason that this should not be the rectangle it should be rect.x because we're only moving in one direction but now let's try this again and now it is going to work so with that we can move our player left and right however there's one problem our player can move outside of the screen so that's not great and this could actually be a really good exercise for you to work on that i want you guys to figure out how to constrain the player to the actual window and this should happen inside of the player class let's start working on it and first of all our player has to know how wide the screen is so what i'm initiating the class here i want to add another argument and that is going to be the screen width and while we're at it i think it's also a good idea to determine the speed of the player in here so i'm going to add another variable for the speed so this is the maximum constraint of the player and this is the speed so with that let's go back and here we have to define a constraint and a speed and the speed is the easier part we just add speed in here and now i'm going to add another attribute that is going to be max x constraint and this one is going to get my constraint and now this attribute we can use to constrain the player and this i'm going to do by creating another method and let's call this one constraint and then here we don't really have to do all that much all we really want to do is to check the left and the right side of the player so if self.direct.left for example is smaller or equal to zero because that's our left side and if that is the case i want to set the self.rect.left exactly to zero so essentially what is happening here is that whatever our player is further to the left than the screen we are placing the player right on the left side of the screen and we can also do the same thing for self.direct.right if this is greater or equal to self.maxx constraint and if that is the case self.rect.right is going to be exactly on that point and that is literally all we needed to make this work so let's call self.constraint let's save the file and let's run our main game and let's try this again and there you can see this is working quite well that the player cannot leave the screen which feels much more realistic now with that part covered we can add the laser shooting mechanic of the player and this is going to be another part in our get input method and really all i want to do in here is if keys pygame dot k underscore space so we are pressing the space button then i want to run a method and let's call this self.shoot laser and obviously now we are going to need that method so define shoot laser needs self and nothing else however right now we can't really create the entire laser yet for the simple reason that the laser is going to be its own spread class that we simply spawn in here and we haven't created laser yet so we can't use it instead i'm just going to print shoot laser for now and we're going to change this in just a bit and let me save the file and let's test if this is going to work so now if i press space you can see in the bottom right that we are shooting the laser multiple times and this is actually one of the problems that we have to address that this laser needs a timer because if there wasn't a timer the player could simply keep on pressing space and shoot forever which would make the game very simple which we want to avoid so in our player class we have to add a timer and that is going to work with a couple of different variables first of all i create a variable called ready and by default it is going to be true then i want a variable called self dot laser time which is going to tell us when we have shot the laser and by default this is zero then i want self.laser cool down and this is going to be 600 so we will be able to shoot a laser every 600 milliseconds and we will only be able to shoot a laser if we are pressing space and if self.ready and every time we have shorter laser self.ready is going to be false so if i just have this setup we should be able to shoot at least once but not multiple times and now if i keep on pressing space we can't see anything but we have shot our laser once so this is working at least for now but we do want to wait to set ready back to true again and for that we have to add a little bit more here first of all i want self.laser time is equal to pygm.time.getticks so we are measuring the time since our game has started and now let me put it right below we need another method that i have called recharge it does not need any arguments besides self and in here we are first going to check if not self read d so if we have to recharge the laser if that is the case we want to check our current time and for that we need pygame.time.getticks the very same line we have used up here the only difference here is that this get ticks is only used once so we're getting one point in time whereas this get ticks here is being run continuously so we get multiple times and that difference is very important because we can use that very well and what we want to do is to check if our current time minus self.laser time is greater or equal than our self.laser cooldown and let me visualize this on a timeline so here we have the entire time of our game and let me use a different color for this that let's say on this point here we have our laser being shot and let's say hypothetically this is at 1 000. so this is our current time let's say at this point here we are going to be at 1 100 at this point here we are at 1 400 and then at some point here we are going to be at 1 600. and really all we are going to do is we are checking this time and subtracting our starting time from that and in this case this would be a hundred if we use this case here this would be 400 however if we get to this case here this would be 600 and as a consequence this if statement down here would trigger and that way we have a timer so if this timer triggers we want to set self.ready back to true and now obviously what we have to do we have to call self.recharge and that should be all we need for the timer so now let me keep on pressing space and you can see that we only get the laser ever so often so this is working really well and with that we pretty much have most of the player class ready there is very little we actually have to add to this the most important thing and let me minimize everything else is that we have to be able to create a laser and right now that doesn't exist so let's start working on the laser and the laser is going to be incredibly simple because at the end of the day it is literally just a sprite class with a position and a speed and really what we are going to do whenever the player presses space we are going to spawn a laser sprite in the position of the player and move it upwards and then later on we are going to use the same laser class to spawn it in the position of an alien whenever they shoot and then move it downwards but that's really all it does it's essentially a sprite class with position and a speed and well that's it so let's jump back into our code and let's create it here i'm back in my code i have main and player and i want to create a new class that i'm going to save as laser dot pi and here again we have to import pygame and again i want to create a class that i call laser and this one again has to inherit from pygame.sprite.sprite and here as always we need our init method needs self for now and we need super and then under init again and now first of all we are going to need self.image and self.direct as always and here we don't even have a graphics instead i just create pygame.surface so an empty surface and this i have set to width of 4 and a height of 20. and then for pygame.rect we get our self.image.getrekt and in here i set the center to a specific position and this position again i am going to get when we initiate the class and there's one more thing i would like to add and that is the image needs to be filled because right now it's black which wouldn't look particularly good and i just want to fill it with white and that again is a very simple setup for our laser so now i can save this file and go back to my player and now i want to import from laser import laser and now every time this laser shoot is being run i want to create one instance of this laser class and for that first of all i am going to need a sprite class so in the init method self.lasers and this is going to be pygame.sprite.group and now every time we are running shoot laser i want to get myself.lasers and add laser and now this is going to need a position and this position i can get from self.direct.center so literally all that's going to happen is every time we are shooting a laser we are creating a new sprite and the position of our player so now i can save the code and i can run the entire game and now if i press space nothing is going to happen for the simple reason that we are not drawing any of these lasers and that i would like to do in this run method because that gives me the most control over where stuff is being drawn and well all i want to do is self dot player dot sprite dot lasers dot draw and this has to be on screen and let me put it below the update method so we are separating updating and drawing and now we should be able to see the lasers so let me run off this again now i press space and now we can see a laser being spawned now obviously right now they're not moving upwards so that's what we have to work on next but we do have a start and this could actually be a really good exercise for you that i want you guys to add a little bit of code to move the lasers upwards so try to figure this out yourself alrighty let's try together so back in my laser class i want to set a speed and then this is going to be self.speed is equal to speed and now this laser is going to get an update method that we are going to get self.direct.y minus equal self.speed and really all we're doing here we are moving the rectangle upwards by a certain speed and since most of our lasers are going to move upwards we can add a default argument in here let's put it to negative 8 that i think looked good and now in our player class what we have to do is to update this laser so self dot lasers dot up date and now when we create our laser here we could add another argument but since we have a default argument we don't necessarily have to but by itself this should already work so let's try now if i press space nothing is working and that is because this has to be plus equals self.speed because right now we had negative 8 and we subtracted negative 8 so it became positive so our laser moved downwards but now if we have this for positive let's try this again now we have a laser that is already working really well so this is going quite well nice but there's one more thing that i do have to add here that right now these lasers never disappear so we have to destroy them once they leave the screen and let's call it destroy and all we want to check is if self.rect.y is either smaller or equal to negative 50 let's say or if self.rect.y is greater or equal to the height of the window so this would be 650. however i want the game to be flexible so we can make it larger so we need to get this number and this has to be another argument so screen height so self thought let's call it height y constrained and this is going to be screen height and then in here self dot height y constraint and let's add 50 just to have a little bit of wiggle room and if that is the case we have to run self.kill to destroy the sprite entirely and don't forget to actually call the function so self.destroy in our update method and now when we create a laser we have to add this argument as well so and here when we create our laser let's say now i want to go with negative 8 for the speed and in here we do have a minor problem now for the y constraint because we didn't actually pass that in we only have the x position however what we do have is the bottom of the player so this self.rectangle we just want to get the bottom so self.rect.bottom and this is actually a fairly realistic way that whenever the laser is below our player we want to destroy it which i think does start to make sense and well that should be it let's try to run the game now and oh well we have a non-default argument follows a default argument so let's get rid of this negative eight and now it works our laser still works you can't really see the laser disappearing but now our game should not spawn infinite lasers so this is going to help our game be slightly more efficient and with that we have both our player and our laser and that's literally all we have to do for both of these later on we are going to do a bit with collision but that happens inside of our main class not inside the player or the laser and that means we already finished a major part of this game now with that covered we have two topics to address one is the placement of the aliens and number two is the placement of the obstacles and they are independent of each other so it really doesn't matter which one we are going to start with and both of these are done in very similar ways so if you understand one you probably are going to understand the other fairly easily and let's start with the obstacle i think that is going to be the easier one and let me first explain how the obstacle is going to work and here you can see one of the obstacles on the screen and it looks like this is one solid figure but that actually isn't the case instead this obstacle consists of individual blocks and each of these blocks is a sprite and really all i have done is i have arranged all of the sprites in such a way that they look like the obstacle but that is literally it all that you are looking at is a couple of square shaped sprites in a certain shape so we have to figure out how to create this kind of shape and for that we are going to need a nested for loop with the enumerate method so let me explain how this is going to work and here is how this would look like in code at least in a more generalized way essentially what we are going to have is a list with strings inside and each string is going to be one row of our obstacle and this would be our first for loop and here we use the enumerate method to figure out on which row we are on and then a nested for loop is going to look at each individual column inside of this row and again we are going to use the enumerate method to figure out on what column we are on and if you combine these two enumerate methods you get a grid with specific positions so for example the top left would be zero and zero and to the right of that we would have one and zero and to the bottom of that we would have zero and one and this we can then use to place elements on the screen and we can even make this more flexible by adding offsets for example to move all of the shapes in a certain direction but that's literally it it really isn't all that hard when it comes down to it but alright that again was a lot of talk so let me actually implement and let's create one obstacle and once we have that we can create multiple and here we are back in the code and the first thing i want to do is to create a new file and i want to save this one as obstacle dot pi and here again we need to import pi game and in here i want to create a class that i call block and this one again is going to be a sprite so we need pygame dot sprite dot sprite now we need the usual init method with self and nothing else and then we need super down there in it for this one as well so everything works well and now we have to create self.image and self.rect and for the image literally all i want to get is a surface and now we have to figure out the size of the surface and i want to set this from our main class so what i'm going to do is when i am creating this class i'm going to set a size and since we want squares the size for x and y is going to be the same so we put size in both of those so with that we have our surface now next up before we get to the rectangle i want to fill the rectangle with a color and this color i also want to set from the main class so i'm going to place this also as an argument or parameter and then finally direct itself.image.getrekt and here i want to place the top left and the position i do not know yet so this is just going to be x and y and usually for this position here i use a tuple so for most other classes i just write pause but in this case i want to have specific control of x and y which is why i'm going to separate them by each variable you're going to see in a second why that is helpful but well that's just going to be another parameter for x and y and that is literally all that we needed so this is going to be one part of our obstacle now there's one more thing we need outside of the class and that is the actual shape and this is going to be a list with strings and in my case let me just paste it in it looks something like this so let me explain how this shape is going to work in here we have lots of strings and there's two options we either have an x so for example this one or we have a space so let me use a different color that here you can see a space all we are going to do at the end of the day is wherever we have an x we are going to create a block so for example this x here is going to be a block this x here is going to be a block all of these x's are going to be a block and if there's a space so like this area here there's not going to be a block and that way we're going to recreate this shape as sprites and for that we are going to use the function i explained earlier but this is literally all we needed from our class so now i can save it and go back to my main file and first of all i have to import my obstacle file and now before we get into an actual method to create all of this we have to create a couple of basic variables and let me actually add a couple more comments to be a bit more specific so let's call this one player setup and below that we have the obstacle set up i should really use comments more regularly but alright the first thing i want to do is to capture the shape of our obstacle so self.shape and this is going to be obstacle dot shape so literally this thing here and that is just to make it a bit easier to work with inside of the game class now next up i want to set myself dot block size and i set this to a six so this is the number we are going to pass in here later on and finally we want to have a group that collects all of these sprites we are going to create in just a bit and i've got this one blocks and for now this is just going to be pygame.sprite.group with nothing inside and that is all we need for basic variables now we have to create a function that creates one obstacle and well let's call it create obstacle and for now we are not going to pass any arguments inside of this but we are going to pass some in just a bit and now we have to use the function i talked about earlier so what we want to do is we want to loop over this shape here so let's start with for row in self dot shape and each row in here would be one of these rows now the problem with just using self.shape is we wouldn't know on what row we are on and to get that information i want to get the enumerate method and what this one does is it returns an integer on what index we are on so for example this would be the index 0 this would be the index 1 the index 2 the index 3 and so on and this is the information we are going to use to set a certain information for our height orbital for our row so once we have that we know on what row we are on now next up we have to figure out on what column we are on and well for that i'm going to approach this in a very similar way so we have call index and call in enumerate and this is just going to be row so let me explain again what we are going to do so let's say we are currently on the first row so we are in here now each of those items is going to get one index so this would be zero this would be one then this one here would be two and then this one here would be three and by using these enumerate methods we get the specific positions of each of these x's for example this x here would have the index 0 and 0 whereas this index here would have the index of 0 and 2 i think and this is the information we need to place something on the screen and all right this is all we need to find the position now obviously what we want to do is an if statement so if the column is equal to the letter x because we want to ignore all the empty spaces and well now we have to figure out the x and the y position and once we have that we can create one block that is going to be obstacle dot block and now in here we have to add a couple of arguments so let me copy them really quick so we can work through them one by one and the size we already have it's literally just this one up here so this one is the easiest one to get we just want to get self dot block size next up the color is also very easy because i have a tuple that i want to use here and the tuple i want to use is this one so this one is pretty fine and this is a reddish color that i think looks quite good now finally we need our x and y coordinates and those we are going to create now and this could actually be a really good exercise for you try to figure out what we have to do for x and y to place each block in the right position so for x we obviously have to look at our columns so this part here and the first we need is to get our call index so this will give us a number from 0 1 2 3 4 and i think the maximum column we have is 11 so the highest number here will be a 10 and the lowest is 0. and this would then be a start however we don't just want to move the x by 1 pixel we want to move it by the size of each block so each block starts after the previous block has ended and well for that all we need is to multiply the index by self dot block size and well that's literally all we need to get this thing started now for y we are going to do basically the same except now we use the row index but we're still going to multiply by self.block size and well with that we have our basic block now the final thing we have to do in here is to get our self.blocks and add the block we have made to it and that is already going to be a really good start now what i want to do is when we initiate the class i just want to run self.create obstacle and then in our game method i want to get myself dot blocks and draw all of them on the screen and let's just see what happens and there you can see in the top left we have our first obstacle so this is actually already working now obviously the obstacle isn't supposed to be in the top left so that's something we do have to work on but well it's a good start so what we have to work on now is to offset all of this to place all of them a bit further down and well that isn't all that difficult because literally all i want to do is to set an x start variable and a y start variable and then every time we are placing either the x or the y we want to add this number towards them so x start plus all of this or y start plus all of this and now when we create our obstacle we can pass a number in here let's say for x we can start with 40 and for y i think i went with 480. and now let's try this and we are getting an error because i made a typo there we go this is looking much better so now we have one obstacle doesn't do anything yet but that we're going to cover later on but this is going to be our first obstacle so alrighty with that we have our first one and now what you could be doing is to run this function four times and figure out the positions by yourself and that would be an approach but not an approach i would like because that seems like a lot of writing instead what i have done is i created another function and this i have called create multiple obstacles and first of all here we are going to need self then besides that i want to pass in the x start and this would be the same x start we have used here i just want to be able to call it from this function because literally what we are going to do is i'm going to call this function only inside of this method here i am not going to call it inside of the init method directly and i also want to do the same for y start and next up i have to get some kind of offset so i have to know what the distance between each of the obstacles is going to be and in my case i don't actually know how many obstacles i am going to have so i want to use a parameter that i can later use to add as many arguments as i want and this is going to look like this i use a star and then offset and that's called another packing operator and that's going to be super useful and now what i want to do in this function is for x in offset so all the arguments we are going to pass in here we are going to loop over and then in here we are going to run self dot create obstacle and now we have to pass in a couple of arguments the first two are the easy ones because we already have our size and our color so for now we have to figure out these two arguments to pass in here and well they're not that hard because we already have x start and we have y start but now there's one more thing we have to do that we have to get the offset in here as well so what i want to do is to add my x variable in here as well so the one we have just looped over but for that to work we have to add another argument in here and that i called off set x and actually let me name things properly so instead of for x is offset x and then we place all of this in here so this should make this a bit easier to read and with that we have the two functions we need to make all of this work so now inside of my init class i want to create multiple obstacles and again let me copy the parameters we need to work through this one by one first of all we will need the x start and let me just place a zero in here so we start the left side of the screen for y start i just want to go with 480 because i think that number worked quite well and now for the offset for now let me just place in a couple of random numbers so let's start with 0 then 100 and then 200 and there's one thing i did forget and that is this line here that i want to add my offset x and let me quickly go through this line because that is the really important one so essentially what happens in here is this line is the really important bit because this is what we are going to use to check what position we are on inside of our shape and then we are going to offset this position twice first of all we are going to get a start position so this is going to be how far we are from the left and then plus that we are going to create another offset and this ensures that if we have multiple obstacles they're not all on the same position because if we didn't include this line here they would all be on the same position but we want to use this information here to offset each obstacle so the first one is going to start at 0 but then the second one is going to be 100 pixels further to the right and the next one is going to be 200 pixels further to the right and that way we can get all of our obstacles so let's try this and we are getting an error message because i made a silly mistake that this here should not be in a tuple it should just be numbers by themselves because this offset already places them in a tuple so now let's try this again and now we can see all of our obstacles obviously not in the right position and we are missing one but we can work on that and i think right now this line here is very difficult to read so instead i want to use named arguments in here but we can't use positional arguments after named arguments so i want to move this x start and y start after the offset so i also want to move those two to the end and now i can call them x start and i can call them y start and all the stuff with in the beginning is just going to be the offset and let's try this again and it's still working just fine so now what we do have to figure out is how to space our obstacles in the right amount and this again i want to be flexible so we can make the screen larger or smaller and it shouldn't really matter and i also want to have flexibility over how many obstacles i am going to have so i'm going to add another attribute that i called obstacle amount and i just went with four and now with that information i'm going to create another attribute that i have called obstacle x positions and now we have to figure out how to get these positions here that space out the entire screen and let's start building this thing up step by step so first of all i want to get num for num in range and this is going to be self dot obstacle amount so for now we would get a list of the numbers 0 1 2 and 3 because our highest number is 4 and we're just looping over it and well all we have to do now with that is to multiply that number with our screen width divided by self dot obstacle amount and that is a slightly longer line now but let me go over what's going to happen here we're going to start with this number and this number is either going to be 0 1 2 or three and for the sake of an example let's assume that this number here is two then we're going to multiply all of this with screen width divided by obstacle amount and screen width in our case is going to be 600 and obstacle amount is going to be four and if we divide one by the other we get a hundred and fifty and if you multiply this by two we get the position of 300 which is exactly in the middle of the screen and then let me use a different color if we had three in there we would also multiply this by 150 and we would get 450 so a nice offset to the right and well with that we can just pass in self dot obstacle x positions in here however right now this would not be working because this is going to be a list and we just have to pass in individual numbers so what i have to use again is the unpacking operator so this star here means that we're going to unpack this list straight away and well let's try this and now we are getting much better so now we have all of our obstacles nicely spaced out so now the final problem is that we have no offset on the left and well for that we have our x start not this one we have this x start or this one here that is zero for now and for this one i gotta be honest i was a little bit lazy because all i did i went with screen with divided by 15 and if you run this it kind of looks okay i'm not sure if the math is entirely correct you would have to look at it in a bit more detail but i think this looks good enough but this probably isn't pixel perfect but i think this works just fine so with that we have our obstacles and now we can use that knowledge to place all of our aliens and this is also going to happen with a nested for loop that uses the enumerate method i'll buy in a slightly different way but the broad strokes are essentially the same and well there's not much point explaining all of this in too much detail let's implement it straight away so here i am back in the code and the first thing i want to do is to create a new file that i am going to save as alien dot pi and in here again i want to import pi game and create a new class that i have called alien this one again is going to be a sprite so we have to do the inheritance thing we need a niche with a space in between it needs self for now and then we need super and under init all right and again we need an image and we need a rectangle and in here each image is going to be a graphic we are going to import so what we first have to figure out is a file path so let's just call it file path and here's what i did whenever we create an alien we have to specify a color so this could be red yellow or green and then in the folder i have named each file either red green or yellow and what that allows us to do so i go up a folder and then i go to graphics and then i am going to add my color to it and then i'm going to add the file ending with dot png so if i were to write the string right in here we would get graphics slash red dot png which is exactly what we need for our file path and that way i don't have to work with different if statements and our code stays nice and clean and now i can use that file path to use pygame dot image dot load add in my file path and then convert alpha the same thing we have seen so many times by now and then for the rectangle i want to get myself dot image dot get wrecked and here again i want to place the top left and this again is going to be x and y so we have to add x and y in here as well and that is going to be our basic alien so this is a pretty good start and all right so with that we can return to our main file and start working with the align and first of all for that we have to import it so from alien import alien and now in the indent method i want to create a couple of basic setup points so let's add a comment with alien setup and in here for now i am only going to need a single thing and that is self.aliens and that is at pygame.sprite so all the aliens are going to be in this group and now with that i want to create a function that is going to create all of these aliens in a specific position and i call this alien set up and now let's create this function so def alien set up and for now we are only going to use self and now let me actually open the create obstacle function as well because we are effectively going to do the same thing we have done here so again i want for row index and row in enumerate and now i want some kind of row or let's call it rows for now and in there for call index and call in enumerate and for this one we want to figure out how many columns we have or calls and now we have to figure out how many rows and how many columns we want to have for our aliens and well that's going to be parameters so let's start with rows and with columns so whenever we call the function we have to specify that information and once we have that we have to again create an x and a y variable and once we have that i want to create an alien sprite that is going to be alien and let me copy the parameters we need so we need color x and y and the color is the easiest part because i have a couple of predefined ones so let's start with red and then we need x and y and once we have that i want to get myself dot aliens and add the alien sprite so now we have most of the stuff we need we just need x and y and this could actually be a really good exercise for you try to work through the logic here and see how you can use this to place the aliens on the screen and most importantly try to think about what bits of information you are going to need to enable all of this let's start with x and in here as always we are going to need our call index and the same for y we need our row index so this would give us numbers in the single digits which is well not great yet actually let me pass in specific numbers and for the rows i went with 6 and for the calls i went with 8. so this means this call index is going to be from 0 to 7 and then the row index is going to go from 0 to 5. so let's just see how this would look like right now and all we need to do for that is in our self.run function i also want to get myself.aliens.draw and do this on the screen and now let's run this code and we get an error message because this rows and this column has to be in a range function so let's put this inside of range and now let's try this again so now in the top left you can see a reddish blob and this is not an obstacle this is an alien the reason why it is looking so weird is because we have a couple of aliens on top of each other with an offset so this is what we have to start working on and here it really helps to look at our earlier grade obstacle function because for this one we had essentially four bits of information we had the column index then we had the size of each block we had the starting position and we had the offset so four pieces and right now we only have one we have the column index so we have to add a couple of bits of information here and there are two major things we need the first one is the size and the other part is the offset and we don't really need a start variable because our aliens are going to move on the screen anyway so it doesn't really matter where they are going to start but we definitely need a size and an offset both for x and y so this is what we have to create and essentially what that means is we have to create a few more parameters and the first two we need is x distance and y distance and literally all i want to do is to multiply each side with the distance we need so x distance and y distance and i think it's a good idea to add some default parameters in here so let's say for the x distance by default this should be 60 and for the y distance this should be 48 and now let's try this again and now this is looking so much better so now we have an actual proper grid and if you had more columns or more rows you could make this more dense or more widespread it's up to you but now the obvious problem is that all of our aliens are starting in the top left so that means we have to give all of this an offset so we are going to need an x offset and we are going to need a y offset and then literally all that we are going to do is to add plus x offset and plus y offset and here again i'm just going to give it some default parameters let's say for x offset we're going to go with 70 and for y offset i put in 100 and now it stress again and there we go now we have our aliens nicely set up on the screen still don't do anything but that's going to come in a second and with all of the default arguments we can have our alien setup function nice with a couple of rows and columns and we don't end up with a ton of arguments in here so this is working quite well but now we have another problem that right now all of our aliens have the same color so that's not exactly what i want and instead for different rows i want to have different colors for each alien and for that we are going to need a couple of if statements and here again the enumerate method is going to help us enormously because this is going to tell us what row we are on for example if we are on row 0 this one is going to tell us we are on row 0. so this is super helpful and this is what i need in my if statement so if row index is equal to zero so we're on the top row and if that is the case i want my alien sprite to be yellow now if that is not the case and my row index is between one and two then i want to create an alien sprite that is an alien with the color green and we again are going to need x and y those stay the same and now for all the other rows so else i want to create an alien sprite that is going to be alien with the color red and again with x and y and now let's try this and there we go now we have nice colors and let's go through what actually happens here and the really important line is this one here because this one is telling what row we are on so this row here would have the index 0 this row would have the index 1 then 2 and 3 4 and 5. and this is the information we are using down here that if we are on row zero so this one here then we want to create a yellow alien however if we are between one and two we are on those two so we want the green alien and then for any other case so three four and five we have the else statement so we create a red alien and well with that logic we have all of our aliens so this is working really nicely and with that we have our basic alien setup so now we can close this method and not worry about it ever again but now the problem is that our aliens are static so that's not particularly helpful and to move them we have to go back to our alien file and in here i want to specify an update method and this one itself and it will also need a direction and really what i want to do in here is self.direct.x plus equal direction and now we can use this function to update all of the aliens so if i go back to my main function i can let's add all of the update methods right at the beginning so self dot aliens and now here we have to specify how fast our aliens are going to go and this i think should be an attribute of the game class so let's call itself.alien direction and this we are going to create in our init method let's place it here self dot alien direction and by default it's going to be one and now let's run this and now we have all of our aliens moving to the right obviously they don't stop but that's fine so now what we have to figure out is if any of the aliens is on the right of the screen we want to change this number to negative one so all the aliens are moving to the left instead of the right and this is going to be another function over method and this i called alien position checker and here's the logic that's going to make all of this work essentially we're going to cycle through every single alien and if any of the aliens is too far to the right we are going to change the direction of all of the aliens and then the same if any of the aliens is too far to the left or smaller than zero then we are also going to change the direction again so first of all we have to get all aliens and this is going to be self dot aliens dot sprites and don't forget the brackets here they are really important we are calling a method and now we can look for alien in all aliens so this way we can look at all the sprites individually and this could be a really good exercise for you that inside of this for loop you can access direct of each alien so use that in an if statement to change the direction if any of the aliens goes outside of the screen either left or right let's start with the right side so if any of the alien dot rekt dot right so the right side of all the aliens if any of those is greater than the screen width and now inside of this function all i want to do is to get myself dot alien direction and set this to negative one and well this should now be working so in our run function we can call self.alienpositionchecker and don't forget to call it and now let's run this and there we go this is working obviously we don't have a left side so there they keep on going forever but this we can work on now because all we need is an l if statement that checks if alien.rect.left is smaller or equal to zero and if that is the case myself.alien.direction is going to be one and now aliens go left they go right and all of this is working super well cool so with that we have our left and right movement but this is only part of the movement because what i also want to achieve is any time our aliens hit either the left or the right side i want to move them downwards by a couple of pixels and that is going to be another method and i have called this one alien move down and in here i want to move for alien in self self.aliens.sprites so kind of like what i've done up here i just check all of the sprites and literally all i want to do is to get my alien and get direct and y so the y position and just that plus equal a certain distance and this distance i'm going to specify every time i run this method and there's one thing i would like to add in here and that is i want to add all of this inside of an if statement and the if statement is going to be if self dot aliens so this is only going to be run if they are aliens inside of the alien class and the reason for that is if the player shot down all of the aliens so they all disappeared we don't really want to run this anymore because it would cause an error so this is a nice way to make our code a bit less buggy but right so with that we have a distance and now we have to call this method every time our aliens hit either the left or the right side of the screen so in here self dot alien move down and the distance i moved them down was two and this function you have to call for both if statements then well let's try this and this is working quite well so now the aliens are moving down so with that we have covered all of the movement of the aliens now the last part we need for them is to give them the ability to shoot lasers and well that's going to be just another method that we are going to call once in a while and let's call it alien underscore shoot and it only needs self as always and let me minimize all the other functions because they start to get a bit annoying and that feels much better and in here i first want to start with the same if statement we used for alien move down that we only want to run this if there are aliens inside of our aliens group and if that is the case i want to select a random alien first so i want to get a random alien and for that i need the random module so all the way at the top i want to import from random import choice and this choice we can then use down here to pick one alien and this happens with self.aliens.sprites so this line here literally just selects a single alien out of all of our aliens and this we are going to use for the x and y position of the laser and we still have our laser from earlier so we can use the position the speed and the screen height so let me copy all of the parameters and let's create a laser sprite this is going to be laser and let me pass in all of the arguments we are going to need and since we're using laser we also have to import it so from laser import laser and in here we have to figure out the different arguments although screen height is literally just the screen height we have specified down here so this one is already covered next up for the position all we need is random alien dot rect.center and then we are going to need a speed and for the alien lasers i went with six but this number is basically random so now we have a laser and in here we have to figure out on what sprite group to add it and there's going to be a minor problem because we already have one group for lasers and that is this one here and this laser is inside of the player and we couldn't just add these lasers to this group here for two reasons mostly first of all it wouldn't really make sense that is bright class inside of our player shouldn't be used for the alien lasers although that is more for the logic of the code itself it would technically work however the second reason would be more important for the game itself that this laser has to be in a different group than these lasers because later on we are going to check collisions that if any of these lasers is colliding with the player and if we had to put all the lasers in one group we would cause the player to be hit every time the player spawns a laser because the laser is drawn right behind the player so this laser sprite has to be in a separate group and well what that means is that we have to create another group so self dot alien lasers and pygame dot sprite dot group and let me put it right below the other group and this is what we're going to add the laser to so self dot alien lasers dot add laser sprite and i hope the logic here makes sense that the lasers from the player and the lasers from the alien have to be separated for the simple reason that we are always spawning the laser right behind the relevant sprite so if we were to spawn a sprite right behind the player and then check for collision between all lasers and the player we would always hit the player so this would make the game unplayable and that's why we need two different groups for our lasers however now we don't really want to call this function all the time oh actually let me call this function all the time and let me demonstrate what's going to happen so self dot alien shoot then we have to self dot alien lasers dot update and then we have to draw all of the lasers so self dot alien lasers dot draw and on the screen and now if i run this you can i think see why this wouldn't be particularly fair or well it would be a little bit of a difficult game so we don't want to run the alien shoot all the time we want to have a timer in between and that is going to happen down here that we want to create a timer and this happens with let's call it alien laser and here we have to create pygame a user event plus one and then pygame.time.set timer and now we want to get the alien laser and now i want to set the time how often this is going to run i went with 800 milliseconds so we have one laser shot about once a second but again you can make the game more difficult if you reduce this number and now if event dot type is equal to the alien laser only if that is the case i want to get my game and run alien shoot so we don't run this method inside of our run function instead we only run it inside of this timer and that we can now run and now we only have a laser that looks much more reasonable we still don't have any collisions but that we can start working on but before we are going to start working on that i want to add an extra alien because a key part of space invader is to get a high score and in the original game there's this top alien coming once in a while at the top of the screen that you can try to hit for extra points and this alien i also want to include and for that we have to create another kind of class in our alien file and this i called class extra this also has to be a pygame.sprite.sprite and as always it needs a init method then we need a super init method and then we're going to do a self.image and a self.rect and to get the image we can just use the usual pygame.image.load and the file path is this one and then convert alpha as always so we are just going to import a graphic for the image but now the rectangle is going to be a little bit more difficult because this extra alien is supposed to ever come in from the left and go to the right or coming from the right and go to the left so what i want to specify is the side this thing is supposed to start on and now we have to add an if statement in here that if side is equal to right then i want to create an x that is the screen width and right now we can't access the screen with so this is going to be another parameter so screen width and this is going to be screen width and let's say plus 50 just for some bit of wiggle room and if we are going to write anything other than right we assume that the extra alien is supposed to start from the left so the x can just be minus 50. and now we have all we need for the rectangle so self.direct is going to be self.image.getrekt as always and in here i want my top left to be x and then for y i just went with 80. and this code would either spawn the extra on the left or the right side of the screen depending on what we specify in here but if we were to add this extra to our aliens we wouldn't see it because it's outside of the screen so instead i want to add a speed to it so let's give it an update function that is going to get self.rect.x plus equals self.speed so this speed is ever going to be positive or negative depending on what side we are spawning the extra alien so we have to specify it so this would be self.speed is equal to let's say negative three and then if we're moving to the right self.speed should just be free and that way if our extra alien is on the right side of the screen so this one here we are moving it to the left and if it is on the left side of the screen we are moving it to the right and that way you should be able to see it once we spawn it in the game so with that i can return to my main file and now you might be tempted let me go up you might be tempted to add the extra alien to this aliens.group but that would not be working actually let me go back and tell you why because this alien sprite has a specific update method because here we are changing the direction of all of the sprites and also in the alien move down function we are moving down all of the aliens inside of this group and this is not something we want to do with our extra alien it's just supposed to stay on the top so we couldn't really add the extra alien to the self.aliens group it just wouldn't work so instead we have to create another group and i just called this one self.extra and since we only ever want to have a single one of those on the screen this could be a group single and i guess we can move this to its own group that is a bit easier to read let's call this extra setup and now this extra alien i only want to spawn ever so often and i want to keep this one inside of the function itself although you could also add another timer to it that will also work but in my case let's do a couple of different approaches so what i want to create is self.extra spawn time and for this i just want to get a random integer let's say between 400 and 800 and for that we need to rent in method so at the top i want to get my from random import choice and rent int so now let's say this number could be 600 and literally all i'm going to do is every time we are running this after the run method we are subtracting a certain amount from this number and whenever it hits 0 we are going to spawn an extra alien so for that we have to create another method and let's call this one extra alien timer needs self and nothing else and now i just want to get myself dot extra alien spawn time minus equal one and then if myself dot extra spawn time is smaller or equal to zero and i want to get myself dot extra group so this group single and add an instance of the extra to it so this would be extra and in here we need two bits of argument we first need decide and then we need this screen width unfortunately the screen width we already have that one's easy but now we have to get the side and this one is just going to be choice and into choice we have to pass in a list that is either right or left and then finally we have to run this function in there again so we set up a new timer for the extra spawn time so essentially what happens in here is we start with a number up here and we get an extra spawn time number and let's say for the sake of the argument for now this is going to be 600 then inside of extra alien timer we're going to reduce this number by one so if we run this once we get to 599 then 588 and so on and eventually we are going to hit zero and if we hit zero we're going to run this if statement and what we're doing there on the first line is we're going to create an extra alien that whatever spawn on the left or the right side and well then we give it a screen width to tell when it's supposed to disappear and well that's all we really have to worry about in this instance and then we are going to set a new random integer so now instead of 600 let's say we could get 520 so now the next extra alien would spawn a little bit faster so the only thing we have to worry about now is to actually calling this function and let's also place it right down here so self dot extra alien timer and now before we are going to call it i forgot that we have to import it so we are from alien import alien and extra and just to test this i am going to set the spawn time for the first spawn between 40 and 80 so we don't have to wait for too long so now let's try this and oh right we're not going to see anything because we are not drawing this extra alien so in here we have to get self dot extra dot draw this has to be on the screen again and then we have to update it as well so self dot update and now let's try this and there we can see it we have a nice little extra alien on the top and if i wait a tiny bit longer this should reappear at some point there we go and now at some point we should also get one that comes from the left i think we just got not very lucky for now and yeah there we go so this is also working very well cool so with that i can minimize this function as well and we are making some really good progress so now we have all the players all the obstacles and all the aliens now we have to work on the collisions to actually turn this into a game and the collision function is going to be the longest part of this game however it's not particularly complicated because all we are really going to do is we check different sprite collisions so for the laser sprites we check with the obstacles and the aliens and for the alien lasers we also checked with the obstacles but now with the player so it should be a fairly straightforward thing to work on so let's jump into the code and let's have a look at this and here i am back and it is getting quite extensive but right at the bottom i want to add one more method and this one i just called collision checks it needs self and nothing else and now in here we have to check a couple of things and let me start with the player lasers that is probably going to be the most interesting one and first of all i want to check if there are any lasers from the player so if self dot player dot sprite dot lasers and if that is the case i want to look for laser in self.player.sprite.lasers bit of an extensive thing but well and let me spell this properly as well that tends to help and now i want to check for two different things if either of these lasers connects with the obstacle or with the aliens and let's start with the obstacle collisions and really all we need in here is if pie game dot sprite dot sprite collide and then here we want one sprite that's the laser then i want to have myself dot blocks so each block of the obstacle and then i need a third argument and that's the do kill and this one should be true because we want to destroy this block as soon as it collides with the laser and for now inside of this if statement let's just call laser for now and don't forget to run this function so we can test it properly so self dot collision checks and now let's try this and now you can see that this is indeed working and we can destroy the obstacles now obviously this isn't working as intended because our laser doesn't disappear and keeps on going and destroys all of the obstacles but this we can change very easily because all we have to do is we have to get our laser sprite and run kill on it so essentially what's going to happen here is when this if statement runs it destroys the block it's colliding with and then if that is the case we are also going to destroy the laser right inside of the if statement and if we do that now we have a well a pretty well working laser with the obstacles and you can keep on destroying individual parts and then use this in the normal way so this is literally all we needed to make the basic laser work for the player and the obstacles now next up we need the alien collisions and well for now we can just copy this entire line and change self.block to self.aliens and well that's all we have to make for change so now let's try this and we can also destroy the aliens so this is also working quite well although later on we are going to make some changes to this line when we add the score but that comes later and then we need the final one and that's the extra collision and here again i can just copy this if statement and change this to self.extra and now okay this is going to be very difficult to hit but i am quite confident that it is going to work but well in an actual game do test this a bit more but right so with that we have our basic collisions for the player now next up we have to do the exact same thing for the alien lasers and this could be a really good exercise for you that i want you guys to create the collision logic for the alien laser colliding with the obstacles and with the player i first want to check if there are any alien lasers so if self dot alien lasers is the case and if that is the case i want to loop over all of the lasers so for laser again in self.alien lasers and now for the obstacle collision i can literally just copy this line here and well that's all we had to do and let's try this and let's open alien hits and obstacle and yeah there we go this is working indeed cool now we can copy this if statement again and now we want to check the laser against self dot player and this one has to be false because we don't want to destroy the player and then here you could close the game but for now i'm just going to print dead so let's try this and let's hope i can get hit there we go this is working quite well and we can be hit by aliens so with that we have the collisions for all of the lasers and this actually wasn't all that bad although well we are going to make some additions to this but there's one more collision that we are going to need and that is between the aliens themselves and the obstacle blocks and then i guess with the player as well although if an alien collides with the player we are going to game over immediately anyway but we do have to work with that so let's call this one aliens and in here i just want to check if self let me scroll down if self.aliens so if there are any aliens on the screen i want to check for alien in self.aliens and now again i can again check this same line except now this doesn't have to be an if statement because we don't want to destroy the alien we just want to destroy the block and this shouldn't be laser this should be alien but we also want to check if an alien collides with the player so let me copy this line here so if pygame.sprite.sprite collide and we want to check between the alien and the player and if that is the case i guess then we can close the game so pygame dot quit and sys.exit and this is going to be kind of annoying to test because we would have to wait quite a while so just for now for our alien position checker i want to set the speed to negative 10 and plus 10. so now they should speed up quite a bit now our obstacles disappear and we go to game over once the aliens collide with the player so this is working cool so this is actually all we needed for the collisions wasn't actually that bad and with that we essentially have a game although not a very good game yet because we want to add a couple more things and i think the most important one for space invaders is to add a score and a live system so that's going to be what we start working on now and let's start with the lives that i guess is the easier one and i think this is best approach by just going straight into the code and going through this one by one so let's start with that so here you can see the main file again and i want to work on my knit method and in here i want to add another section and let's call this one health and score setup and in here we have to specify a couple of different things since we are starting with the lives i want self.lives and i want to have three different lives and since i want to display all of these lives as the player in the top right i also need a surface so let's call this live surface and for this we need pygame dot image dot and this is going to be the same file we have for the player this one here and then we have to again convert alpha all of this and there's one more thing that we need and that is going to be live x start position so effectively we want to place all of these lives in the top right of the screen but for that we have to know how far of an offset we need from the right side of the screen and this is what this is going to be for and in here i want to first get my screen width and from that i want to subtract a couple of things and basically what i want to get first of all is self dot live surface dot get size and this should be zero so effectively what we're doing here we are first getting our live surface so the thing we have literally just imported then i want to get getsize and this is going to give me an x and a y parameter and from that i only care about the x part so i'm going to get zero so this will ultimately get me the width of my live surface that's literally all this does so this would give me the width of one player live but i don't just want to show one i want to show two and then along with that i also want to have an offset and that i have just set to 20. and now we have a somewhat reasonable offset from the right side of the screen that we can use so with that i can close the init function and create another method all the way at the bottom and let's call this one display lives it doesn't need any arguments and in here i want to loop over my life so for life in range self dot lives however here i only ever want to display my lives minus one so let me explain what this does essentially i want to have the ability on the top right to either show two lives one lives or zero lives left but with zero lives we are still alive we just can't see any lives in the top right anymore and this is i think how the original space invaders also worked and this is why i want to have the minus one that we still have one life left but we can't see it in the top right because it's our last life so i hope that makes sense and really all we want to do in here is to use screen dot blit we want to get myself dot live surface and now we are going to need an x and a y position the y position is the very easy part so we just need eight it's a random number i have chosen because it's very close to the top but for x i'm going to put this in its own variable and first of all i want to get the left side of my starting position so self dot live x start position and that is the variable we created up here this one so this is going to tell us when our lives should start on the left side but we don't want to place all of our lives right on there so we have to add something to it and what we have to add to it is going to be for now our life so this one here and this right now would either be zero or one but with this by itself we would only move it by one or two pixels so this wouldn't be good enough by itself instead we have to multiply it by the width of the player live so again we have to get self dot live surface get the size again and again zero and this will place both of the lives right next to each other and i want to have a slight offset so let's add plus 10 to it and this should give us all of our lives so now in our run method self dot display lives and let's see what's going to happen and we are getting an error message and the reason for that is up right here getsize is a method so i just forgot the brackets and since i'm calling get size again down here you also have to add it in there now let's try it again and now this is working although the top right is a bit too close to each other and this we can avoid by putting those two inside of a bracket and now stress again and this is looking better so now we have a live system now the problem is it doesn't really work with our collisions so this is what we have to work on now that every time our players sit i want to reduce our lives by one so effectively we have to go back to our collisions and in here the important part is this if statement that i don't want to print that anymore instead i want to give self dot lives minus equal one and what we can also do while we're here is if self.lives is smaller or equal to zero then we want to end the game so pygame dot quit and says dot exit and let's try this so i hope i can get hit there it's reduced by one now we have just one life left and now you can see we can't see lives on the top and now if i get hit again we are dead so this is working very nicely and with that we have our live system now next up i also want to add a score to all of this and for that we have to make a couple of changes to a few different things we have to update our collisions and we have to update our aliens so they all have a certain kind of score so i guess let's jump straight back into the code and let's have a look at this so here i have my entire setup again and it is really getting quite extensive but for now i want to work on my init method again and in here i want to add two more attributes the first of all is self.score and by default this is zero and besides that i also want to have a font because now we have to display some text and for the font i need pygame.font.font and the font i am going to import is also in the folders and the file path towards that is this one and the font size i went with was 20. so now we have a font that we can use and that's all we needed in the ended method and now we can create another method that i have called display score it needs self and nothing else and in here we have to do three different things we have to first get a score surface so we have to render the text then we have to get a rectangle of that surface and then we have to put both of these on the screen and this could be a really good exercise for you that i want you guys to create this display score method and use it to display the score 0 on the screen for now ideally in the top left but well wherever you really want it to be all right so first of all i want to create a score surface and for that we need self.font.render and i want to create an f string that says score and then we get our self.score and for the anti-aliasing i want folds since we're using pixel art and for the color i just went with white and with that we have our surface now i want to get my score rectangle and this is going to be score surface dot get wrecked as always and in here i want to place the top left and for now let's place it at 0 and 0. although i'm going to change that in just a second and finally i just want screen dot blitz with my score surface and my score wrecked and well that's all we needed so now i can open up the run method and actually display lives should not be there it should be down here and i also want self dot display the score and i guess by now i can just delete these two comments they don't really do anything anymore and this run method has become quite extensive it might be a good idea to clean it up a tiny bit let's do that now actually so here we are drawing all of this and there we are updating everything and i guess it is a good idea to have all of the update methods in one chunk and that makes a little bit more sense yeah i think that looks a bit better so here we're updating all of the spread groups then here we are adding extra updates and here we are drawing everything let's try this now and we can see the score in the top left and it doesn't update right now but we can work on that in a second but first of all i want to work with this top left and there might be one thing you have noticed that this font is not in the top left there's a slight y offset even though we specified zero in the score and the reason for that is that the font is rather large so for the x offset i want to go with 10 and for the y with negative 10 and that's going to look like this which i think is better and well the score here is just very very tall for some reason but well that's literally all we needed now so now we have a score and the one thing we have to figure out now is how to update this number here and for that we have to go to our alien file and give each alien a score relative to that color and well that is literally just an if statement so if the color is equal to red then self.value is going to be a hundred l if color is equal to green then we have self.value is going to be 200 and if we have neither of these cases so we have else then self.value is going to be 300. so this would be the yellow aliens all the way at the top and now we can look at our collision checks in here and the two if statements you want to look at are these two and the easier one is going to be this one because there's only one extra alien so if we hit that one we want self.score plus equal 500. and that is all we needed here and let me place it above laser.kill not for any particular reason it just feels better all right but now this statement here is going to become a little bit more complex and that is because right now we are only deleting the alien once it's being hit but we don't know which alien is hit and since we don't know what alien has hit we don't know what the score is going to be so instead we have to do this slightly differently and let me actually copy all of this and what i want to find out is first i want to get all the alien let's call it aliens hit and this is going to be pigem.sprite.spread collide and it is still going to be true and this is still going to destroy the alien however now it is also going to store it in this aliens.hitlist so i can get rid of all of this and i can check if aliens hit and if that is the case i still want to get laser.kill that stays the same however now i can also loop over all the aliens so for alien in alien hit and then self.score is simply going to be plus equal alien dot value oh and this should be for alien in alien set and now let's try this and it is working indeed i can shoot at the aliens and i get different scores and they hit the green one and the score is updating quite nicely i hope i can get to hit a yellow one and there we go so this is working very well so with that we also have our score and that is basically finishing up the game by itself so let me close everything and i think what i really want you guys to get away from this is that none of this was particularly difficult we just added lots of simple systems on top of each other and thereby created complexity and the only two things i really want to add number one is i want to add some crt styling on top and i think that makes the game look much better although it's not strictly necessary and you might not like it at all and the second one is going to add sound and that's going to be the really easy part so i will leave that for the end but first of all i want to draw on top of this so by the end of this section our game goes from this to this but literally all we are going to do here is we are drawing some elements on top of the game and then reducing the opacity and well let's jump into the code and let's create this so here i'm back in the main game and i want to create a new class and let me minimize the game class so we have a bit more space and this class i called crt it does not inherit from anything and we just set an init method and it here i want to have self.t and this is just going to be an image so pygame.image.load and the file path for that is this one so it's in the same graphics folder and i also want to convert alpha this and you're going to see in a second what that actually does and what i'm going to do is i'm going to create a draw function that needs self and for now all we're going to do is screen dot blit blitz self.tv and it should be in the top left so 0 and 0. and now in our if statement for the main game i'm going to create crt as an instance of the crt class and then on top of our game run or below in the code crt.draw and now let's have a look what this does and there we go so really all this does is it well it draws a tv style thing around the address of the screen but now this image has a problem and let me actually demonstrate this that if i increase the width of our game now this image doesn't cover the entirety of the game which starts to look very very silly so we have to add some code here to make this wider along with the screen and let me move it back to 600. and for that we can use pygame.scale and well all we are going to need is self.tv is pygame.transform.scale and in here we need a couple of arguments the first one is the surface itself so self.tv and then we're going to need the width and the height we want this to be so in our case this is screen width and screen height and both of these have to be inside of a tuple so if i run this by itself we can't see any difference however if i make the screen wider now this is going to scale up along with it so this way our game keeps on being responsive which is quite nice but let me keep it at 600 to 600 and there's one thing that is kind of annoying right now so we can't really see the edges too well and this we can change by lowering the opacity of this surface and to do that all we need is self.tv and set alpha and then here you have that a number between 0 and 100 with 0 being invisibility and 100 being the standard value so you can see everything so let me put it to 50 and now this is looking much better and what we can even do in here because traditional crt monitors flickered a bit and i imitated that effect by using a random opacity between 75 and 90. and what is going to happen here is that every time we are running the draw method so well every frame we are changing the opacity a tiny bit which makes the entire thing look much more dynamic and i think you can see it this starts to feel better so all right this is then working quite well but now these old tvs also showed a couple of lines and for that i want to define create crt lines and let's place this above draw again we are going to need self and now we need a couple of different things first of all i want to get a line height so how tall each line is going to be and i just went with three and then we have to figure out how many lines we want to have so line amount and really all we need is this green height divided by the line height and this i want to be an integer so we can work with it a bit more easily and well all we have to do now is for a line in range line amount i want to draw the lines and for that i need a y position and well this line position is just going to be line multiplied by line height so we figure out what line we are on and then we are multiplying this by the height of each line and now we can start drawing so pygame.draw.line and here is one important thing that usually when i use this.draw i always draw on the screen however now i'm going to draw on self.tv so self.tv and the reason why i am doing that is because i want to have the same opacity for the lines and for the tv and if i put all of them on the same surface when we run this line they are all going to have the same opacity which makes the game look a bit more consistent now we are going to need our color and in my case i just went with black and now we need the start point and the end point of each line and a width the width is the easiest part i just went with one and for the start point we need a tuple and here we are going to need x and y and both of these are very easy because x is just going to be zero so the left side of whatever window we have and y is just going to be the y position and for the endpoint this is also quite easy so we need x and y again and y is just going to be the y position and x is going to be this green width and well that was literally all we needed so now i have to call self dot create crt lines and let's run this game and there we go this is looking very well so now we have a much more retro feeling game that i think is looking better than the original but you could just not run crt.draw and then if you like this one more this would also work just fine and with that we have all of our styling done now with that we have the final part of our game and that is going to be the music and the sounds and well there really isn't anything complicated now this is going to be very easy so let's go straight into the code and let's have a look at this so here we have our entire game setup and i want to look at my game class again and if you open this one you can see the init method and all the other methods we created earlier and the first thing i have to do is to import all the music let's call it audio actually and first of all i want to import the music itself so the background music and this is going to be pygame dot mixer dot sound and the file path towards that file is this one next up i want to set the volume of this file and i set it to 0.2 so it doesn't get too loud and finally i want music dot play and for the loops we want to set it to negative one so at this place forever and now if i run this you should be able to hear that so i hope this is working so this would then be the first part and this could be a really good exercise for you there's also a laser sound and an explosion sound try to import them set the volume and play them at the appropriate time all right i'm going to shorten this a bit because this is very samey so let me just copy them and here we have a laser sound and we set the volume and then we have an explosion sound and we're also going to set the volume that's literally all that's happening here so now we have to figure out when each of these sound is supposed to be played and let's start with the laser sound and the laser is going to happen every time an alien shoots so in here alien sound dot play and the explosion sound i want to play if we have a collision check between the player laser and the alien so in here and let's just play it right afterwards and let's try this although there's one part missing that if our player shoots a laser we don't hear sound and for that we have to go back into our player class and in here i also want to import the laser sound so here the literal same thing we have done earlier and now every time we're pressing the space button and we are ready i want to self dot laser sound dot play and now stress again really well however there's one thing i did forget and that is a victory message once we have destroyed all of the aliens so let's add this one really quick sorry about that but this is not a difficult thing to add and really what i want to do is if there are no more alien sprites i want to display a text that says you won that's literally all it is so let's jump straight into the game and let's fix this really fast and then we are finished here we are back in our game class and i just want to add another method and let's call it victory message it doesn't need any arguments and really all we're going to check in here is if not self.aliens.sprites so if there are no more aliens in the aliens group then we know we have one and in there all we need to do is create a victory surface and this is going to be self.font.render that says u1 needs to be false for anti-aliasing and the color is going to be white and then i also want to get a victory rectangle so we can place it in the middle of the screen and this is just going to be victory surface dot get direct and the center is going to be the screen width over 2 and then also the screen height over 2 and then finally screen dot blit victory surface and victory rectangle and then of course also don't forget to run this so self dot victory message and for that i am going to cheat ever so slightly here so what i'm going to do is in the collision check i am going to comment out laser.kill and replace it with pass whenever our play hits an obstacle and we're also going to comment it out whenever we hit an alien so that way our player is basically going to shoot [Music] so now i can uncomment all of this get rid of this and with that we have the entirety of the game so i hope that was helpful and i will see you around
Info
Channel: Clear Code
Views: 18,641
Rating: 4.975831 out of 5
Keywords:
Id: o-6pADy5Mdg
Channel Id: undefined
Length: 114min 50sec (6890 seconds)
Published: Mon Aug 02 2021
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.