Space Invaders game using Python

Space Invaders game using Python

We have already developed multiple games using the turtle library and now continuing our series of projects development in Python using the turtle library, today, in this article, we are going to develop a Space Invaders game using Python.
As we all know the number of various built-in functions that a turtle library offers is more than enough to develop a moderate-level game and using those functions and methods we will start the development of our game. Before starting with the game, let’s take a look at what exactly the game is all about.

Table of Contents:

Introduction

Space Invaders is a stationary shooter in which the player fires at aliens overhead by moving a laser weapon horizontally across the bottom of the screen. As a group, the aliens travel left and right, shifting downward as they approach the screen’s edge. The objective is to shoot all of the aliens to death. The game finishes quickly if the invaders reach the bottom of the screen while the player has three lives.

Resources for Space Invaders game using Python

All the resources we are using in this project can be downloaded here.

Development rules for this game:

  • After some interval of time, the aliens should move towards the bottom.
  • When the aliens hit the bottom edge, the game should end.
  • The player’s ship can only move in the left or right direction.
  • There should be a score counter. 10 points should be added for successfully hitting the alien.

Code Flow:

Now, starting with the actual coding for this project. We will start by importing all the libraries that we will use in the development of the Space Invaders game using Python. And then for designing, we will use turtle library functions and finally the working logic of the game.

Importing all the required libraries:

import turtle
import math
import random

Explanation:
We will use all the basic libraries for this project. We all are familiar will these above libraries. turtle for graphics-related work. math for using maths functions and random to generate random numbers in a given range.

Setting up the game screen with background:

window = turtle.Screen()
window.bgcolor("green")
window.title("Space Invaders - CopyAssignment")
window.bgpic("background.gif")

Explanation:

On Line 1: On this line, we have used the screen() function that shows all the turtles that are on the screen.
Line 2: Here we have set the background color to green using the bgcolor() function.
Line 3: title() function of the turtle library is used to set the title of the window. Here we have passed the title as “Space Invaders – CopyAssignment”
Line 4: To set the background picture, we used bgpic() function.

Registering custom shapes:

Here comes the best feature of the turtle library and that is registering our own custom shapes in the turtle library from images, gifs etc. reigster_shape() function is used for this purpose.

turtle.register_shape("invader.gif")
turtle.register_shape("player.gif")

Explanation:
Line 1 & 2: We have used register_shape to register our own custom shape. We have passed two gif files for this purpose.

Adding a border to the Space Invaders game in Python:

border_pen = turtle.Turtle()
border_pen.speed(0)
border_pen.color("white")
border_pen.penup()
border_pen.setposition(-300,-300)
border_pen.pendown()
border_pen.pensize(3)
for side in range(4):
    border_pen.fd(600)
    border_pen.lt(90)
border_pen.hideturtle()

Explanation:

On Line 1: turtle.Turtle() is a constructor method and aims to return the instances of the class.
On Line 2: The speed of the turtle is set using the speed() function
Line 3: Changing the color of the pen is possible using the color() function. Here we have set the color of the pen to white color.
Line 4: penup() helps the user to pick up the pen. Many times we want to pick up the pen and don’t want to leave extra lines on the screen and so penup() is used.
Line 5: setposition() sets the position of the cursor to the desired position. It takes values in the form of X and Y coordinates.
Line 6: pendown() is the default state of the turtle. With this command, we can assure that the turtle is ready to draw.
Line 7: On this line, pensize() is used. This method helps us to set the width of the pen. It takes the arguments in the form of width.
Line 8 to 10:Here, we have used for loop that will run the commands in the loop. We have used fd and lt methods to move the turtle forward and in the left direction respectively.
Line 11: tp.hideturtle() this is used to hide the turtle and that will eventually make our design more attractive by hiding unwanted things.

Displaying score in Space Invaders game using Python:

score = 0
score_pen = turtle.Turtle()
score_pen.speed(0)
score_pen.color("red")
score_pen.penup()
score_pen.setposition(-290, 280)
scorestring = "SCORE: %s" %score
score_pen.write(scorestring, False, align="left", font=("Arial", 14, "normal"))
score_pen.hideturtle()

Explanation:
Line 1: score variable will keep track of the player’s score. here it is set to 0.
On Line 2: Here is the turtle.Turtle() will create a call function with reference to score_pen, so every time you want to access you can use it by giving the variable score_pen.
Line 3: speed() is used to control the speed of the pen.
Line 4: We use the color() function to put the color of the pen. In our case, it will be the “SCORE” whose color will be red by giving color(“red”) to the pen.
Line 5: The name itself suggests the work of the function penup(). It picks up the turtle pen and moves to a new location without leaving any tracks.
On Line 6: To set the position of the pen we use setposition() function, it will not only set the position but also make it absolute.
Line 7: String you want to write with the pen, in our case, it will be “SCORE:” will be written.
Line 8: The position of the string written in the previous line is set with the help of the write() function. The Scorestring stores the score, then the move is false in this case, the string can be aligned “left, right or centre” as per our choice and at the last, the fonts style is given the string.
Line 9: At last to hide the turtle we use hideturtle() command.

Creating the player’s ship:

player = turtle.Turtle()
player.shape("player.gif")
player.penup()
player.speed(0)
player.setposition(0,-250)
player.setheading(90)
playerspeed = 15

Explanation:
Here we have used multiple functions to create a player’s ship. The player’s ship is added using shape(player.gif). We have used the speed() function to set the ship’s speed. To set the ship’s position, we have used setposition() with X coordinate as 0 and Y coordinate as -250.

Creating an enemy and setting its position:

number_of_enemies = 10
enemies = []

for i in range(number_of_enemies):
    enemies.append(turtle.Turtle())
for enemy in enemies:
    enemy.shape("invader.gif")
    enemy.penup()
    enemy.speed(0)
    x = random.randint(-200, 200)
    y =  random.randint(100, 250)
    enemy.setposition(x, y)

enemyspeed = 5

Explanation:
Line 1 & 2: Number of enemies and an empty list of enemies.
On Line 3: for loop will run 9 times and will append turtle.Turtle() to the enemies list.
Line 4: Again a for loop.
Line 5: The shape of the enemy is set using shape()
On Line 6: penup() to pick up the pen without leaving any tracks.
Line 7: The speed of the enemies is set to 0.
On Line 8,9 & 10: Here we have used the randint function of the random library to randomly set the position of the enemy.
Line 11: Variable enemyspeed is set to 5.

Creating ship’s bullet:

bullet = turtle.Turtle()
bullet.color("white")
bullet.shape("triangle")
bullet.penup()
bullet.speed(0)
bullet.setheading(90)
bullet.shapesize(0.5,0.5)
bullet.hideturtle()

bulletspeed = 30

bulletstate = "ready"

Explanation:
The color of the bullet is set to white using color() function. To set the shape of the bullet to a triangle, we used the shape() function. And in order to set the size of the triangle, we used the shapesize() function. We declared a variable bulletspeed and assigned a value of 30. bulletstate stores the status of the bullet.

Function to move the ship left and right:

def move_left():
    x = player.xcor()
    x -= playerspeed
    if x < -280:
        x = -280
    player.setx(x)

def move_right():
    x = player.xcor()
    x += playerspeed
    if x > 280:
        x = 280
    player.setx(x)

Explanation:
move_left(): This function is capable of moving the ship to left. We used xcor() function to get the turtle’s X coordinate value of the current turtle position. To limit the movement of the turtle, we used a conditional loop.
move_right: This function helps to move the player’s ship to the right side. The remaining commands in this function are the same as the move_left function.

Function to fire bullets:

def fire_bullet():
    global bulletstate
    if bulletstate == "ready":
        bulletstate = "fire"
        # Move the bullet to the just above the player
        x = player.xcor()
        y = player.ycor() + 10
        bullet.setposition(x,y)
        bullet.showturtle()

