2
votes

So I'm creating this asteroid shooting game and I have a class called Laser. Since I need to create a lot of lasers I created a Sprite Group for this class and added 25 sprites. I'm making it such that whenever the user presses the spacebar the last laser (I managed to organize it using sprite list) in the group goes up across the screen using Vector2D and the e rest of the lasers are "hidden" under the spaceship sprite. Here is the code for Laser class:

import pygame


class Laser(pygame.sprite.Sprite):
    def __init__(self, pos):
        super().__init__()
        self.image = pygame.image.load("laser.png")
        self.scale = 0.05
        self.image = pygame.transform.smoothscale(
            self.image,
            (int(self.image.get_rect().width * self.scale),
             int(self.image.get_rect().height * self.scale)),
        )
        self.rect = self.image.get_rect()
        # Getting the rectangle around the image lets you move it.
        self.rect.center = pos
        self.speed = pygame.math.Vector2(0, -5)
        self.speed.rotate_ip(0)
        self.track = 0

    def update(self, pos):
        if self.track == 0:
            self.rect.move_ip(pos)

    def move(self):
        self.rect.move_ip(self.speed)
        self.track = self.track + 1
        # self.track makes it so that the program knows not to call the update function for this Laser object.

A snippet of the code in main.py that concerns the laser shooting and creation (including all of the code would be too much):

global lasers
    player.collect((width/2, height), spriteNum)
    for i in range(0, 25):
        lasers.add(Laser((width/2, height)))
    init()
    while True:
        screen.fill([255, 255, 255])
        bigger_img = pygame.transform.scale(background.image, (width, height))
        screen.blit(bigger_img, (0, 0))
        playerPosition = player.update()
        lasers_list = lasers.sprites()
        for i in range(0, len(lasers_list)):
            lasers_list[i].update(playerPosition)
            screen.blit(lasers_list[i].image, lasers_list[i].rect)
            if lasers_list[i].rect.top < 0:
                lasers.remove(lasers_list[i])
        enemies.update(speed)
        clock.tick(60)
        last_laser = lasers.sprites()[len(lasers_list) - 1]
        player_hit = pygame.sprite.spritecollide(player, enemies, False)
        screen.blit(player.image, player.rect)
        enemies.draw(screen)
        lasers.draw(screen)
        screen.blit(last_laser.image, last_laser.rect)
        pygame.display.flip()
        for event in pygame.event.get():
            if event.type == pygame.KEYDOWN:
                if event.key == pygame.K_RIGHT:
                    player.speed[0] = 5
                if event.key == pygame.K_LEFT:
                    player.speed[0] = -5
                if event.key == pygame.K_UP:
                    player.speed[1] = -5
                if event.key == pygame.K_DOWN:
                    player.speed[1] = 5
                if (event.key == pygame.K_SPACE):
                    if  (len(lasers_list) - 1) > -1:
                        last_laser.move()
            if event.type == pygame.KEYUP:
                if event.key == pygame.K_RIGHT:
                    player.speed[0] = 0
                if event.key == pygame.K_LEFT:
                    player.speed[0] = 0
                if event.key == pygame.K_UP:
                    player.speed[1] = 0
                if event.key == pygame.K_DOWN:
                    player.speed[1] = 0
                if event.key == pygame.K_SPACE:
                    abcd = "0"
                    #nothing, I don't feel like I need anything here since no lasers would be launched if the spacebar was up. 

Ok, so the last_laser shows up if I change the code from this:

                if (event.key == pygame.K_SPACE):
                    if  (len(lasers_list) - 1) > -1:
                        last_laser.move()

to this:

                if (event.key == pygame.K_SPACE):
                    last_laser.rect.center = (400, 400)
                    # Some arbitrary x and y values (400, 400).
                    last_laser.track += 1

picture of laser showing up on the screen

But there are multiple things I want that isn't satisfied with the code up above. First of all, I want the laser to shoot out and like move across the screen (I know the movement is recoloring of pixels). Also, I want it to go to different positions because it will be reliant on where the user moves his/her spaceship sprite to and shoots from. I've been trying to find a solution for this for hours and specially made a stack overflow account for this question, any help would be greatly appreciated. :)

EDIT: The laser is being fired but now I need to figure out how the player can fire multiple lasers. What I mean by that is the player should be able to press space, wait a few seconds, then press space again and 2 lasers should be on the screen. Currently, if the player does that the laser that was on the screen is erased and then re positioned so it looks like a new laser is coming from the spaceship. This confuses the player as the laser that was just going up "disappears".

1

1 Answers

1
votes

First of all, you have all the lasers in a Sprite group, but you get all the lasers from the group and get them into a list, then loop through the list to update and draw them, then draw them again through the sprite group, which is unnecessary, rather have all sprites in Sprite group and call lasers.update() and lasers.draw(screen), or have them in a list and do what your doing now, except here

for i in range(0, 25):
    lasers.add(Laser((width/2, height)))

turn into

for i in range(0, 25):
     lasers_list.append(Laser((width/2, height)))

Theres also a lot of lines that you could shorten and look a bit cleaner

last_laser = lasers.sprites()[len(lasers_list) - 1]
last_laser = lasers.sprites()[-1] #-1 gets the last in list

if (len(lasers_list) - 1) > -1:
if len(lasers_list) > 0:

self.track = self.track + 1
self.track += 1

would also move

screen.blit(lasers_list[i].image, lasers_list[i].rect)
    if lasers_list[i].rect.top < 0:
        lasers.remove(lasers_list[i])

to the update function in the laser class

def update(self, pos):
    if self.track == 0:
        self.rect.move_ip(pos)
    screen.blit(lasers_list[i].image, lasers_list[i].rect)
        if self.rect.top < 0:
            self.kill() #deletes from sprite group

In the update function in the laser class i would add

def update(self, pos):
    if self.track == 0:
        self.rect.move_ip(pos)
    else:  #if not moving with ship
        self.move() # so it moves on its own
    screen.blit(lasers_list[i].image, lasers_list[i].rect)
        if self.rect.top < 0:
            self.kill() #deletes from sprite group

becuase of this

 if (event.key == pygame.K_SPACE):
     if len(lasers_list) > 0:
         last_laser.move()

when you press space, it will keep moving the same laser until it hits the top and gets removed. If thats what you want, its not perfect, but it will do. Otherwise you may want a counter, that counts down the lasers.

So i think that solves the second part of your question. As for the laser not showing when you press space, if you clean up your code and use use a sprite group or a list, not both, then it should work, if not, post all your code so i can recreate it.