How to Stop More Than 1 Bullet Firing At Once

How do I stop more than 1 bullet firing at once?

The general approach to firing bullets is to store the positions of the bullets in a list (bullet_list). When a bullet is fired, add the bullet's starting position ([start_x, start_y]) to the list. The starting position is the position of the object (player or enemy) that fires the bullet. Use a for-loop to iterate through all the bullets in the list. Move position of each individual bullet in the loop. Remove a bullet from the list that leaves the screen (bullet_list.remove(bullet_pos)). For this reason, a copy of the list (bullet_list[:]) must be run through (see How to remove items from a list while iterating?). Use another for-loop to blit the remaining bullets on the screen:

bullet_list = []

while run == True:
# [...]

for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False

if event.type == pygame.KEYDOWN:
if event.key == pygame.K_SPACE:
bullet_list.append([start_x, start_y])

for bullet_pos in bullet_list[:]:
bullet_pos[0] += move_bullet_x
bullet_pos[1] += move_bullet_y
if not screen.get_rect().colliderect(bullet_image.get_rect(center = bullet_pos))
bullet_list.remove(bullet_pos)

# [...]

for bullet_pos in bullet_list[:]
screen.blit(bullet_image, bullet_image.get_rect(center = bullet_pos))

# [...]

See also Shoot bullet.


The states which are returned by pygame.key.get_pressed() are, set, as long a key is hold down. That is useful for the movement of a player. The player keeps moving as long a key is hold down.

But it contradicts your intention, when you want to fire a bullet. If you want to fire a bullet when a key is pressed, then can use the KEYDOWN event. The event occurs only once when a key is pressed:

while run == True:
clock.tick(27)
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False

if event.type == pygame.KEYDOWN:
if event.key == pygame.K_SPACE:
if player1.left == True: ## handles the direction of the bullet
facing = -1
else:
facing = 1
if len(bullets) < 5: ## max amounts of bullets on screen
bx, by = player1.x + player1.width //2 ,player1.y + player1.height//2
bullets.append(projectile(bx, by, 6, black, facing))

# [...]

If you want to implement some kind of rapid fire, then the things get more tricky. If you would use the state of pygame.key.get_pressed() then you would spawn one bullet in every frame. That is far too fast. You have to implement some timeout.

When a bullet is fired, the get the current time by pygame.time.get_ticks(). Define a number of milliseconds for the delay between to bullets. Add the dela to the time and state the time in a variable (next_bullet_threshold). Skip bullets, as long the time is not exceeded:

next_bullet_threshold = 0

run = True
while run == True:

# [...]

current_time = pygame.time.get_ticks()
if keys[pygame.K_SPACE] and current_time > next_bullet_threshold:

bullet_delay = 500 # 500 milliseconds (0.5 seconds)
next_bullet_threshold = current_time + bullet_delay

if player1.left == True: ## handles the direction of the bullet
facing = -1
else:
facing = 1
if len(bullets) < 5:
bx, by = player1.x + player1.width //2 ,player1.y + player1.height//2
bullets.append(projectile(bx, by, 6, black, facing))

Minimal example: Sample Image repl.it/@Rabbid76/PyGame-ShootBullet

Sample Image

import pygame
pygame.init()

window = pygame.display.set_mode((500, 200))
clock = pygame.time.Clock()

