First you need to install pygame, so follow the instructions in VS code library install instructions but use:
python3 -m pip install pygame
This install PyGame onto your computer.
You will also need images and sounds for your game. I collected and made some resources for you to use:
Note that line numbers will change since you will add code in different spots. Read carefully as I try to tell you where to put your code, the line numbers don't need to match up but I will give you screenshots that provide you context of where to go.
If you run this code you will see a window open and then close, this will be the window that runs our game. To keep the window open we have to keep a loop (called a game loop) going and checking to see if we should keep the game running.
Line 1: import the pygame module
Line 4: initialize the pygame module (this starts timers, event lists, and other needed elements)
line 7: sets a variable named screen to hold our pygame display. 800 pixels wide by 600 high.
Now let's add the game loop:
Line 10: set up a boolean variable that will end our game loop when it turns false (i.e. someone quits)
Line 11: while loop to keep game running
Line 13: For loop that checks all events in PyGame. Pygame and other engines (not all) use events and systematically check all of them each frame of gameplay to see what they need to do. For this it's just to make sure when we close the window that we quit the game
Line 15: if the event is that we hit the close button on the window
Line 16: set the running to false, thus closing the game.
Line 20: updates the display with what's happening in game. Nothing is happening yet. This is needed in every game you make.
Events are checked numerous times a second (about 60 or more times a second). Events can be collisions, button presses, life counters hitting zero, or many other things. We will learn more about this towards the end.
Okay now you have a window let's add a little more to it.
If you have downloaded the images feel free to keep going, if not please go back and download them now. Add this code above your game loop since we don't want this looping.
Line 10: sets the text of the display to Space Invaders
Line 11: holds the display icon, in the brackets you need to put the file path and image name. For me it was in the sub folder Content, then the name was ufo.png. You can use any 32 by 32 png for the icon or use the one provided.
Line 12: Sets the display icon to the actual icon now that was stored in the icon variable.
Inside of the game loop I set the screen fill to a colour.
Line 28: screen is filled with an RGB value. RGB Value selector
Line 37: screen is updated each loop here. The screen update should be at the END of your game loop.
First we need to understand how coordinates work. Coordinates work with 0,0 being the top left corner of the display. The units are in pixels. When you draw an image onto the screen it will draw the image from the top left of it, so if you set a 10 by 10 image to draw at position (300, 400) it will fill the space from (300, 400) to (310, 410). So if you want to center things you will have to do some math depending on the image size.
In addition, it takes processing speed to resize images in pygame, so it's always better to edit the image outside of pygame and use it's native size.
Add the player set up before the game loop. I chose to put it after the screen, but this isn't that important as long as it's not in the game loop.
Line 14: set the player icon. I had to use the file path like I did before.
Line 16 - 17: set the player's x and y coordinate variables. These will change as the player moves around.
Line 19: make a function that draws the player:
Line 20: draws the player image onto the screen at the player coordintates. blit is what draws onto the screen.
Okay this won't work unless the function is called though, so we need to call it in our game loop.
Line 38: calls the drawing player function, we'll edit this as the player moves
Note that the player is drawn after the screen fill, this is so that the screen fill doesn't go over the player's ship.
Okay we have to add some set up before we can add player movement. The idea is that we'll make the draw function take in x and y coordinates, but the player coordinates will change if we continue to hold down a key. So as you hold the right arrow key it will change the playerX value, then pass it into the drawing function (player function) that will draw it in the new updated coordinate.
Line 19: changed the function to have 2 parameters, x and y
Line 20: blit's the player image at x and y
Line 38: passes in playerX and playerY
Note: these aren't being updated yet, now we need to take in keypresses. Once we do then we can update player positions.
First add speed for the player to tell how fast they're going to go. This will change with keypresses so set it to 0 to start.
Inside the Game loop, inside the event checking loop is where we need to add our controls.
Line 39: Checks if a key is pressed
Line 40: Check if it's the left arrow key
Line 41: if it is, then it sets the player speed to be -0.3, meaning it will move left
Line 42: Check if the right arrow key is the one being pressed
Line 43: if it is, then it sets the player speed to be 0.3, moving them right
We need to account for if they release the key though, so
Line 46: checks if a key is released
Line 47: checks if the key is left or right arrow key
Line 48: if either, then if it's released it sets the player speed back to 0 to stop it from moving.
Here it is in action:
The problem with programming without delta time is that your program will run as fast as it can, so if you run it on an older machine then it will feel sluggish and won't move or play consistent on older hardware. To prevent this from being an issue we need to use delta time (the time between frames draw) to calculate how far the ship should move. Here's what you need:
Import time, this will help us time the difference between frames.
Line 7: set a variable so we can easily set the framerate.
Line 8: set the clock variable so we can access it later to set frame rate.
Line 9: set prevTime to the current time.
Line 33: Sets the framerate to 60 frames per second (this may work better with a lower number on older computers, all you have to do is change the FPS value)
Line 36: takes current time
Line 37: calculates the time between frames (a small fraction around 1/60, but can change depending how demanding your game is)
Line 38: sets the previous time to the time of the last frame.
Line 54: updated playerSpeed to be -360 * deltaTime
Line 56: updated playerSpeed to be 360 * deltaTime
You can play with the velocity here but I found 360 to be fast paced and a bit more interesting. It has to be much higher because now the code is locked at 60 frames a second so it's only moving 6 pixels per frame on average because 360 * 1/60 = 6.
For our enemy we don't want them spawning in the same spot, otherwise the player could easily break the game. We need to import random.
Next we need to set up the enemy icon like how we do with the player icon.
Added enemy set up after player but before the game loop
Line 29: set up the enemy image
Line 30-31: set the enemy coordinates to random numbers. This is to avoid making the game too predictable.
Line 32: set enemy speed to 0... for now.
Line 34-35: made the enemy draw function so that it takes in x and y coordinates
Inside the game loop after updating the player I added a section to update the enemy
Line 79: draws the enemy after the player
We want the enemy to move left and right but when it hits the boundary we want it to move forward a bit. Let's focus on making one enemy work first, then we can focus on making multiple enemies.
Math alert: Line 32 uses framerate math.
Line 32: update the enemy speed, change it to 6 pixels every 60 frames, if the frame rate is slower then it will scale. For example if we half the frames the enemy has to travel twice as fast to cover the same distance, so it should be 12 pixels every frame. So 12 = 6 *(60 /30) which is true.
Go into the game loop in the Update Enemy section
Line 79: moves the enemy left or right depending on the speed.
Line 80: if the enemy tries to move too far left off the screen...
Line 81: keeps enemy from moving off the screen
Line 82: Starts moving the enemy away from the left wall
Line 83: advances the enemy to the player
Line 84: If the enemy hits the right wall
Line 85: prevents them from leaving the screen
Line 86: sends them back towards the left wall
Line 87: moves the enemy to the player
We need to first set up the missile, this is very similar to the player and enemy that we did prior so I'll only comment on the newer stuff. This was added above the game loop.
Line 39: set the x coordinate to 0, it actually doesn't matter because this will be overwritten
Line 40: set the missile y coordinate to the same as the player's
Line 41: set the missile speed to 360*(60/FPS) which is fast compared to the enemies and player, we want it to move fast. Feel free to edit this
Line 42: stores if the missile is ready or not to be fired.
Line 44: made a function to fire the missile
Line 45: needs the boolean to be global so that we can check if we can fire another one.
Line 46: sets missle ready to false since it is firing
Line 47: draws the missile centered in front of the player (hence the +16)
Okay let's add some controls and draw it out.
Inside the game loop and inside the event loop we need to add a control to fire the missile.
Line 78: if space is pressed
Line 79: set missile x coordinate to the same as the player's once fired
Line 80: draws the missile and sets the missileReady to false so we can't fire until it's ready again.
Okay this will let us fire once, but it won't draw it leaving the screen. It will only stay on screen 1 frame (1/60 of a second)
This was added below the enemy update section.
Line 103: check if the missile is firing
Line 104: update the missile location
Line 105: draws the missile again using the fireMissle function
Line 106: if the missile goes off screen
Line 107: we reset it's Y value to the same as the player's
Line 108: the missile is ready to fire again
We can now fire missiles that will go off screen, but they don't detect collision yet.
Note: There's a bug that I will leave for you to fix. You must update the speed of the missle inside the game loop in case there's a performance hiccup. Set the speed to 360 * deltaTime * FPS
Note: I didn't update the line numbers in this section. If the line numbers are different from your code because your functions are in different spots that's okay. You don't need to have it in the exact same line number, just make sure it's in the right contextual place.
For this we need to understand a little bit of math. We don't need to know how it works, just what the equation for distance is:
We can use this to calculate if the bullet is close enough to touching the enemy now.
This was added right above the Game Loop
Line 3: imported math
Line 46: Made the is collision function, it takes 2 x values and 2 y values and calculates distance
Line 47: use the distance formula to figure out how far apart the missile is from the enemy
Line 48: If the distance is less than 27
Line 49: then return true
Line 50- 51: else return false
Line 53: set up a score counter.
Line 108: check if there's a collision
Line 109-110: if there is, then reset the missile
Line 111: add one to the score because you hit the enemy
Line 113-114: Respawns the enemy in a random location
This was placed above the game Loop
Line 55: set the font to the one included with pyGame
Line 57-58: set the coordinates for where I want the font and stored it in a variable
Line 60: made a show score function
Line 61: renders what the font will look like, set the string, then True for showing it, then set the font colour to white
Set inside the game loop near the end,
Line 125: shows the text with the score on screen now
Line 4: import the mixer from pygame
Line 12: load the music (similar to how you load the images)
Line 13: plays the music, if you want it to loop you put -1 in the brackets
Line 88: if the space bar is pressed it plays the laser sound effect
Line 123: if there's a collision is plays the explosion sound effect