Reputation: 11
I started learning python around 6 weeks ago and started playing around with pygame around 2 weeks ago. I'm working my way through a good book (Python Crash Course) and for an exercise am making a simple game.
On the left hand side of the screen I have a picture of a bow and arrow, that can be moved up and down and 'shot' by pressing the space bar. When the arrow is shot, the image changes to a non-drawn bow without the arrow so it kind of looks like the bow's string has been released.
I have this all working so far. What I want though is for the bow to be reloaded a certain amount of time after the arrow has been fired. So the player presses space, the arrow is shot, then a second or so later the bow is reloaded and the player can shoot again.
In particular, after the fire_bow() method has been called, after 2 seconds I want the bow_drawn flag to be set back to True.
Does anyone know how I could do this? I've read the documentation but I can't seem to find anything that would give me the desired effect. In particular I don't want to pause the program. Ideally I am imagining a method that says 'wait 1 second'.
Here's my code so far if it would be useful.
import sys
import pygame
from target_practise_settings import Settings
from bow_and_arrow import Bow
class TargetPractise:
""" Overall class to manage game assets and behaviour. """
def __init__(self):
""" Overall class to manage game assets and behavior. """
pygame.init()
self.settings = Settings()
self.screen = pygame.display.set_mode((0, 0), pygame.FULLSCREEN)
self.screen_width = self.screen.get_rect().width
self.screen_height = self.screen.get_rect().height
pygame.display.set_caption("Target Practise")
self.bow = Bow(self)
def run_game(self):
""" Start the main loop for the game. """
while True:
# Watch for keyboard and mouse events.
self._check_events()
self.bow.update()
self._update_screen()
def _check_events(self):
""" Respond to keypresses and mouse events. """
for event in pygame.event.get():
if event.type == pygame.QUIT:
sys.exit()
elif event.type == pygame.KEYDOWN:
self._check_keydown_events(event)
elif event.type == pygame.KEYUP:
self._check_keyup_events(event)
def _check_keydown_events(self, event):
""" Respond to keypresses """
if event.key == pygame.K_UP:
self.bow.moving_up = True
elif event.key == pygame.K_DOWN:
self.bow.moving_down = True
elif event.key == pygame.K_q:
sys.exit()
elif event.key == pygame.K_SPACE:
self.bow.fire_bow()
def _check_keyup_events(self, event):
""" Respond to key releases """
if event.key == pygame.K_UP:
self.bow.moving_up = False
if event.key == pygame.K_DOWN:
self.bow.moving_down = False
def _update_screen(self):
""" Update images on the screen, and flip to the new screen. """
# Redraw the screen during each pass through the loop.
self.screen.fill(self.settings.bg_colour)
self.bow.blitme()
# Make the most recently drawn screen visible.
pygame.display.flip()
if __name__ == '__main__':
# Make a game instance, and run the game.
tp = TargetPractise()
tp.run_game()
and the bow class.
import pygame
class Bow:
""" A class to manage the bow. """
def __init__(self, tp_game):
""" Initialise the bow and its starting position """
self.screen = tp_game.screen
self.settings = tp_game.settings
self.screen_rect = tp_game.screen.get_rect()
# Load the bow and arrow image
self.bow_arrow_image = pygame.image.load(
'images/FINAL/bow_and_arrow_rotcrop_resize.bmp')
# Load the bow (no arrow) image and get its rect
self.bow_image = pygame.image.load(
'images/FINAL/bow_no_arrow_rotcrop_resize.bmp')
self.rect = self.bow_arrow_image.get_rect()
# Start each new bow at the centre right of the screen
self.rect.midleft = self.screen_rect.midleft
# Store a decimal value for the ship's vertical position
self.y = float(self.rect.y)
# Movement flags
self.moving_up = False
self.moving_down = False
# Bow drawn flag
self.bow_drawn = True
def fire_bow(self):
self.bow_drawn = False
def update(self):
""" Update the bow's position based on the movement flags. """
# Update the bow's y value, not the rect
if self.moving_up:
self.y -= self.settings.bow_speed
if self.moving_down:
self.y += self.settings.bow_speed
# Update rect object from self.y
self.rect.y = self.y
def blitme(self):
"""
Draw the bow at its current location.
Bow will be drawn depending on the bow_drawn flag
"""
if self.bow_drawn:
self.screen.blit(self.bow_arrow_image, self.rect)
elif not self.bow_drawn:
self.screen.blit(self.bow_image, (self.rect.x + 75, self.rect.y))
and finally the settings.
class Settings:
""" A class to store all settings for Target Practise """
def __init__(self):
""" Initialise the game's settings. """
# Screen Settings
self.screen_width = 1200
self.screen_height = 800
self.bg_colour = (255, 255, 255)
# Bow Settings
self.bow_speed = 1
Thanks a lot for reading this!
Upvotes: 1
Views: 2958
Reputation: 2779
I would recommend using pygame.time.set_timer()
and pygames event mechanics. The docs on it are here.
I have an answer to a similar question here, but will include enough info here to help with this specific situation.
You set a timer to expire after a certain time. When it does, it triggers an event that you can look for in your event loop. That allows you to add it into your existing event mechanics pretty simply.
After firing the bow you would add this and it starts a timer that will trigger an event after 2 seconds have elapsed:
pygame.time.set_timer(pygame.USEREVENT, 2000)
To catch that, in your event loop inside _check_events()
you would have something like:
if event.type == pygame.USEREVENT:
# Timer expired. Bow can be fired again
self.bow.pull_bow()
In the bow class you would have to add that pull_bow()
call, something like:
class Bow:
...
def pull_bow(self):
self.bow_drawn = True
Upvotes: 2
Reputation: 121
You have to add a timer variable. This is simply and integer that counts down from a certain number after you release the bow. When the timer variable reaches 0, then you know to allow firing again.
First add this to the init function:
self.bow_fire_wait = 100
self.bow_fire_timer = 0
Then modify the fire_bow function tot this:
def fire_bow(self):
if(self.bow_fire_timer < 0):
self.bow_drawn = False
self.bow_fire_timer = self.bow_fire_wait
Finally add this line to the end of the blitme function:
self.bow_fire_timer -= 1
You can make "self.bow_fire_wait" larger to increase the wait time between shots. The value in there represents the number of frames to wait. So if you're doing 60 fps, self.bow_fire_wait = 60
will wait 1 second.
Upvotes: 0
Reputation: 96
If you don't want to pause the only thread you are using. You should probably use threading.
Try something like this:
from threading import Timer
def bow():
bow_drawn = True
t = Timer(2.0, bow)
t.start() # after 2 seconds, function "bow" will run
Upvotes: 1