Your problem is caused by having multiple events loops - this is almost always a bad idea. Once the code enters the Bullet
function, control is not returned to the players until the bullet leaves the screen. Obviously this is the reported problem.
It's better to store the bullets as a list of "data-points" and use some other method to draw and paint them. This new method can then be integrated into the main loop, interleaving with the existing player movement code.
In the example code below, I have created an all_bullets
list, which holds tuples defining a bullet. But what do you need to define a bullet: The (x,y) co-ordinates, next move time, and which direction it travels. So literally a bullet might be something like ( 12500, 50, 100, -5 )
. This represents: next move is after 12500 milliseconds, current position is (50, 100), and the bullet travels -5 pixels each step.
The beauty of this solution, is on-firing, it's only a matter of adding a new tuple to the list. The movement and drawing functions can maintain the list.
So to move a bullet, we only need to add the y-change value (last value in the tuple) to the current y. However it it would also be handy to "clean up" bullets that go off-screen. Since we're already moving the bullets, it's a convenient place to handle this too.
This leads us to the function moveBullets()
:
def moveBullets( bullet_list ):
""" Given a list of bullet tuples (move-time, x, y, dy)
Move the bullets along their trajectory.
If they go off-screen, delete them """
time_now = pygame.time.get_ticks()
for i in range( len( bullet_list )-1, -1, -1): # iterate in reverse so we can del() safely
next_move_time, x, y, dy = bullet_list[i] # unpack tuple
# is it time to move this bullet again?
if ( time_now > next_move_time ):
y -= dy
# Did the bullet go off-screen?
if ( x < 0 or x > Max_w or y < 0 or y > Max_h ):
# bullet has left screen, delete it it from the list
print("DEBUG: erasing bullet at index " + str( i ) )
del( bullet_list[i] )
else:
bullet_list[i] = ( time_now+bullet_speed, x, y, dy ) # save new time, and position
The most complicated part is the loop iteration in reverse. We do this because when you remove an item from a list, it changes the list ordering. Imagine we del()
list item 3, now items 4,5,6... have moved to a -1 index. But if your go in reverse, you've already processed the tail-end of the list, so it doesn't matter if they move.
The other significant part is the movement-delay. The existing code moves the bullet every 100 milliseconds. So now the code stores the time of the next-movement along with the co-ordinates. If the clock-time now is later than this timestamp, that means it is we should move the bullet (otherwise we do nothing). When moving the bullet, we store the clock-time + delay as the future-time of the next movement. The time function pygame.time.get_ticks()
returns the current time in milliseconds, so it's easy to use this with millisecond comparisons & delays.
The drawing function is similar to the moving, except we don't adjust the list, so it can be simpler.
def drawBullets( window, bullet_list ):
""" Draw all the bullets to the window """
for bullet in bullet_list:
next_move_time, x, y, dy = bullet
# draw the bullet
pygame.draw.rect( window, (255, 0, 0), ( x, y, 10, 10 ) )
For every bullet in the list, we get its (x,y) position, and use that to draw the rectangle.
So now your main loop simply adds a new Bullet tuple to all_bullets
to "fire" a bullet. Every frame it draws the bullets, and does any bullet movement.
Ref: test/example code:
import pygame
Max_w=400
Max_h=400
bullet_speed=100
def moveBullets( bullet_list ):
""" Move the bullets along their trajectory.
If they go off-screen, delete them """
time_now = pygame.time.get_ticks()
for i in range( len( bullet_list )-1, -1, -1): # iterate in reverse order so we can del() safely
next_move_time, x, y, dy = bullet_list[i]
# is it time to move this bullet again?
if ( time_now > next_move_time ):
y -= dy
# Did the bullet go off-screen?
if ( x < 0 or x > Max_w or y < 0 or y > Max_h ):
# bullet has left screen, delete it it from the list
print("DEBUG: erasing bullet at index " + str( i ) )
del( bullet_list[i] )
else:
bullet_list[i] = ( time_now+bullet_speed, x, y, dy ) # save new time, and position
def drawBullets( window, bullet_list ):
""" Draw all the bullets to the window """
for bullet in bullet_list:
next_move_time, x, y, dy = bullet
# draw the bullet
pygame.draw.rect( window, (255, 0, 0), ( x, y, 10, 10 ) )
window=pygame.display.set_mode((Max_w,Max_h))
pygame.display.set_caption("Game")
x_1=0
y_1=0
x=360
y=360
velocity=5
width=40
height=40
all_bullets = [] # list of all bullet tuples: (time, x, y)
run=True
while run:
pygame.time.delay(50)
for event in pygame.event.get():
if event.type==pygame.QUIT:
run=False
keys=pygame.key.get_pressed()
if keys[ord("w")] and y_1<Max_h-height:
y_1-=velocity
if keys[ord("a")] and x_1>0:
x_1-=velocity
if keys[ord("s")] and y_1<Max_h-height:
y_1+=velocity
if keys[ord("d")] and x_1<Max_w-width:
x_1+=velocity
if keys[ord("f")]:
# create a new bullet
all_bullets.append( ( pygame.time.get_ticks(), x_1+15, y_1+40, -5 ) )
if keys[pygame.K_LEFT] and x>0:
x-=velocity
if keys[pygame.K_RIGHT] and x<Max_w-width:
x+=velocity
if keys[pygame.K_UP] and y>0:
y-=velocity
if keys[pygame.K_DOWN] and y<Max_h-height:
y+=velocity
if keys[pygame.K_SPACE]:
# create a new bullet
all_bullets.append( ( pygame.time.get_ticks(), x+15, y-10, 5 ) )
# move the bullets
moveBullets( all_bullets )
# draw the screen
window.fill((0,0,0))
pygame.draw.rect(window,(0,0,255),(x,y,width,height))
pygame.draw.rect(window,(0,255,0),(x_1,y_1,width,height))
drawBullets( window, all_bullets )
pygame.display.update()
pygame.quit()
Note that I added f to allow the 2nd player to also fire a bullet.
You should consider renaming x
, y
and x_1
, y_1
to something like player_x
, player_y
and opponent_x
, etc. It really will make your code easier to read and therefore debug.