2

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

smci
  • 32,567
  • 20
  • 113
  • 146
HEL
  • 43
  • 4
  • not sure but I think you're looking for `pygame.key.set_repeat()` doc [here](https://www.pygame.org/docs/ref/key.html#pygame.key.set_repeat) – Lwi Jun 22 '20 at 06:47
  • 3
    Hamza, please don't [edit the title to 'UNANSWERED'](https://stackoverflow.com/posts/62508801/revisions) when you already got two answers. Just **edit the question body and/or add comments to explain succinctly why in your opinion neither of those answers is adequate**. – smci Jun 22 '20 at 09:51
  • 1
    Please strongly re-consider the design of your application. Having "**the** bullet", and storing it as ``global``, is fundamentally in conflict to creating "**a new** bullet". You may want to look into classes and instances to have well-defined entities of which multiple version can co-exist. – MisterMiyagi Jun 22 '20 at 09:59
  • 1
    Your issue is because you only have one bullet, `bullet_state, bullet_x, bullet_y` are each one single global. Don't do that, it's not OO. Create a `Bullet` class with members `x, y`. You probably don't need `state` member, because it seem to refer to the state of the jet firing rather than the bullet itself. Define methods on `Bullet` class to do whatever you need (move, etc.) Keep an array of `Bullet` instances. Move the `state` variable inside a `Plane` class for your jet. – smci Jun 22 '20 at 10:02
  • Also, there are lots of good existing Q&A on [python] OOP decomposition (e.g. for games), both here and on [CodeReview.SE](https://codereview.stackexchange.com/), please look through them, they will answer you. – smci Jun 22 '20 at 10:09
  • Strictly as AndreasL says, you can cut corners and get away without a `Bullet` object, but don't do that when you're starting to learn OOP, it just breeds bad habits and buggy code. Later on you can revisit when objects don't really need to be class instances. – smci Jun 22 '20 at 10:10

2 Answers2

2

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.

marc_s
  • 732,580
  • 175
  • 1,330
  • 1,459
Andreas Lorenzen
  • 3,810
  • 1
  • 24
  • 26
  • "linked list" in other languages is usually just `list` when working in Python. Though your comment about using a `deque` could be appropriate here, since one might also add a `time_to_live` property to the bullets, and pop them off the front of the `deque` if they have outlived their lifetime. – PaulMcG Jun 22 '20 at 13:20
  • Quite right, I edited the answer to just suggest `deque`. A `time_to_live` property could be a good idea! And generally, a lot could be done for this code - but I have tried to just focus on the topic of the question :) – Andreas Lorenzen Jun 27 '20 at 11:57
0

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()
Rabbid76
  • 202,892
  • 27
  • 131
  • 174
  • hello sorry for deleting the previous question, I thought I figured it out after I saw your code but then when I tried it today it still elongated in weird ways, I deleted it not knowing this isn't something you should do in this website and I'm really sorry for that. i rephrased the question in a much clearer way. – HEL Jun 22 '20 at 08:52
  • 1
    @HamzaEyad *"I tried it today it still elongated in weird ways, [...]"* - sorry, but then you've made a mistake. Of course I've tested the suggestion and it works fine. – Rabbid76 Jun 22 '20 at 09:03