Search code examples
pythonpython-3.xpygamepygame-surface

How do I spawn multiple enemies in pygame


I am working on a game which involves the user to control a spaceship (player in code) and and shoot aliens (enemy in code) spawned randomly in the surface.

As of now, I have only successfully made one alien spawn at a time. How do I add say, 6 aliens simultaneously, that respawn (in random places of the surface) after a bullet hits it?

import pygame
import random
import math

# for initialising pygame (req for every pygame app)
pygame.init()

# making the basic window (dimensions must be written inside a tuple )
screen = pygame.display.set_mode((500, 500))

# background
background = pygame.image.load('C:/Users/aryan/Downloads/background.jpg')

# load and set the logo
logo = pygame.image.load('C:/Users/aryan/Downloads/bp.png')  # directory of logo
pygame.display.set_icon(logo)
pygame.display.set_caption("space wars")  # program name

# define a variable to control the main loop
running = True

# player
playerimg = pygame.image.load('C:/Users/aryan/Downloads/spaceship.png')
playerX = 218  # x and y coordinates of image
playerY = 350
playerxchange = 0  # this will be the change in movement in x direction of our image
playerychange = 0  # this will be the change in movement in y direction of our image


def player(x, y):
    screen.blit(playerimg, (x, y))  # blit draws our image on the surface(basically the background)
    # syntax for blit(imagename, (xcoordinate,ycoordinate))


# enemy
enemyimg = pygame.image.load('C:/Users/aryan/Downloads/enemy.png')
enemyX = random.randint(0, 464)
enemyY = random.randint(0, 30)
enemyxchange = 0.2
enemyychange = 40

# game over
overimg = pygame.image.load('C:/Users/aryan/Downloads/gameover.png')

# bullet
bulletimg = pygame.image.load('C:/Users/aryan/Downloads/bullet.png')
bulletX = 0
bulletY = 350
bulletxchange = 0
bulletychange = 1
bullet_state = "ready"              # "ready" you cant see bullet on screen
                                    # "fire" you can see bullet firing

bullets = []                        # bullets is a list that contains the coordinates of every bullet

score = 0

font30 = pygame.font.SysFont(None, 30)

    # Functions
def enemy(x, y):
    screen.blit(enemyimg, (x, y))  # blit draws our image on the surface(basically the background)
    # syntax for blit(imagename, (xcoordinate,ycoordinate))


def firebullet(x, y):
    global bullet_state
    bullet_state = "ready"
    bullets.append([x + 12, y + 6]) # Creating a new bullet


def iscollision(enemyX, enemyY, bulletX, bulletY):
    distance = math.sqrt(math.pow(enemyX-bulletX, 2)+ math.pow(enemyY-bulletY,2))     # distance formula
    if distance <= 20:
        return True
    else:
        return False

def TextScore(game):
    text2 = font30.render("Your Score is: " + str(game), True, (37, 97, 188))
    screen.blit(text2, (10, 45))



# main loop
while running:
    screen.fill((120, 120, 120))  # in order (r, g, b) . (0, 0, 0) is black (255, 0, 0) is red...
    screen.blit(background, (0, 0))
    # event handling, gets all event from the event queue
    for event in pygame.event.get():
        # only do something if the event is of type QUIT
        if event.type == pygame.QUIT:
            # change the value to False, to exit the main loop
            running = False

        # checking keystroke
        if event.type == pygame.KEYDOWN:
            if event.key == pygame.K_RIGHT:
                playerxchange += 0.3  # change in movement will be 0.2 towards the right
            if event.key == pygame.K_LEFT:
                playerxchange -= 0.3  # change in movement will be 0.2 towards the right
            if event.key == pygame.K_UP:
                playerychange -= 0.3
            if event.key == pygame.K_DOWN:
               playerychange += 0.3
            if event.key == pygame.K_SPACE:
                bullet_state = "fire"
                firebullet(playerX, playerY)
        if event.type == pygame.KEYUP:
            if event.key == pygame.K_LEFT or event.key == pygame.K_RIGHT or event.key == pygame.K_DOWN or event.key == pygame.K_UP:
                playerxchange = 0
                playerychange = 0

    playerY += playerychange
    playerX += playerxchange  # the value of playerx changes by +- 0.1 depending on keystroke

    if playerX <= -64:  # this teleports the spaceship from left end to right end
        playerX = 564
    elif playerX >= 564:  # this teleports spaceship from right end to left
        playerX = -64

    if playerY >= 436:  # this prevents spaceship from leaving vertically
        playerY = 436
    if playerY <= 0:
        playerY = 0

    # enemy movement
    enemyX += enemyxchange

    if enemyY >= 476:
        enemyY = 476
        enemyYchange = 0
        enemyXchange = 0

    if enemyX <= 0:
        enemyxchange = 0.2
        enemyY += enemyychange
    elif enemyX >= 465:
        enemyxchange = -0.2
        enemyY += enemyychange

    # bullet movement
    if bullet_state == "fire":
        firebullet(playerX, playerY)


    for bullet in bullets:
        screen.blit(bulletimg, (bullet[0], bullet[1]))  # Print a bullet
        bullet[0] -= bulletxchange  # Updates its position
        bullet[1] -= bulletychange
        if bullet[1] < 0:
            bullets.remove(bullet)

    # collision
    for bullet in bullets:       # Use a for-loop to iterate through all the bullets in the list.
        collision = iscollision(enemyX, enemyY, bullet[0], bullet[1])
        if collision:               # Test if a single bullet collides with the enemy inside the loop.
            score += 1
            print(score)
            bullets.remove(bullet)  # Remove the bullet from the list when it collides with the enemy.
            enemyX = random.randint(0, 476)     # if collision takes place, alien respawns
            enemyY = random.randint(0, 30)



    TextScore(score)
    player(playerX, playerY)  # player method is called AFTER screen.fill otherwise the screen will fill after image has been blitted
    enemy(enemyX, enemyY)

    pygame.display.update()  # necessary for events to keep updating