tank_surf = pygame.Surface((60, 40), pygame.SRCALPHA)
pygame.draw.rect(tank_surf, (0, 96, 0), (0, 00, 50, 40))
pygame.draw.rect(tank_surf, (0, 128, 0), (10, 10, 30, 20))
pygame.draw.rect(tank_surf, (32, 32, 96), (20, 16, 40, 8))
tank_rect = tank_surf.get_rect(midleft = (20, window.get_height() // 2))

bullet_surf = pygame.Surface((10, 10), pygame.SRCALPHA)
pygame.draw.circle(bullet_surf, (64, 64, 62), bullet_surf.get_rect().center, bullet_surf.get_width() // 2)
bullet_list = []
max_bullets = 4
next_bullet_time = 0
bullet_delta_time = 200 # milliseconds

run = True
while run:
clock.tick(60)
current_time = pygame.time.get_ticks()
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False

if event.type == pygame.KEYDOWN:
if len(bullet_list) < max_bullets and current_time >= next_bullet_time:
next_bullet_time = current_time + bullet_delta_time
bullet_list.insert(0, tank_rect.midright)

for i, bullet_pos in enumerate(bullet_list):
bullet_list[i] = bullet_pos[0] + 5, bullet_pos[1]
if bullet_surf.get_rect(center = bullet_pos).left > window.get_width():
del bullet_list[i:]
break

window.fill((224, 192, 160))
window.blit(tank_surf, tank_rect)
for bullet_pos in bullet_list:
window.blit(bullet_surf, bullet_surf.get_rect(center = bullet_pos))
pygame.display.flip()

pygame.quit()
exit()

pygame shooting multiple bullets

To reload, just reset the shoot flag and bullet position.

Try this update:

if shoot:
draw()
if b_x > 1300: # off screen
b_x = 0
shoot=False # wait for space to re-shoot

For rapid fire, use a list to store the bullet positions. The space key adds to the bullet list. When the bullet goes off screen, remove it from the list.

import pygame
import sys

pygame.init()
bg=pygame.display.set_mode((1300,640))
FPS=pygame.time.Clock()
p=x,y,width,height=(0,0,100,100)
b=b_x,b_y,b_width,b_height=(x,y,25,25)
bullets=[] # all active bullets
shoot=False
def draw():
global bullets,bullet,b_x,shoot,m
#b_x+=50
m=b_y+50
for i in range(len(bullets)):
b_x = bullets[i] # x position of bullets
bullet=pygame.draw.rect(bg,(0,0,255),(b_x+100,m,b_width,b_height))
bullets[i] += 50 # move each bullet
bullets = [b for b in bullets if b < 1300] # only keep bullets on screen

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

if event.type==pygame.KEYDOWN:
if event.key==pygame.K_LEFT:
x-=50

if event.key==pygame.K_RIGHT:
x+=50

if event.key==pygame.K_SPACE:
bullets.append(0) # add new bullets

bg.fill((0,0,0))
player1=pygame.draw.rect(bg,(255,0,0),(x,y,width,height))
if len(bullets): # if any active bullets
draw()

pygame.display.update()
FPS.tick(30)

Is there a way to make sure that two events do not repeat too close together in pygame?

pygame.key.get_pressed() is not an event. Use the KEYDOWN event.

pygame.key.get_pressed() returns a sequence with the state of each key. If a key is held down, the state for the key is True, otherwise False. Use pygame.key.get_pressed() to evaluate the current state of a button and get continuous movement.

The keyboard events (see pygame.event module) occur only once when the state of a key changes. The KEYDOWN event occurs once every time a key is pressed. KEYUP occurs once every time a key is released. Use the keyboard events for a single action:

def main():
# [...]

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

if event.type == pygame.KEYDOWN:
if event.key == pygame.K_SPACE:
bullet_group.add(player.create_bullet())

# [...]

If you want continuous fire, in which the projectiles can be fired at a certain interval, see How do I stop more than 1 bullet firing at once?.

how to shoot bullets in pygame?

You just need to set bulletX = characterX + 20 once - when the bullet is fired. Currently you set it to that with each while run loop iteration, which is why its position is getting updated constantly. So, if you removed that, and updated the X position only when needed, this is how that part of your code could look:

if event.key == pygame.K_SPACE:
y_speed_bullet = y_speed * -1 * 2.3
bulletX = characterX + 20

Although you'll want something to keep track of if the bullet is currently fired or not, so that holding down or repeatedly pressing space doesn't reset the bullet's position.



Related Topics



Leave a reply



Submit