Explanation:
fire_bullet(): This function is used to control the bullet fire.
Line 1: A global variable bulletstate is declared.
Line 2: We used a condition that checks bulletstate. If it is ready then the flow will execute the code inside the if block.
On Line 3: The first line of the if block changes the state of the bullet to fire.
On Line 4 to 5: In order to move the bullet and to give the player a functionality we used xcor and y cor to get x and y co-ordinates values.
Line 6 to 7: We used setposition to set the bullet position. The above two lines and these two lines are basically used to set the position of the bullet just above the ship.

Function to check collision:

def isCollision_enemy_bullet(t1, t2):
    distance = math.sqrt(math.pow(t1.xcor()-t2.xcor(),2)+math.pow(t1.ycor()-t2.ycor(),2))
    if distance < 25:
        return True
    else:
        return False

def isCollision_enemy_player(t1, t2):
    distance = math.sqrt(math.pow(t1.xcor()-t2.xcor(),2)+math.pow(t1.ycor()-t2.ycor(),2))
    if distance < 30:
        return True
    else:
        return False

Explanation:
isCollision_enemy_bullet(): This function is used to check the collision of the bullet with the enemy. We have used the math module to compute the distance and based upon that distance we have used the conditional statements.
isCollision_enemy_player(): This function takes care of the collision between the ship and the bullet.

Handling keyboard inputs:

turtle.listen()
turtle.onkey(move_left, "Left")
turtle.onkey(move_right, "Right")
turtle.onkey(fire_bullet, "space")

Explanation:
Line 1: turtle.listen() helps us to listen and detect when the user hit keys on the keyboard or mouse.
Line 2: We used onkey() to listen to the events. In this function, we have passed the move_left function to move the ship in the left direction.
Line 3: Again we used onkey() to listen to the events. In this function, we have passed the move_right function to move the ship in the right direction.
Line 4: This time we have passed the fire_bullet function to handle the ship’s firing.

Game logic:

Game_Over = False
missed_enemies = 0
while True:

    for enemy in enemies:
        # Move the enemy
        x = enemy.xcor()
        x += enemyspeed
        enemy.setx(x)

        # Move the enemy back and down
        if enemy.xcor() > 270:
            # Move all enemies down
            for e in enemies:
                y = e.ycor()
                y -= 40
                e.sety(y)
                if e.ycor() < -285 and Game_Over == False:
                    e.hideturtle()
                    missed_enemies += 1
                    if missed_enemies == 5:
                        Game_Over = True
                    x = random.randint(-200, 200)
                    y = random.randint(100, 250)
                    e.setposition(x, y)
                    e.showturtle()
            # Change enemy direction
            enemyspeed *= -1

        if enemy.xcor() < -270:
            # Move all enemies down
            for e in enemies:
                y = e.ycor()
                y -= 40
                e.sety(y)
                if e.ycor() < -285 and Game_Over == False:
                    e.hideturtle()
                    missed_enemies += 1
                    if missed_enemies == 5:
                        Game_Over = True
                    x = random.randint(-200, 200)
                    y = random.randint(100, 250)
                    e.setposition(x, y)
                    e.showturtle()
            # Change enemy direction
            enemyspeed *= -1

        # check for a collision between the bullet and the enemy
        if isCollision_enemy_bullet(bullet, enemy):
            # Reset the bullet
            bullet.hideturtle()
            bulletstate = "ready"
            bullet.setposition(0, -400)
            # Reset the enemy
            x = random.randint(-200, 200)
            y = random.randint(100, 250)
            enemy.setposition(x, y)
            enemyspeed += 0.5
            # update the score
            score += 10
            scorestring = "Score: %s" % score
            score_pen.clear()
            score_pen.write(
                scorestring, False, align="left", font=("Arial", 14, "normal")
            )
        # check for a collision between the player and enemy
        if isCollision_enemy_player(player, enemy):
            Game_Over = True
        if Game_Over == True:
            player.hideturtle()
            bullet.hideturtle()
            for e in enemies:
                e.hideturtle()
            window.bgpic("end.gif")
            break

    # Move the bullet
    if bulletstate == "fire":
        y = bullet.ycor()
        y += bulletspeed
        bullet.sety(y)

    # Check to see if the bullet has gone to the top
    if bullet.ycor() > 275:
        bullet.hideturtle()
        bulletstate = "ready"

