Pygame-Space invaders bullets

Viewed 490

I'm sorry for editing this question. I really have a hard time explaining my question because this thing is really nerve wracking. I started about 3 weeks ago and my mind is already exploding. To summarize what I want, this is the code right here:

import sys
import pygame

pygame.init()

# Setting up a window
screen = pygame.display.set_mode((1200, 800))
screen_rect = screen.get_rect()

# Caption
pygame.display.set_caption("space shooter".title())

# Setting up the icon
icon = pygame.image.load("undertake.png").convert_alpha()
pygame.display.set_icon(icon)

# Identifying a Background
bg = pygame.image.load("bg.png").convert_alpha()

# Setting up the jet
jet = pygame.image.load("jet.png").convert_alpha()
jet_rect = jet.get_rect()
jet_rect.centerx = screen_rect.centerx
jet_rect.bottom = screen_rect.bottom

# Setting up 2 bullets
bullet = pygame.image.load("pixel_laser_red.png").convert_alpha()
bullet_rect = bullet.get_rect()
bullet_state = "ready"


# Moving the jet
def move_jet(x):
    jet_rect.centerx += x


# Changing the bullet state
def fire_bullet():
    global bullet_state
    bullet_state = "fire"
    screen.blit(bullet , (bullet_x - 28 , bullet_y +7) )


# Adding Boundaries
def boundaries():
    if jet_rect.left >= 1200:
        jet_rect.right = 0
    elif jet_rect.right <= 0:
        jet_rect.left = 1200


# Game Loop
while True:
    screen.blit(bg, (0, 0))
    screen.blit(jet, jet_rect)

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

        # KeyStrokes
    pressed = pygame.key.get_pressed()
    jet_xincrement = 0
    if pressed[pygame.K_RIGHT]:
        jet_xincrement += 3
    if pressed[pygame.K_LEFT]:
        jet_xincrement -= 3

    if pressed[pygame.K_SPACE]:
        if bullet_state == "ready":
            bullet_x = jet_rect.centerx
            bullet_y = jet_rect.top
            fire_bullet()



    if bullet_state == "fire":
        bullet_y -= 3
        screen.blit(bullet, (bullet_x - 28, bullet_y + 7))
        if bullet_y < 400:
            bullet_state = "ready"


    boundaries()
    move_jet(jet_xincrement)

    pygame.display.flip()

My question: When I hold the space bar the fire_bullet function is called which changes the bullet_state to "fire" and prints it in my required position, when the bullet state is "fire" the bullet is printed each time in the loop incremented by -3 in the y direction until it reaches 400 where the bullet_state is then changed to ready, but the same bullet object is reseted I want a new bullet to shoot when the previous bullet reaches 400. I hope that makes my point clear and I'm really sorry for any confusion

2 Answers

I would recommend creating a deque list for the bullets (can also be done with an array or another list structure), and keeping track of a limitation of how many active bullets are allowed for the player at one time. Keep track of the "oldest" (first item in the list) and the latest (last item) in the list - that way you can just check if the oldest bullet has left the screen, and remove it, if it has.

There are two approaches to limiting the number of bullets on the screen, either setting a max number of allowed bullets, or controlling the interval with which the player is allowed to fire a new bullet. The latter option gives better granularity of control, of the shooting power of the player in my opinion.

Further more, it is beneficial to encapsulate game objects such as bullets, player and enemies in objects. It gives greater convenience when writing the code, and promotes readability and reusability.

To summarize, I would introduce the following variables in the game code that you have posted:

  • milliseconds_between_allowed_shots (how many milliseconds need to pass before the player can fire another shot)
  • last_shot_time (keeping track of when the last shot has been fired)
  • bullets a list structure for holding all the active bullets.

The logic would then be, if the space bar is down:

  • If the amount time since last_shot_time is greater than the minimum amount of time allowed before another shot can be fired (milliseconds_between_allowed_shots)
    • Fire another bullet, add it to the end of the list of bullets
    • Set the last_shot_time to the current time.

Here's a bit of a crude example of how to write it (this will probably not run, I have not tested it - but it should give you the idea):

import sys
import pygame
from collections import deque

pygame.init()

# Setting up a window
screen = pygame.display.set_mode((1200, 800))
screen_rect = screen.get_rect()

# Caption
pygame.display.set_caption("space shooter".title())

# Setting up the icon
icon = pygame.image.load("undertake.png").convert_alpha()
pygame.display.set_icon(icon)

# Identifying a Background
bg = pygame.image.load("bg.png").convert_alpha()

# Setting up the jet
jet = pygame.image.load("jet.png").convert_alpha()
jet_rect = jet.get_rect()
jet_rect.centerx = screen_rect.centerx
jet_rect.bottom = screen_rect.bottom

# Setting up the bullets
bullet = pygame.image.load("pixel_laser_red.png").convert_alpha()
bullet_rect = bullet.get_rect()

# deque structure for holding the current bullets
bullets = deque()
milliseconds_between_allowed_shots = 250
last_shot_time = 0

# a class for defining a bullet (could hold more, like bounds, graphics, etc.
class Bullet:
    x = 0
    y = 0

# Moving the jet
def move_jet(x):
    jet_rect.centerx += x

# fire a bullet, and push the bullet to the collection of bullets
def fire_bullet():
    global last_shot_time
    last_shot_time = pygame.time.get_ticks()
    new_bullet = Bullet()
    new_bullet.x = jet_rect.centerx
    new_bullet.y = jet_rect.top
    bullets.push(new_bullet)
    screen.blit(bullet , (new_bullet.x - 28, new_bullet.y + 7))

# Adding Boundaries
def boundaries():
    if jet_rect.left >= 1200:
        jet_rect.right = 0
    elif jet_rect.right <= 0:
        jet_rect.left = 1200

# Game Loop
while True:
    screen.blit(bg, (0, 0))
    screen.blit(jet, jet_rect)

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

    # KeyStrokes
    pressed = pygame.key.get_pressed()
    jet_xincrement = 0
    if pressed[pygame.K_RIGHT]:
        jet_xincrement += 3
    if pressed[pygame.K_LEFT]:
        jet_xincrement -= 3

    if pressed[pygame.K_SPACE]:
        current_time = pygame.time.get_ticks()
        # only allow firing a bullet, if sufficient time has passed
        if current_time > last_shot_time + milliseconds_between_allowed_shots:
            fire_bullet()

    # move all bullets, and render them
    for a_bullet in bullets:
        a_bullet.y -= 3
        screen.blit(bullet, (a_bullet.x - 28, a_bullet.y + 7))
    
    # remove oldest bullet, if it is offscreen (TODO: check list length > 0)
    if bullets[0].y <= 0
        bullets.popleft()
    
    boundaries()
    move_jet(jet_xincrement)

    pygame.display.flip()

I hope this answers your question. Let me know if you need more input, or want another type of answer. Or if it was helpful.

Use pygame.time.get_ticks() to get the time in milliseconds and define a minimum time interval (bullet_interval) for ths bullets. Set a variable which stores the time when the next bullet is allowed to be fired (next_bullet_time). Increment the time when a bullet is fired:

bullet_interval = 300 # 300 milliseconds = 0.3 seconds
next_bullet_time  = 0

while True:
    current_time = pygame.time.get_ticks()

    # [...]

    if pressed[pygame.K_SPACE]:
        if bullet_state == "ready" and current_time > next_bullet_time:
            next_bullet_time = current_time + bullet_interval

            bullet_x = jet_rect.centerx
            bullet_y = jet_rect.top
            fire_bullet()
Related