M
M
max_1O2021-06-09 10:35:36
Python
max_1O, 2021-06-09 10:35:36

How to make infinite shooting in pygame?

Hello everyone. I am writing a game like alien invasion using pygame. I can’t implement it so that when the player presses and holds the Space button pressed, the bullets shoot endlessly ... I tried it in cycles, there all the bullets are fired at once as one (I checked with print (len(), there are as many bullets as I set). I checked with time.sleep(), so in general the ship also stopped and the idea did not work out at all. I googled, but there are other answers to other questions. I will be glad for the help
Main module

import pygame
from pygame.sprite import Group

from settings import Settings
# module controlling ship,bullet,alien speed,
# amount of aliens, bullets etc.

import game_functions as gf
# module controlling changes coords of things in screen in every loop

from ship import Ship
# module of class Ship


def run_game():

    pygame.init()
    settings = Settings()
    screen = pygame.display.set_mode((0, 0), pygame.FULLSCREEN)
    ship = Ship(screen, settings)  # creating ship
    bullets = Group()  # creating tuple to store bullets

    while True:

        screen.fill(settings.bg_color)  # changing background color
        ship.blitme()  # drawing ship onto screen
        gf.check_events(ship, settings, screen, bullets)  # keyboard events
        ship.update()  # changing position of ship when player presses definite key
        gf.update_screen(bullets)  # changing coords of bullet after it's been fired
        pygame.display.flip()  # loop


run_game()  # starts game


game_functions.py
import sys
import time
import pygame

from bullet import Bullet  # class of Bullet


def check_keydown_events(event, ship, settings, screen, bullets, fire):
    if event.key == pygame.K_LEFT:
        # left arrow keydown events
        ship.move_left = True  # moves ship left when key's keeping pressed 
    if event.key == pygame.K_RIGHT:
        # right arrow keydown events
        ship.move_right = True  # moves ship right when key's keeping pressed
    if event.key == pygame.K_DOWN:
        # down arrow keydown events
        ship.move_down = True  # moves ship down when key's keeping pressed
    if event.key == pygame.K_UP:
        # up arrow keydown events
        ship.move_up = True  # moves ship up when key's keeping pressed
    if event.key == pygame.K_SPACE:
        fire = True
        # fires bullets
        if fire and len(bullets) < settings.bullets_allowed:
            # continues firing until amount of bullets reaches limit
            new_bullet = Bullet(settings, ship, screen)  # creating new bullet
            bullets.add(new_bullet)  # adding created bullet to Group


def check_keyup_events(event, ship, fire):
    # works with keydown events
    if event.key == pygame.K_LEFT:
        ship.move_left = False  # stops moving of ship to left
    if event.key == pygame.K_RIGHT:
        ship.move_right = False  # stops moving of ship to right
    if event.key == pygame.K_UP:
        ship.move_up = False  # stops moving of ship to up
    if event.key == pygame.K_DOWN:
        ship.move_down = False  # stops moving of ship to down
    if event.key == pygame.K_SPACE:
        fire = False  # stops firing bullets


def check_events(ship, settings, screen, bullets):
    """main event checker function"""
    fire = False  # it'll be true when player press space
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            sys.exit()
        elif event.type == pygame.KEYDOWN:
            # working on keydown events
            check_keydown_events(event, ship, settings, screen, bullets, fire)
            # function that checks keydown event type and works according event type
        elif event.type == pygame.KEYUP:
            check_keyup_events(event, ship, fire)
            # function that checks keyup event type and works according event type
    print(len(bullets))  # to check amount of bullets on screen


def update_screen(bullets):
    """works with bullet Group"""
    for bullet in bullets.sprites():
        bullet.update_bullet()  # changes position of bullet in every loop
        bullet.draw_bullet()  # draws bullets with new coords
        if bullet.rect.bottom < 0:
            bullets.remove(bullet)  # removes bullets reached outline of screen


settings.py
import pygame


class Settings():

    def __init__(self):
        self.screen_width = 1200
        self.screen_height = 700
        self.bg_color = (15, 38, 128)
        self.ship_speed_factor = 1.5
        self.bullet_width = 5
        self.bullet_height = 15
        self.bullet_color = 245, 15, 15
        self.bullets_allowed = 5
        self.bullet_speed_factor = 1


bullets.py
import pygame
from pygame.sprite import Sprite


class Bullet(Sprite):

    def __init__(self, settings, ship, screen):
        super(Bullet, self).__init__()
        self.settings = settings
        self.ship_rect = ship.rect  # rect of image of ship
        self.screen = screen
        self.width = settings.bullet_width
        self.height = settings.bullet_height
        self.color = settings.bullet_color
        self.rect = pygame.Rect(0, 0, self.width, self.height)  # left bullet
        self.rect1 = pygame.Rect(0, 0, self.width, self.height)  # right bullet
        # variables below makes bullets being fired from ship wings
        self.rect.x = self.ship_rect.centerx - 21
        self.rect1.x = self.ship_rect.centerx + 21
        self.rect.y = self.ship_rect.centery - 15
        self.rect1.y = self.ship_rect.centery - 15
        # variables below makes coords much more correct
        self.x = float(self.rect.x)
        self.y = float(self.rect.y)
        self.x1 = float(self.rect1.x)
        self.y1 = float(self.rect1.y)

    def draw_bullet(self):
        # draws bullets when being called
        pygame.draw.rect(self.screen, self.color, self.rect)
        pygame.draw.rect(self.screen, self.color, self.rect1)

    def update_bullet(self):
        # changes bullets coords in every loop
        # rect can't get float number, so we use variables below and return it's integer type back at the end 
        self.y -= self.settings.bullet_speed_factor
        self.y1 -= self.settings.bullet_speed_factor

        self.rect.y = self.y
        self.rect1.y = self.y1


ship.py
import pygame


class Ship():

    def __init__(self, screen, ai_settings):

        self.screen = screen
        self.settings = ai_settings
        self.image = pygame.image.load("images/ship.xcf")  # ship image
        self.rect = self.image.get_rect()  # ship rect
        self.screen_rect = self.screen.get_rect()
        self.rect.centerx = self.screen_rect.centerx  # moving ship to center
        self.rect.centery = self.screen_rect.centery  # vertical center aligning
        self.centerx = float(self.rect.centerx)  # we'll use float to be more correct
        self.centery = float(self.rect.centery)
        # variables below will be "True" or "False" according keydown events in game_functions.py
        self.move_right = False
        self.move_left = False
        self.move_up = False
        self.move_down = False

    def update(self):
        # changes ship position according keydown events in game_function.py
        if self.move_right and self.rect.right < self.screen_rect.right:
            self.centerx += self.settings.ship_speed_factor
        if self.move_left and self.rect.left > self.screen_rect.left:
            self.centerx -= self.settings.ship_speed_factor
        if self.move_up and self.rect.top > self.screen_rect.top:
            self.centery -= self.settings.ship_speed_factor
        if self.move_down and self.rect.bottom < self.screen_rect.bottom:
            self.centery += self.settings.ship_speed_factor
        self.rect.centerx = self.centerx
        self.rect.centery = self.centery

    def blitme(self):
        # draws ship onto screen
        self.screen.blit(self.image, self.rect)

Answer the question

In order to leave comments, you need to log in

1 answer(s)
O
o5a, 2021-06-09
@max_1O

The problem is that the button click event fires only when the button is clicked. So if you just hold a space, then in each subsequent cycle, the click event does not fire, but the fire flag is reset to False each cycle, so the shooting stops.
You can bind the fire flag to the ship itself as an attribute, i.e. in __init__ And in events, work with the class attribute, and not pass a separate fire variable.
self.fire = False

def check_keydown_events(event, ship, settings, screen, bullets):
    if event.key == pygame.K_LEFT:
        # left arrow keydown events
        ship.move_left = True  # moves ship left when key's keeping pressed
    if event.key == pygame.K_RIGHT:
        # right arrow keydown events
        ship.move_right = True  # moves ship right when key's keeping pressed
    if event.key == pygame.K_DOWN:
        # down arrow keydown events
        ship.move_down = True  # moves ship down when key's keeping pressed
    if event.key == pygame.K_UP:
        # up arrow keydown events
        ship.move_up = True  # moves ship up when key's keeping pressed
    if event.key == pygame.K_SPACE:
        ship.fire = True

Similarly change in button release function.
But since the click event does not fire again every cycle, then the very creation of new bullets will not work in that function, so it needs to be moved to where each cycle fires, for example, to the same check_events
def check_events(ship, settings, screen, bullets):
    """main event checker function"""
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            sys.exit()
        elif event.type == pygame.KEYDOWN:
            # working on keydown events
            check_keydown_events(event, ship, settings, screen, bullets)
            # function that checks keydown event type and works according event type
        elif event.type == pygame.KEYUP:
            check_keyup_events(event, ship)
            # function that checks keyup event type and works according event type
    print(len(bullets))  # to check amount of bullets on screen
    if ship.fire and len(bullets) < settings.bullets_allowed:
        # continues firing until amount of bullets reaches limit
        new_bullet = Bullet(settings, ship, screen)  # creating new bullet
        bullets.add(new_bullet)  # adding created bullet to Group

Didn't find what you were looking for?

Ask your question

Ask a Question

731 491 924 answers to any question