Open In App

Building Space Invaders Using PyGame – Python

Improve
Improve
Like Article
Like
Save
Share
Report

In this article, we’re going to build a Space Bullet shooter game using PyGame in Python

The used file can be downloaded from here.

Approach:

  • Import the required module.
  • Initialize the pygame.
  • Create three functions:
    • isCollision(): Which tells us whether the collision has occurred or not?
    • game_over(): Which returns True or False on the basis of which the code decided if the game has ended.
    • show_score(x, y): This shows the score on the screen
  • Create an infinite loop to execute the code continuously.

Space Bullet Shooter Game 

isCollision():

It’s very simple actually. Before explaining this, we want you to take a look at the collision portion of the code inside the game loop first below:

Python3




# Collision
collision = isCollision(bullet_X, invader_X[i], bullet_Y, invader_Y[i])
if collision:
    score_val += 1
    bullet_Y = 600
    bullet_state = "rest"
    invader_X[i] = random.randint(64, 736)
    invader_Y[i] = random.randint(30, 200)
    invader_Xchange[i] *= -1


The value returned by the “isCollision()” function is stored inside the “collision” variable. The value returned by the function is either True or False based on the criteria set for collision inside the function. Let’s take a look at what is inside the isCollision() function:

Python3




def isCollision(x1, x2, y1, y2):
    distance = math.sqrt((math.pow(x1 - x2, 2)) + (math.pow(y1 - y2, 2)))
    if distance <= 50:
        return True
    else:
        return False


Explanation:

The criteria for collision set inside the function is the simplest thing as the distance between the bullet and the invader (our enemy). As you can see the formula used for calculating distance is something that every student study in their high school mathematics class. It’s the formula of the distance between two points having coordinates (x1, y1) and (x2, y2) which are being passed as parameters of the isCollision() function. 

Here, we have set the criteria that if the value of the distance is less than or equal to 50, then it means a collision has occurred. The value is chosen on the basis of the height and width of the png image used for the bullet and the invader. The value can be tweaked as per your own requirements by using the trial and error method. 

So, whenever the position of the bullet and the invader changes then the isCollision() function checks if a collision has occurred or not. That is the reason why it is being called inside the game loop. 

game_over():

Which returns True or False on the basis of which the code decided if the game has ended. For understanding the game_over() function, let’s take a look at the below snippet of code which is present inside the game loop:

Python3




# movement of the invader
for i in range(no_of_invaders):
 
    if invader_Y[i] >= 450:
        if abs(player_X-invader_X[i]) < 80:
            for j in range(no_of_invaders):
                invader_Y[j] = 2000
                explosion_sound = mixer.Sound('data/explosion.wav')
                explosion_sound.play()
            game_over()
            break


Before getting into the explanation of code, it is recommended to know about the coordinate system followed in pygame. Take a look at the image below:

 coordinate system

 coordinate system

Explanation:

So, the criteria for game over is also collision. When the y-coordinate of the invader is greater than the spaceship i.e., 450 (y-coordinate of the spaceship), and the distance between the invader and the spaceship is less than 80 then a collision occurs and the game_over() function is called followed by the explosion sound. 

The following lines will make it more clear:

Python3




# y-coordinate of invader more than 450
if invader_Y[i] >= 450:
   
        # distance between the x-coordinate of invader
        # and spaceship
        if abs(player_X-invader_X[i]) < 80:
            #....... rest code .....#


The above is checked for all the invaders present in the game which is ensured by a for-loop at the starting.

show_score(x, y):

It shows the score on the screen.

Python3




def show_score(x, y):
    score = font.render("Points: " + str(score_val),
                        True, (255, 255, 255))
    screen.blit(score, (x, y))


Explanation:

The only thing show_score() function is doing is showing the score on the screen in a proper font selected by the user.  

Every time a collision between the bullet and the invaders is happening a variable “score_val” is being incremented. This variable is then being displayed on the screen by the show_score() function as can be seen in the above code snippet.  