Explanation:

  • In this main logic of the game, we have used a while loop for the continuous flow of the code. Inside the while loop, we used a for loop that helps in the continuous movement of the enemies.
  • We then used an if block and inside that if block we used a for loop that helps in setting the position of the enemies and regenerating the enemies. This is done by checking the value of their x coordinates (as they move from left to right). Using this conditional block we also calculate when the game needs to be over. This is done when any one of the enemy ships touches the player’s ship. This functionality is also handled inside this conditional block.
  • To check the collision between the enemies and the bullet we have used the function isCollision_enemy_bullet() and isCollision_enemy_player(). If there’s a collision between the bullet and the enemy then there should be an increase in the score and this is handled by a function named isCollision_enemy_bullet()
  • Now outside the for loop, we check for the bullet state whether it is fired or not.
  • And finally, if the bullet is fired and it has gone to the top then the state of the next bullet should be ready. In the end, we have checked this condition.

Starting the game:

turtle.done()

Explanation: In order to start the event loops turtle.done() is used. There’s no need to pass any argument in this function.

Complete code:

# importing turtle, math and random python modules
import turtle
import math
import random

# Set up the game window screen
window = turtle.Screen()
window.bgcolor("green")
window.title("Space Invaders - CopyAssignment")
window.bgpic("background.gif")

# Register the shape
turtle.register_shape("invader.gif")
turtle.register_shape("player.gif")

# Draw border
border_pen = turtle.Turtle()
border_pen.speed(0)
border_pen.color("white")
border_pen.penup()
border_pen.setposition(-300, -300)
border_pen.pendown()
border_pen.pensize(3)
for side in range(4):
    border_pen.fd(600)
    border_pen.lt(90)
border_pen.hideturtle()

# Set the score to 0
score = 0

# Draw the pen
score_pen = turtle.Turtle()
score_pen.speed(0)
score_pen.color("red")
score_pen.penup()
score_pen.setposition(-290, 280)
scorestring = "SCORE: %s" % score
score_pen.write(scorestring, False, align="left", font=("Arial", 14, "normal"))
score_pen.hideturtle()

# Create the player turtle
player = turtle.Turtle()
player.shape("player.gif")
player.penup()
player.speed(0)
player.setposition(0, -250)
player.setheading(90)

playerspeed = 15

# Choose a number of enemies
number_of_enemies = 10
# Creat an empty list of enemies
enemies = []

# Add enemies to the list
for i in range(number_of_enemies):
    # create the enemy
    enemies.append(turtle.Turtle())

for enemy in enemies:
    # enemy.color("Red")
    enemy.shape("invader.gif")
    enemy.penup()
    enemy.speed(0)
    x = random.randint(-200, 200)
    y = random.randint(100, 250)
    enemy.setposition(x, y)

enemyspeed = 5

# Creat the player's bullet
bullet = turtle.Turtle()
bullet.color("white")
bullet.shape("triangle")
bullet.penup()
bullet.speed(0)
bullet.setheading(90)
bullet.shapesize(0.5, 0.5)
bullet.hideturtle()

bulletspeed = 30

# define bullet state
# ready - ready to fire
# fire - bullet is firing
bulletstate = "ready"


# Move the player left and right
def move_left():
    x = player.xcor()
    x -= playerspeed
    if x < -280:
        x = -280
    player.setx(x)


def move_right():
    x = player.xcor()
    x += playerspeed
    if x > 280:
        x = 280
    player.setx(x)


def fire_bullet():
    # Declare bulletstate as a global if it needs changed
    global bulletstate
    if bulletstate == "ready":
        bulletstate = "fire"
        # Move the bullet to the just above the player
        x = player.xcor()
        y = player.ycor() + 10
        bullet.setposition(x, y)
        bullet.showturtle()


# For collision between enemy and bullet
def isCollision_enemy_bullet(t1, t2):
    distance = math.sqrt(
        math.pow(t1.xcor() - t2.xcor(), 2) + math.pow(t1.ycor() - t2.ycor(), 2)
    )
    if distance < 25:
        return True
    else:
        return False


# For collision between enemy and player
def isCollision_enemy_player(t1, t2):
    distance = math.sqrt(
        math.pow(t1.xcor() - t2.xcor(), 2) + math.pow(t1.ycor() - t2.ycor(), 2)
    )
    if distance < 30:
        return True
    else:
        return False


# Create keyboard bindings
turtle.listen()
turtle.onkey(move_left, "Left")
turtle.onkey(move_right, "Right")
turtle.onkey(fire_bullet, "space")

# Main game loop
Game_Over = False
missed_enemies = 0
while True:

    for enemy in enemies:
        # Move the enemy
        x = enemy.xcor()
        x += enemyspeed
        enemy.setx(x)

        # Move the enemy back and down
        if enemy.xcor() > 270:
            # Move all enemies down
            for e in enemies:
                y = e.ycor()
                y -= 40
                e.sety(y)
                if e.ycor() < -285 and Game_Over == False:
                    e.hideturtle()
                    missed_enemies += 1
                    if missed_enemies == 5:
                        Game_Over = True
                    x = random.randint(-200, 200)
                    y = random.randint(100, 250)
                    e.setposition(x, y)
                    e.showturtle()
            # Change enemy direction
            enemyspeed *= -1

        if enemy.xcor() < -270:
            # Move all enemies down
            for e in enemies:
                y = e.ycor()
                y -= 40
                e.sety(y)
                if e.ycor() < -285 and Game_Over == False:
                    e.hideturtle()
                    missed_enemies += 1
                    if missed_enemies == 5:
                        Game_Over = True
                    x = random.randint(-200, 200)
                    y = random.randint(100, 250)
                    e.setposition(x, y)
                    e.showturtle()
            # Change enemy direction
            enemyspeed *= -1

        # check for a collision between the bullet and the enemy
        if isCollision_enemy_bullet(bullet, enemy):
            # Reset the bullet
            bullet.hideturtle()
            bulletstate = "ready"
            bullet.setposition(0, -400)
            # Reset the enemy
            x = random.randint(-200, 200)
            y = random.randint(100, 250)
            enemy.setposition(x, y)
            enemyspeed += 0.5
            # update the score
            score += 10
            scorestring = "Score: %s" % score
            score_pen.clear()
            score_pen.write(
                scorestring, False, align="left", font=("Arial", 14, "normal")
            )
        # check for a collision between the player and enemy
        if isCollision_enemy_player(player, enemy):
            Game_Over = True
        if Game_Over == True:
            player.hideturtle()
            bullet.hideturtle()
            for e in enemies:
                e.hideturtle()
            window.bgpic("end.gif")
            break

    # Move the bullet
    if bulletstate == "fire":
        y = bullet.ycor()
        y += bulletspeed
        bullet.sety(y)

    # Check to see if the bullet has gone to the top
    if bullet.ycor() > 275:
        bullet.hideturtle()
        bulletstate = "ready"

turtle.done()

Output:

Space invaders game using python output

Reference:

Here’s the link to the official turtle library: Turtle Library

Endnote:

Here’s the end of the Space Invaders game using Python. We hope we were up to the mark in delivering this article. We always encourage our readers to try and make some changes to the code and modify it. This time we encourage to add sound when a bullet is fired and a different sound when an enemy is hit. You can also add a boom effect when an enemy is hit. Try adding all these features and make this game a smooth one. We promise we will be back with one another article with amazing implementation in Python. Thank you for visiting our website.


Also Read:

Share:

Author: Ayush Purawr