Axroxx
Axroxx

Reputation: 53

Make bullets for "space invader"

I'm trying to make projectiles for my ship in my version of space invader. I think the only thing that doesen't work right now is displaying all the projectile because I don't know how, and that's why i tried using blit which didn't work. Also I want to delay the timings between each projectile but i'm not sure how to do that. So far this is the most advanced screen-related program i've done.

import pygame
pygame.init()

class Player():
    def __init__(self):
        self.originalImage = pygame.image.load("ship.png")
        self.image = self.originalImage
        self.xposition = 480
        self.yposition = 450
        self.speed = 1

class Projectile():
    def __init__(self):
        self.originalImage = pygame.image.load("shot.png")
        self.image = self.originalImage
        self.xposition = player.xposition
        self.yposition = player.yposition
        self.speed = 1


size = width, height = 960, 540
screen = pygame.display.set_mode(size)
background = 0, 0, 0
player = Player()
projectile = Projectile()
projectiles = []
keys = pygame.key.get_pressed()

while True:
    screen.fill(background)

    for projectile in projectiles:
        if projectile.yposition < height:
            projectile.yposition -= projectile.speed
        else:
            projectiles.pop(projectiles.index(projectile))

    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            sys.exit()

    if (keys[pygame.K_RIGHT] or keys[pygame.K_d]) == True:
        player.xposition += player.speed
    if (keys[pygame.K_LEFT] or keys[pygame.K_a]) == True:
        player.xposition -= player.speed
    if (keys[pygame.K_SPACE] or keys[pygame.K_w] or keys[pygame.K_UP]) == True:
        projectiles.append(projectile)

    for projectile in projectiles:
        screen.blit(projectile.image, (projectile.xposition, projectile.yposition))

    screen.blit(player.image, ((player.xposition, player.yposition)))
    pygame.display.flip()

I edited the code after the comment but still nothing moves...

Upvotes: 1

Views: 273

Answers (2)

Rabbid76
Rabbid76

Reputation: 211116

I recommend to use pygame.sprite.Sprite. Derive Projectile from Sprite. A Sprite should have the attributes .image and .rect. Add a method which moves the Projectile and kill()s it when it goes out of bounds. e.g:

class Projectile(pygame.sprite.Sprite):

    def __init__(self):
        super().__init__() 
        self.originalImage = pygame.image.load("shot.png")
        self.image = self.originalImage
        self.rect = self.image.get_rect(center = (player.xposition, player.yposition))
        self.speed = 1

    def move(self):
        self.rect.y -= self.speed
        if self.rect.y < 0:
            self.kill()

Create a pygame.sprite.Group to contain the projectiles:

projectiles = pygame.sprite.Group()

The Sprites in a Group can be drawn by .draw().

projectiles.draw(screen)

Every time when the player fires a bullet, then a new instance of bullet has to be created and be add to the Group of bullets. I recommend to us the keyboard events for that:

for event in pygame.event.get():
    # [...]

    if event.type == pygame.KEYDOWN:
        if event.key == pygame.K_SPACE or event.key == pygame.K_w or event.key == pygame.K_UP: 
            projectiles.add(Projectile())

The states of the keys (keys = pygame.key.get_pressed()) have to be evaluated in the main application loop. The speed of the application can be controlled by the pygame.time.Clock():

import pygame
pygame.init()

class Player(pygame.sprite.Sprite):
    def __init__(self):
        self.originalImage = pygame.image.load("ship.png")
        self.image = self.originalImage
        self.rect = self.image.get_rect(center = (480, 450))
        self.speed = 1

class Projectile(pygame.sprite.Sprite):

    def __init__(self):
        super().__init__() 
        self.originalImage = pygame.image.load("shot.png")
        elf.image = self.originalImage
        self.rect = self.image.get_rect(center = player.rect.center)
        self.speed = 1

    def move(self):
        self.rect.y -= self.speed
        if self.rect.y < 0:
            self.kill()


size = width, height = 960, 540
screen = pygame.display.set_mode(size)
clock = pygame.time.Clock()
background = 0, 0, 0
player = Player()
projectiles = pygame.sprite.Group()

run = True
while run:
    clock.tick(120)

    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            run = False
        if event.type == pygame.KEYDOWN:
           if event.key == pygame.K_SPACE or event.key == pygame.K_w or event.key == pygame.K_UP: 
              projectiles.add(Projectile())

    keys = pygame.key.get_pressed()
    if (keys[pygame.K_RIGHT] or keys[pygame.K_d]) == True:
        player.rect.x += player.speed
    if (keys[pygame.K_LEFT] or keys[pygame.K_a]) == True:
        player.rect.x -= player.speed

    for projectile in projectiles:
        projectile.move()

    screen.fill(background)
    projectiles.draw(screen)
    screen.blit(player.image, player.rect.topleft)
    pygame.display.flip()

Upvotes: 1

DarklingArcher
DarklingArcher

Reputation: 262

Where is the problem:

  • Nothing is moving because pygame.key.get_pressed() is outside the game loop, ie you only check the state of keys at startup.

  • You must create a new Projectile instance each time you shoot, otherwise you'd be able to shoot only once.

  • To make the projectiles appear properly, you must blit the player sprite first, and then blit the projectiles, otherwise the player sprite will cover them.

  • To code in a delay between shots, store the shooting cooldown in a variable (preferably a player attribute), which you set to some positive value each time you shoot and decrement each turn.

  • You use sys.exit() but you haven't imported sys, which makes your game crash during exiting. Alternatively you can raise SystemExit instead, which does exactly the same thing as sys.exit() and doesn't need any imports.

Here is the corrected code:

import pygame
pygame.init()

class Player():
    def __init__(self):
        self.originalImage = pygame.image.load("ship.png")
        self.image = self.originalImage
        self.xposition = 480
        self.yposition = 450
        self.speed = 1
        self.attack_cooldown = 0   # variable to store shooting cooldown

class Projectile():
    def __init__(self):
        self.originalImage = pygame.image.load("shot.png")
        self.image = self.originalImage
        self.xposition = player.xposition
        self.yposition = player.yposition
        self.speed = 1


size = width, height = 960, 540
screen = pygame.display.set_mode(size)
background = 0, 0, 0
player = Player()
projectiles = []

while True:
    screen.fill(background)

    # decrement the current shooting cooldown
    if player.attack_cooldown > 0:
        player.attack_cooldown -= 1

    for projectile in projectiles:
        if projectile.yposition < height:
            projectile.yposition -= projectile.speed
        else:
            projectiles.pop(projectiles.index(projectile))

    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            raise SystemExit   # does the same thing as sys.exit() but you don't need to import sys

    keys = pygame.key.get_pressed()   # put this in the game loop

    if keys[pygame.K_RIGHT] or keys[pygame.K_d]:
        player.xposition += player.speed
    if keys[pygame.K_LEFT] or keys[pygame.K_a]:
        player.xposition -= player.speed
    if keys[pygame.K_SPACE] or keys[pygame.K_w] or keys[pygame.K_UP]:
        if player.attack_cooldown == 0:   # shoot only when not on cooldown
            projectiles.append(Projectile())   # shoot a new projectile instead of the one you already shot
            player.attack_cooldown = 100   # add shooting cooldown, the number can be any positive integer you want

    # blit the player first
    screen.blit(player.image, ((player.xposition, player.yposition)))
    for projectile in projectiles:
        screen.blit(projectile.image, (projectile.xposition, projectile.yposition))

    pygame.display.flip()

Upvotes: 1

Related Questions