Below is the implementation:

Python3




import pygame
import random
import math
from pygame import mixer
 
# initializing pygame
pygame.init()
 
# creating screen
screen_width = 800
screen_height = 600
screen = pygame.display.set_mode((screen_width,
                                  screen_height))
 
# caption and icon
pygame.display.set_caption("Welcome to Space\
Invaders Game by:- styles")
 
 
# Score
score_val = 0
scoreX = 5
scoreY = 5
font = pygame.font.Font('freesansbold.ttf', 20)
 
# Game Over
game_over_font = pygame.font.Font('freesansbold.ttf', 64)
 
 
def show_score(x, y):
    score = font.render("Points: " + str(score_val),
                        True, (255,255,255))
    screen.blit(score, (x , y ))
 
def game_over():
    game_over_text = game_over_font.render("GAME OVER",
                                           True, (255,255,255))
    screen.blit(game_over_text, (190, 250))
 
# Background Sound
mixer.music.load('data/background.wav')
mixer.music.play(-1)
 
# player
playerImage = pygame.image.load('data/spaceship.png')
player_X = 370
player_Y = 523
player_Xchange = 0
 
# Invader
invaderImage = []
invader_X = []
invader_Y = []
invader_Xchange = []
invader_Ychange = []
no_of_invaders = 8
 
for num in range(no_of_invaders):
    invaderImage.append(pygame.image.load('data/alien.png'))
    invader_X.append(random.randint(64, 737))
    invader_Y.append(random.randint(30, 180))
    invader_Xchange.append(1.2)
    invader_Ychange.append(50)
 
# Bullet
# rest - bullet is not moving
# fire - bullet is moving
bulletImage = pygame.image.load('data/bullet.png')
bullet_X = 0
bullet_Y = 500
bullet_Xchange = 0
bullet_Ychange = 3
bullet_state = "rest"
 
# Collision Concept
def isCollision(x1, x2, y1, y2):
    distance = math.sqrt((math.pow(x1 - x2,2)) +
                         (math.pow(y1 - y2,2)))
    if distance <= 50:
        return True
    else:
        return False
 
def player(x, y):
    screen.blit(playerImage, (x - 16, y + 10))
 
def invader(x, y, i):
    screen.blit(invaderImage[i], (x, y))
 
def bullet(x, y):
    global bullet_state
    screen.blit(bulletImage, (x, y))
    bullet_state = "fire"
 
# game loop
running = True
while running:
 
    # RGB
    screen.fill((0, 0, 0))
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            running = False
 
        # Controlling the player movement
        # from the arrow keys
        if event.type == pygame.KEYDOWN:
            if event.key == pygame.K_LEFT:
                player_Xchange = -1.7
            if event.key == pygame.K_RIGHT:
                player_Xchange = 1.7
            if event.key == pygame.K_SPACE:
               
                # Fixing the change of direction of bullet
                if bullet_state is "rest":
                    bullet_X = player_X
                    bullet(bullet_X, bullet_Y)
                    bullet_sound = mixer.Sound('data/bullet.wav')
                    bullet_sound.play()
        if event.type == pygame.KEYUP:
            player_Xchange = 0
 
    # adding the change in the player position
    player_X += player_Xchange
    for i in range(no_of_invaders):
        invader_X[i] += invader_Xchange[i]
 
    # bullet movement
    if bullet_Y <= 0:
        bullet_Y = 600
        bullet_state = "rest"
    if bullet_state is "fire":
        bullet(bullet_X, bullet_Y)
        bullet_Y -= bullet_Ychange
 
    # movement of the invader
    for i in range(no_of_invaders):
         
        if invader_Y[i] >= 450:
            if abs(player_X-invader_X[i]) < 80:
                for j in range(no_of_invaders):
                    invader_Y[j] = 2000
                    explosion_sound = mixer.Sound('data/explosion.wav')
                    explosion_sound.play()
                game_over()
                break
 
        if invader_X[i] >= 735 or invader_X[i] <= 0:
            invader_Xchange[i] *= -1
            invader_Y[i] += invader_Ychange[i]
        # Collision
        collision = isCollision(bullet_X, invader_X[i],
                                bullet_Y, invader_Y[i])
        if collision:
            score_val += 1
            bullet_Y = 600
            bullet_state = "rest"
            invader_X[i] = random.randint(64, 736)
            invader_Y[i] = random.randint(30, 200)
            invader_Xchange[i] *= -1
 
        invader(invader_X[i], invader_Y[i], i)
 
 
    # restricting the spaceship so that
    # it doesn't go out of screen
    if player_X <= 16:
        player_X = 16;
    elif player_X >= 750:
        player_X = 750
 
 
    player(player_X, player_Y)
    show_score(scoreX, scoreY)
    pygame.display.update()


Output:

output

output

Code Explanation:

  1. The code starts with the pygame.init() function which is used to initialize the game.
  2. The screen_width and screen_height variables are set to 800 pixels wide and 600 pixels tall respectively, so that they can be displayed on a computer monitor or TV screen.
  3. Next, the code sets up a new window called “screen” using pygame’s display module.
  4. This window will have an 800 pixel width by 600 pixel height size, which is then assigned to the variable “screen”.
  5. The next line of code creates two objects: caption and icon.
  6. These objects are created in order for their respective functions later in this program to use them as needed.
  7. The caption object has text that says “Welcome to Space”, while the icon object has a picture of Earth inside it (see Figure 1).
  8. After creating these two objects, they are both assigned values using PyGame’s display module’s set_caption() function and set_icon() function respectively (see Figure 2).
  9. Finally, after setting up all of these variables we call PyGame’s display module’s set_mode() function in order for our program to start displaying on our computer monitor or TV screen (see Figure 3).
  10. The code is a basic introduction to the Pygame library.
  11. The first line of code creates an instance of pygame, which is then initialized with the following lines.
  12. The next line sets up the screen dimensions and displays it on the screen.
  13. The last two lines create an image for the caption and icon respectively.
  14. The code starts with a function called show_score.
  15. This function is used to display the score on screen.
  16. The first line of code in this function sets up some variables that will be used later in the program.
  17. The next few lines are just setting up some global variables for use throughout the game, such as playerImage and playerXchange which will be used later when drawing the spaceship and moving it around.
  18. Next comes a list of functions that are going to be used throughout the game: game_over(), draw_invader(), move_bullet().
  19. These functions are all defined below these lines of code, so they can’t really be explained here without looking at them individually.
  20. The code is to create a background sound, player image, and an invader image.
  21. The code above creates the background sound by loading data/background.wav into the mixer object.
  22. The code above creates the player image by loading data/spaceship.png into the pygame object and creating a variable called player_X with value 370 and player_Y with value 523.
  23. The variables are then used in conjunction with each other to create a new variable called player_Xchange which will be used to change the position of the spaceship on screen according to how many points have been scored by the player in that particular level.
  24. The variables are also being used for changing how far away from each other both objects are on
  25. The code starts with the player image being drawn on the screen.
  26. The invader and bullet images are then loaded into memory, and they are both drawn onto the screen.
  27. The game loop starts by setting running to true so that it will continue to run until a QUIT event is received from pygame.
  28. Then, for each event in pygame’s event queue, if it is not a quit event (meaning that it is still running), then an action will be taken based on what type of event was received: If an ENTER_FRAME or KEYDOWN events were received, then the player movement function will be called.
  29. If a KEYUP or MOUSEBUTTONDOWN events were received, then either the invader or bullet functions will be called depending on which state they’re currently in (resting or firing).
  30. The code is an example of a game loop that is used to control the player movement.
  31. The while running: section of code will continue to run until the user presses the “Quit” button on their keyboard.
  32. The pygame.event.get() function will be called every time there is an event in the game loop, and if it returns True then pygame will execute the code below it, which in this case would be a call to player().
  33. The code starts by declaring a variable called player_Xchange.
  34. This is the variable that will be used to store the change of direction of the player’s bullet.
  35. The code then declares a new function called “bullet”.
  36. Inside this function, there are two variables: bullet_X and bullet_Y.
  37. These variables represent where on screen the bullets should go when they are fired from their respective locations in space.
  38. The next line inside this function sets up an event listener for pygame events using “event.”
  39. Then, if pygame detects a key being pressed or released, it will call one of three functions depending on what key was pressed or released: KEYDOWN(), KEYUP(), or QUIT().
  40. If it detects that a key has been pressed down (KEYDOWN), then it checks to see if its value is equal to K_LEFT (if you press left) or K_RIGHT (if you press right).
  41. If so, then it stores -1 into player_Xchange and 1 into player_Xchange respectively.
  42. It also plays sound effects for both left and right presses with mixer objects’ Sound() methods as well as playing sounds with those same methods again after pressing SPACEBAR which stops all sounds currently playing in order
  43. The code is a snippet of code that illustrates how to use the arrow keys to control the movement of a player object in Pygame.
  44. The code starts by defining what the event variable will be used for.
  45. In this case, it will be used for determining if the user pressed any of the arrow keys and then which key was pressed.
  46. The next part of the code is where we define what happens when an event occurs on one of our objects.
  47. If an event occurs on our player object, it will check to see if that event is a KEYDOWN or KEYUP.
  48. If it’s a KEYDOWN, then it will determine what key was pressed and do something with that information accordingly.
  49. If it’s a KEYUP, then nothing happens
  50. The code starts by adding the change in the player position.
  51. The code then adds a for loop that iterates through all of the invaders and increases their X coordinate by changing it to player_Xchange.
  52. Next, it adds another for loop that iterates through all of the invaders and changes their Y coordinate by changing it to invader_Ychange.
  53. Finally, it adds a bullet movement section where if bullet_Y is less than 0, then its value will be changed to 600; otherwise, its value will remain unchanged at 0.
  54. The next section is called “bullet movement.”
  55. This section has two parts: one part checks whether or not there are bullets on screen; if so, then they move accordingly; otherwise, they stay still until something else happens with them (i.e., when they hit an enemy).
  56. In this case, if bullet_state is equal to “fire,” then a new instance of Bullet(x1 , y1) will be created and moved according to x2 + dx * tau / dt + dy * tau / dt .
  57. If you want more information about how these functions work together please see my blog post here: http://www.mathsisfunblog.com/2014
  58. The code attempts to move the player and the bullets.
  59. The change in the player position is calculated by adding the change in the player position with each iteration of a loop.
  60. The for loop iterates through all of the invaders and calculates their new positions based on how much they have moved from their original location.
  61. The invader’s new position is calculated by subtracting their old position from their current one, which is then multiplied by 450 to account for gravity.
  62. The code starts by declaring variables for the player and invader.
  63. Next, it declares a variable called no_of_invaders which is set to 5.
  64. The code then creates an explosion sound with the mixer object’s Sound method and plays it.
  65. Then, if there are no more invaders left on screen, the game will end.
  66. If there is still an invader on screen, then the next line of code checks whether or not that invader has been hit by a bullet yet.
  67. If so, then score will be increased by 1 point and all bullets will change their state from “on” to “rest”.
  68. Next comes collision detection where we check whether or not two objects collide with each other using isCollision().
  69. If they do collide with each other, then score is increased by 1 point and all bullets change their state from “on” to “rest”.
  70. The code will cause the player to be shot by a bullet.
  71. The code will cause an explosion sound to play and then the game will end.


Last Updated : 06 Dec, 2022
Like Article
Save Article
Previous
Next
Share your thoughts in the comments
Similar Reads