Solution

  • Create an Enemy class with a move and a draw method (see Classes):

    class Enemy:
        def __init__(self):
            self.x = random.randint(0, 476)
            self.y = 20
            self.moveX = 0.2
            self.moveY = 40
            
        def move(self):
            self.x += self.moveX 
    
            if self.y >= 476:
                self.y = 476
                self.moveY = 0
                self.moveX = 0
    
            if self.x <= 0:
                self.moveX = 0.2
                self.y += self.moveY
            elif self.x >= 465:
                self.moveX = -0.2
                self.y += self.moveY
    
        def draw(self):
            screen.blit(enemyimg, (self.x, self.y))
    

    Create a number of enemies and store them in a list (enemy_list ) instead of the global variables enemyX, enemyY, enemyxchange and enemyychange:

    enemyimg = pygame.image.load('C:/Users/aryan/Downloads/enemy.png')
    
    enemy_list = []
    for i in range(5):
        new_enemy = Enemy()
        enemy_list.append(new_enemy)
    

    move and draw the enemies in a loop. For the collision test, you need to check if any of the bullets hits any of the enemies. Therefore, the collision test has to be done in nested loops. Note that the loop that goes through the bullets has to be the inner loop because the bullets are removed from the list:

    # main loop
    while running:
        # [...]
    
        for enemy in enemy_list:
            enemy.move() 
    
        # [...]
    
         # collision
        for enemy in enemy_list:
            for bullet in bullets:       # Use a for-loop to iterate through all the bullets in the list.
                collision = iscollision(enemy.x, enemy.y, bullet[0], bullet[1])
                if collision:               # Test if a single bullet collides with the enemy inside the loop.
                    score += 1
                    print(score)
                    bullets.remove(bullet)  # Remove the bullet from the list when it collides with the enemy.
                    enemy.x = random.randint(0, 476)     # if collision takes place, alien respawns
                    enemy.y = random.randint(0, 30)
    
        # [...]
    
        for enemy in enemy_list:
            enemy.draw()
    

    Complete code:

    import pygame
    import random
    import math
    
    # for initialising pygame (req for every pygame app)
    pygame.init()
    
    # making the basic window (dimensions must be written inside a tuple )
    screen = pygame.display.set_mode((500, 500))
    
    # background
    background = pygame.image.load('C:/Users/aryan/Downloads/background.jpg')
    
    # load and set the logo
    logo = pygame.image.load('C:/Users/aryan/Downloads/bp.png')  # directory of logo
    pygame.display.set_icon(logo)
    pygame.display.set_caption("space wars")  # program name
    
    # define a variable to control the main loop
    running = True
    
    # player
    playerimg = pygame.image.load('C:/Users/aryan/Downloads/spaceship.png')
    playerX = 218  # x and y coordinates of image
    playerY = 350
    playerxchange = 0  # this will be the change in movement in x direction of our image
    playerychange = 0  # this will be the change in movement in y direction of our image
    
    
    def player(x, y):
        screen.blit(playerimg, (x, y))  # blit draws our image on the surface(basically the background)
        # syntax for blit(imagename, (xcoordinate,ycoordinate))
    
    class Enemy:
        def __init__(self):
            self.x = random.randint(0, 476)
            self.y = 20
            self.moveX = 0.2
            self.moveY = 40
    
        def move(self):
            self.x += self.moveX 
    
            if self.y >= 476:
                self.y = 476
                self.moveY = 0
                self.moveX = 0
    
            if self.x <= 0:
                self.moveX = 0.1
                self.y += self.moveY
            elif self.x >= 465:
                self.moveX = -0.1
                self.y += self.moveY
    
        def draw(self):
            screen.blit(enemyimg, (self.x, self.y))
    
    # enemy
    enemyimg = pygame.image.load('C:/Users/aryan/Downloads/enemy.png')
    
    enemy_list = []
    for i in range(5):
        new_enemy = Enemy()
        enemy_list.append(new_enemy)
    
    # game over
    overimg = pygame.image.load('C:/Users/aryan/Downloads/gameover.png')
    
    # bullet
    bulletimg = pygame.image.load('C:/Users/aryan/Downloads/bullet.png')
    bulletX = 0
    bulletY = 350
    bulletxchange = 0
    bulletychange = 1
    bullet_state = "ready"              # "ready" you cant see bullet on screen
                                        # "fire" you can see bullet firing
    
    bullets = []                        # bullets is a list that contains the coordinates of every bullet
    
    score = 0
    
    font30 = pygame.font.SysFont(None, 30)
    
    
    def firebullet(x, y):
        global bullet_state
        bullet_state = "ready"
        bullets.append([x + 12, y + 6]) # Creating a new bullet
    
    
    def iscollision(enemyX, enemyY, bulletX, bulletY):
        distance = math.sqrt(math.pow(enemyX-bulletX, 2)+ math.pow(enemyY-bulletY,2))     # distance formula
        if distance <= 20:
            return True
        else:
            return False
    
    def TextScore(game):
        text2 = font30.render("Your Score is: " + str(game), True, (37, 97, 188))
        screen.blit(text2, (10, 45))
    
    
    
    # main loop
    while running:
        screen.fill((120, 120, 120))  # in order (r, g, b) . (0, 0, 0) is black (255, 0, 0) is red...
        screen.blit(background, (0, 0))
        # event handling, gets all event from the event queue
        for event in pygame.event.get():
            # only do something if the event is of type QUIT
            if event.type == pygame.QUIT:
                # change the value to False, to exit the main loop
                running = False
    
            # checking keystroke
            if event.type == pygame.KEYDOWN:
                if event.key == pygame.K_RIGHT:
                    playerxchange += 0.3  # change in movement will be 0.2 towards the right
                if event.key == pygame.K_LEFT:
                    playerxchange -= 0.3  # change in movement will be 0.2 towards the right
                if event.key == pygame.K_UP:
                    playerychange -= 0.3
                if event.key == pygame.K_DOWN:
                   playerychange += 0.3
                if event.key == pygame.K_SPACE:
                    bullet_state = "fire"
                    firebullet(playerX, playerY)
            if event.type == pygame.KEYUP:
                if event.key == pygame.K_LEFT or event.key == pygame.K_RIGHT or event.key == pygame.K_DOWN or event.key == pygame.K_UP:
                    playerxchange = 0
                    playerychange = 0
    
        playerY += playerychange
        playerX += playerxchange  # the value of playerx changes by +- 0.1 depending on keystroke
    
        if playerX <= -64:  # this teleports the spaceship from left end to right end
            playerX = 564
        elif playerX >= 564:  # this teleports spaceship from right end to left
            playerX = -64
    
        if playerY >= 436:  # this prevents spaceship from leaving vertically
            playerY = 436
        if playerY <= 0:
            playerY = 0
    
        for enemy in enemy_list:
            enemy.move() 
    
    
        # bullet movement
        if bullet_state == "fire":
            firebullet(playerX, playerY)
    
    
        for bullet in bullets:
            screen.blit(bulletimg, (bullet[0], bullet[1]))  # Print a bullet
            bullet[0] -= bulletxchange  # Updates its position
            bullet[1] -= bulletychange
            if bullet[1] < 0:
                bullets.remove(bullet)
    
        # collision
        for enemy in enemy_list:
            for bullet in bullets:       # Use a for-loop to iterate through all the bullets in the list.
                collision = iscollision(enemy.x, enemy.y, bullet[0], bullet[1])
                if collision:               # Test if a single bullet collides with the enemy inside the loop.
                    score += 1
                    print(score)
                    bullets.remove(bullet)  # Remove the bullet from the list when it collides with the enemy.
                    enemy.x = random.randint(0, 476)     # if collision takes place, alien respawns
                    enemy.y = random.randint(0, 30)
    
    
        TextScore(score)
        player(playerX, playerY)  # player method is called AFTER screen.fill otherwise the screen will fill after image has been blitted
        for enemy in enemy_list:
            enemy.draw()
    
        pygame.display.update()  # necessary for events to keep updating
    

    If you want to spawn non-overlapping enemies, see the answers to the following questions: