Pygame - drop enemies randomly in time
I recommend to use a timer event. Use pygame.time.set_timer()
to repeatedly create an USEREVENT
. e.g.:
milliseconds_delay = 3000
enemy_spawn_event = pygame.USEREVENT + 1
pygame.time.set_timer(enemy_spawn_event, milliseconds_delay)
Note, in pygame customer events can be defined. Each event needs a unique id. The ids for the user events have to start at pygame.USEREVENT
. In this case pygame.USEREVENT+1
is the event id for the timer event, which spawns the enemies.
Create a new enemy when the event occurs in the event loop:
milliseconds_delay = 3000
enemy_spawn_event = pygame.USEREVENT + 1
pygame.time.set_timer(enemy_spawn_event, milliseconds_delay)
while not game_over:
# [...]
# Scoring and lifepoints systems
for event in pygame.event.get():
if event.type == pygame.QUIT:
sys.exit()
if event.type == enemy_spawn_event and len(all_enemies) < number_of_enemies:
all_enemies.append(Enemies(int(SCREEN_WIDTH/2), 0, color = YELLOW, position = "top"))
for e in all_enemies:
e.randomise()
time_counter = 0
Pygame : Repeating an action every x amount of time
I recommend to use a timer event. Use pygame.time.set_timer()
to repeatedly create an USEREVENT
. e.g.:
banana_delay = 500 # 0.5 seconds
banana_event = pygame.USEREVENT + 1
pygame.time.set_timer(banana_event, banana_delay)
Note, in pygame customer events can be defined. Each event needs a unique id. The ids for the user events have to be between pygame.USEREVENT
(24) and pygame.NUMEVENTS
(32). In this case pygame.USEREVENT+1
is the event id for the timer event, which spawns the bullets.
Create a new banana when the event occurs in the event loop:
def check_events(screen, settings, bananas, monkey):
for event in pygame.event.get():
if event.type == pygame.QUIT:
sys.exit()
# [...]
if event.type == banana_event:
create_banana(screen, settings, bananas)
The timer event can be stopped by passing 0 to the time parameter.
If you want spawn the bananas by a random time spawn, then you have to change the timespan (in milliseconds) randomly. Use random.randint(a, b)
to generate random times:
if event.type == banana_event:
create_banana(screen, settings, bananas)
banana_delay = random.randint(500, 3000) # random from 0.5 to 3 seconds
pygame.time.set_timer(banana_event, banana_delay)
How can I use time to make random arrows in pygame?
First of all don't create on class for each arrow. A class is an object with attributes. Use the attributes of a class and create instances of the class with different appearance and behavior. Add parameters to the signature of the constructor and use the the parameters to the initialize the attributes.
Add parameters for the position and image filename:
class ArrowGray(pygame.sprite.Sprite):
def __init__(self, pos, imagename):
pygame.sprite.Sprite.__init__(self)
self.image = pygame.image.load(imagename).convert()
self.image.set_colorkey(BLACK)
self.rect = self.image.get_rect()
self.rect.center = pos
class Arrow(pygame.sprite.Sprite):
def __init__(self, pos, imagename):
pygame.sprite.Sprite.__init__(self)
self.image = pygame.image.load(imagename).convert()
self.image.set_colorkey(BLACK)
self.rect = self.image.get_rect()
self.rect.center = pos
def update(self):
self.rect.x += 5
if (self.rect.left > (WIDTH + 50)):
self.rect.right = 0
a1g = ArrowGray((750, 25), "gray right.png")
a2g = ArrowGray((750, 125), "gray left.png")
a3g = ArrowGray((750, 250), "gray up.png")
a4g = ArrowGray((750, 375), "gray down.png")
a1 = Arrow((25, 25), "red right.png")
a2 = Arrow((25, 125), "blue left.png")
a3 = Arrow((25, 250), "green up.png")
a4 = Arrow((25, 375), "yellow down.png")
I recommend to use a timer event. Use pygame.time.set_timer()
to repeatedly create an USEREVENT
. e.g.:
arrowGrayList = [a1g, a2g, a3g, a4g]
arrowList = [a1, a2, a3, a4]
allArrows = pygame.sprite.Group()
millisecondsDelay = 500 # 0.5 seconds
arrowTimerEvent = pygame.USEREVENT + 1
pygame.time.set_timer(arrowTimerEvent, millisecondsDelay)
spawned = 0
while running:
clock.tick(FPS)
#Update
allArrows.update()
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
if event.type == arrowTimerEvent:
if spawned < len(arrowList):
allArrows.add(arrowGrayList[spawned])
allArrows.add(arrowList[spawned])
spawned += 1
keys = pygame.key.get_pressed()
if keys[pygame.K_RIGHT]:
if pygame.sprite.collide_rect(a1, a1g):
a1.rect.center = (900, 600)
if keys[pygame.K_LEFT]:
if pygame.sprite.collide_rect(a2, a2g):
a2.rect.center = (900, 600)
if keys[pygame.K_UP]:
if pygame.sprite.collide_rect(a3, a3g):
a3.rect.center = (900, 600)
if keys[pygame.K_DOWN]:
if pygame.sprite.collide_rect(a4, a4g):
a4.rect.center = (900, 600)
#draw
screen.fill(SALMON)
allArrows.draw(screen)
pygame.display.flip()
pygame.quit()
Note, in pygame customer events can be defined. Each event needs a unique id. The ids for the user events have to start at pygame.USEREVENT
. In this case pygame.USEREVENT+1
is the event id for the timer event, which spawns the arrows.
Spawning multiple instances of the same object concurrently in python
It does not work that way. time.sleep
, pygame.time.wait()
or pygame.time.delay
is not the right way to control time and gameplay within an application loop. The game does not respond while you wait. The application loop runs continuously. You have to measure the time in the loop and spawn the objects according to the elapsed time.pygame.Surface.fill
clears the entire screen. Add the newly created objects to a list. Redraw all of the objects and the entire scene in each frame.
See also Time, timer event and clock
You have 2 options. Use pygame.time.get_ticks()
to measure the time. Define a time interval after which a new object should appear. Create an object when the point in time is reached and calculate the point in time for the next object:
object_list = []
time_interval = 500 # 500 milliseconds == 0.1 seconds
next_object_time = 0
while run:
# [...]
current_time = pygame.time.get_ticks()
if current_time > next_object_time:
next_object_time += time_interval
object_list.append(Object())
Minimal example:
repl.it/@Rabbid76/PyGame-TimerSpawnObjects
import pygame, random
pygame.init()
window = pygame.display.set_mode((300, 300))
class Object:
def __init__(self):
self.radius = 50
self.x = random.randrange(self.radius, window.get_width()-self.radius)
self.y = random.randrange(self.radius, window.get_height()-self.radius)
self.color = pygame.Color(0)
self.color.hsla = (random.randrange(0, 360), 100, 50, 100)
object_list = []
time_interval = 200 # 200 milliseconds == 0.2 seconds
next_object_time = 0
run = True
clock = pygame.time.Clock()
while run:
clock.tick(60)
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
current_time = pygame.time.get_ticks()
if current_time > next_object_time:
next_object_time += time_interval
object_list.append(Object())
window.fill(0)
for object in object_list[:]:
pygame.draw.circle(window, object.color, (object.x, object.y), round(object.radius))
object.radius -= 0.2
if object.radius < 1:
object_list.remove(object)
pygame.display.flip()
pygame.quit()
exit()
The other option is to use the pygame.event
module. Use pygame.time.set_timer()
to repeatedly create a USEREVENT
in the event queue. The time has to be set in milliseconds. e.g.:
object_list = []
time_interval = 500 # 500 milliseconds == 0.1 seconds
timer_event = pygame.USEREVENT+1
pygame.time.set_timer(timer_event, time_interval)
Note, in pygame customer events can be defined. Each event needs a unique id. The ids for the user events have to be between pygame.USEREVENT
(24) and pygame.NUMEVENTS
(32). In this case pygame.USEREVENT+1
is the event id for the timer event.
Receive the event in the event loop:
while run:
for event in pygame.event.get():
if event.type == timer_event:
object_list.append(Object())
The timer event can be stopped by passing 0 to the time argument of pygame.time.set_timer
.
Minimal example:
repl.it/@Rabbid76/PyGame-TimerEventSpawn
import pygame, random
pygame.init()
window = pygame.display.set_mode((300, 300))
class Object:
def __init__(self):
self.radius = 50
self.x = random.randrange(self.radius, window.get_width()-self.radius)
self.y = random.randrange(self.radius, window.get_height()-self.radius)
self.color = pygame.Color(0)
self.color.hsla = (random.randrange(0, 360), 100, 50, 100)
object_list = []
time_interval = 200 # 200 milliseconds == 0.2 seconds
timer_event = pygame.USEREVENT+1
pygame.time.set_timer(timer_event, time_interval)
run = True
clock = pygame.time.Clock()
while run:
clock.tick(60)
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
elif event.type == timer_event:
object_list.append(Object())
window.fill(0)
for object in object_list[:]:
pygame.draw.circle(window, object.color, (object.x, object.y), round(object.radius))
object.radius -= 0.2
if object.radius < 1:
object_list.remove(object)
pygame.display.flip()
pygame.quit()
exit()
Trying to delay a specific function for spawning enemy after a certain amount of time
In pygame exists a timer event. Use pygame.time.set_timer()
to repeatedly create a USEREVENT
in the event queue.. The time has to be set in milliseconds:
pygame.time.set_timer(pygame.USEREVENT, 1000) # 1 second
Note, in pygame customer events can be defined. Each event needs a unique id. The ids for the user events have to be between pygame.USEREVENT
(24) and pygame.NUMEVENTS
(32). In this case the value of pygame.USEREVENT
is the event id for the timer event.
Receive the event in the event loop:
running = True
while run:
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
elif event.type == pygame.USEREVENT:
# [...]
The timer event can be stopped by passing 0 to the time argument of pygame.time.set_timer
.
See also Spawning multiple instances of the same object concurrently in python.
Create a list of moles
and add a random position to the list in mole_spawn_easy
:
moles = []
def mole_spawn_easy():
molex = random.randint(50, 950)
moley = random.randint(450, 682)
moles.append((molex, moley))
Draw the moles
in the main application loop:
while run:
# [...]
for pos in moles:
screen.blit(mole, pos)
See the example:
moles = []
def mole_spawn_easy():
molex = random.randint(50, 950)
moley = random.randint(450, 682)
moles.append((molex, moley))
pygame.time.set_timer(pygame.USEREVENT, 1000)
while run:
ax, ay = pygame.mouse.get_pos()
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
if event.type == pygame.USEREVENT:
counter -= 1
text = ("Time Left: " + str(counter)).rjust(3)
if counter > 0:
mole_spawn_easy()
else:
print("game over")
screen.blit(background, [0,0])
for pos in moles:
screen.blit(mole, pos)
screen.blit(aim, ((ax - 32 ),(ay - 32)))
screen.blit(font.render(text, True, (0, 0, 0)), (32, 48))
pygame.display.flip()
clock.tick(FPS)
Time Delay and keeping track of the of # of click
Never use a delay in your application loop. Use the application loop. Compute the point in time when the rectangles have to change color back. Change the color after the current time is greater than the calculated point of time.
In pygame the system time can be obtained by calling pygame.time.get_ticks()
, which returns the number of milliseconds since pygame.init()
was called. See pygame.time
module.
Add 2 attributes self.tile_clicked = 0
and self.turn_white_time = 0
to the class Game
:
class Game:
def __init__(self,surface,surface_width,surface_height):
# [...]
self.tile_clicked = []
self.turn_white_time = 0
Compute the the point in time when the rectangles have to change color back after the 2nd rectangle was clicked:
class Game:
# [...]
def handle_color_change(self, event):
if len(self.tile_clicked) < 2:
if 1 not in self.tile_clicked:
if event.button == 1 and self.tile_left.inside_tile(event.pos) == True:
self.tile_left.set_color(random.choice(self.color_options))
self.tile_clicked.append(1)
if 2 not in self.tile_clicked:
if event.button == 1 and self.tile_right.inside_tile(event.pos) == True:
self.tile_right.set_color(random.choice(self.color_options))
self.tile_clicked.append(2)
if len(self.tile_clicked) == 2:
delay_time = 1000 # 1000 milliseconds == 1 second
self.turn_white_time = pygame.time.get_ticks() + delay_time
get_ticks()
returns the current time. A time is just a number. get_ticks() + delay_time
is a time in the future. When the program is running, the current time is continuously retrieved and compared with turn_white_time
. At some point the current time is greater than turn_white_time
and the color of the rectangles is changed.
Change back to the white color after the current time is greater than the calculated point of time in update
:
class Game:
# [...]
def update(self):
current_time = pygame.time.get_ticks()
if len(self.tile_clicked) == 2 and current_time > self.turn_white_time:
self.tile_left.set_color(self.default_color)
self.tile_right.set_color(self.default_color)
self.tile_clicked = []
Complete example:
import pygame,time,random
# User-defined functions
def main():
# for initializing all pygame modules
pygame.init()
# this creates the pygame display window
surface_width = 500
surface_height = 400
surface = pygame.display.set_mode((surface_width,surface_height))
# this sets the caption of the window to 'Pong'
pygame.display.set_caption('Painting')
# creates a game object
game = Game(surface, surface_width, surface_height)
# this starts the game loop by calling the play method found in the game object
game.play()
# quits pygame and cleans up the pygame window
pygame.quit()
# User-defined classes
class Game:
# an object in this class represents the complete game
def __init__(self,surface,surface_width,surface_height):
# # Initialize a Game.
# - self is the Game to initialize
# - surface is the display window surface object
# - surface_width is the display width size
# - surface_height is the display height size
# attributes that are needed to run any game
self.surface = surface
self.surface_width = surface_width
self.surface_height = surface_height
self.close_clicked = False
self.surface_color = pygame.Color('black')
# attributes that are needed to run this specific game
self.FPS = 60
self.game_clock = pygame.time.Clock()
self._continue = True
self.score = [0,0]
self.max_mismatch = 5
# Game specific objects
self.default_color = 'white'
self.color_options = ('blue' , 'red', 'yellow', 'green')
self.tile_width = 50
self.tile_height = 150
self.tile_left = Tile( self.default_color, (self.surface_width/3) - self.tile_width, (self.surface_height/2)/ 2 , self.tile_width, self.tile_height , self.surface)
self.tile_right = Tile(self.default_color, self.surface_width/2 + self.tile_width, (self.surface_height/2)/ 2
,self.tile_width, self.tile_height , self.surface)
self.tile_clicked = []
self.turn_white_time = 0
def play(self):
# this is main game loop
# plays the game until the players has closed the window or the score of a players equals the max score
# - self is the game that should be continued or not
while not self.close_clicked:
self.main_handle_events()
self.draw()
self.update()
self.game_clock.tick(self.FPS)
def draw(self):
# this draws the circle and the rectangles that are needed for this specific game
# -self is the Game to draw
self.surface.fill(self.surface_color)
self.tile_left.draw()
self.tile_right.draw()
self.display_score_match()
self.display_score_mismatch(self.surface_width)
pygame.display.update() # makes the updated surface appear on the display
def update(self):
current_time = pygame.time.get_ticks()
if len(self.tile_clicked) == 2 and current_time > self.turn_white_time:
self.tile_left.set_color(self.default_color)
self.tile_right.set_color(self.default_color)
self.tile_clicked = []
def main_handle_events(self):
# handles each user events by changing the game state appropriately
# -self is the Game of whose events are handled
events = pygame.event.get()
for event in events:
if event.type == pygame.QUIT:
self.close_clicked = True
if event.type == pygame.MOUSEBUTTONDOWN:
self.handle_color_change(event)
#self.update_score()
#self.handle_color_change(event)
def display_score_match(self):
text_string = 'Match: ' + str(self.score[0])
text_colour = pygame.Color('white')
text_font = pygame.font.SysFont('Times New Roman',25)
text_image = text_font.render(text_string, True, text_colour)
text_pos = [0,0]
self.surface.blit(text_image, text_pos)
def display_score_mismatch(self, surface_width):
text_string = 'Mismatch: ' + str(self.score[1])
text_colour = pygame.Color('white')
text_font = pygame.font.SysFont('Times New Roman',25)
text_image = text_font.render(text_string, True, text_colour)
text_pos = [(surface_width - text_image.get_width()), 0]
self.surface.blit(text_image, text_pos)
def handle_color_change(self, event):
if len(self.tile_clicked) < 2:
if 1 not in self.tile_clicked:
if event.button == 1 and self.tile_left.inside_tile(event.pos) == True:
self.tile_left.set_color(random.choice(self.color_options))
self.tile_clicked.append(1)
if 2 not in self.tile_clicked:
if event.button == 1 and self.tile_right.inside_tile(event.pos) == True:
self.tile_right.set_color(random.choice(self.color_options))
self.tile_clicked.append(2)
if len(self.tile_clicked) == 2:
delay_time = 1000 # 1000 milliseconds == 1 second
self.turn_white_time = pygame.time.get_ticks() + delay_time
def update_score(self):
if self.tile_left.color_match(self.tile_right) == True:
self.score[0] = self.score[0] + 1
else:
self.score[1] = self.score[1] + 1
class Tile:
def __init__(self, rect_color, rect_left, rect_top, rect_width, rect_height,surface):
# Initialize a rectabgle which is used as a paintbrush.
# - self is the rectangle to initialize
# - rect_color is the pygame.Color of the dot
# - rect_height is the int length of the rectangle in the y axis
# - rect_width is the int width of the rectangle in the x axis
# - rect_left is the int coordinate position of the rectangle in the x axis
# - rect_top is the int coordinate position of the rectangle in the y axis
# - rect_velocity is a list of x and y components and the speed of which the rectangles can move
self.rect_colour = pygame.Color(rect_color)
self.rect_height = rect_height
self.rect_width = rect_width
self.rect_left = rect_left
self.rect_top = rect_top
self.surface = surface
self.rect_parameters = pygame.Rect(rect_left, rect_top, rect_width, rect_height)
def draw(self):
# draws the rectangle on the surface
# - self is the rectangle
pygame.draw.rect(self.surface, self.rect_colour, self.rect_parameters)
def inside_tile(self, position):
inside = False
if self.rect_parameters.collidepoint(position):
inside = True
return inside
def set_color(self, color):
self.rect_colour = pygame.Color(color)
def color_match(self, other_tile):
match = False
if self.rect_colour == other_tile.rect_colour:
match = True
return match
main()
how to spawn a sprite after a time limit and how to display a timer pygame
There are several ways to implement a timer in pygame. You can use the time that pygame.Clock.tick
returns to increase or decrease a timer variable, calculate the time difference with pygame.time.get_ticks
or use a custom event in conjunction with pygame.time.set_timer
.
Example 1 - delta time:
import sys
import random
import pygame as pg
class Block(pg.sprite.Sprite):
def __init__(self, pos):
super().__init__()
self.image = pg.Surface((40, 40))
self.image.fill(pg.Color('sienna1'))
self.rect = self.image.get_rect(topleft=pos)
def main():
screen = pg.display.set_mode((640, 480))
clock = pg.time.Clock()
font = pg.font.Font(None, 30)
all_sprites = pg.sprite.Group()
# Delta time is the time that has passed since clock.tick
# was called the last time.
dt = 0
# We'll subtract dt (delta time) from this timer variable.
timer = 1 # 1 means one second.
done = False
while not done:
for event in pg.event.get():
if event.type == pg.QUIT:
done = True
# Decrease timer to get a countdown.
timer -= dt
# When the timer is below or equal to 0, we spawn
# a new block.
if timer <= 0:
all_sprites.add(Block((random.randrange(600),
random.randrange(440))))
# Reset the countdown timer to one second.
timer = 1
all_sprites.update()
screen.fill(pg.Color('gray15'))
all_sprites.draw(screen)
timer_surface = font.render(
str(round(timer, 3)), True, pg.Color('yellow'))
screen.blit(timer_surface, (20, 20))
pg.display.flip()
# dt = time in seconds that passed since last tick.
# Divide by 1000 to convert milliseconds to seconds.
dt = clock.tick(30) / 1000
if __name__ == '__main__':
pg.init()
main()
pg.quit()
sys.exit()
If you want to spawn exactly 1 sprite, you can add another variable like boss_spawned = False
and change the timer only if the boss hasn't spawned:
if not boss_spawned:
timer -= dt
if timer <= 0:
all_sprites.add(Block((random.randrange(600),
random.randrange(440))))
boss_spawned = True
Or set the timer to exactly 0 after the spawn and only decrease the timer if it's != 0
.
if timer != 0:
timer -= dt
if timer <= 0:
all_sprites.add(Block((random.randrange(600),
random.randrange(440))))
timer = 0
Example 2 - pygame.time.get_ticks (replace the main function above):
def main():
screen = pg.display.set_mode((640, 480))
clock = pg.time.Clock()
font = pg.font.Font(None, 30)
all_sprites = pg.sprite.Group()
# Start time.
now = pg.time.get_ticks()
done = False
while not done:
for event in pg.event.get():
if event.type == pg.QUIT:
done = True
# If the time difference is greater than 1000
# milliseconds, spawn a block.
time_difference = pg.time.get_ticks() - now
if time_difference >= 1000:
all_sprites.add(Block((random.randrange(600),
random.randrange(440))))
# Reset the start time.
now = pg.time.get_ticks()
all_sprites.update()
screen.fill(pg.Color('gray15'))
all_sprites.draw(screen)
timer_surface = font.render(
str(time_difference/1000), True, pg.Color('yellow'))
screen.blit(timer_surface, (20, 20))
pg.display.flip()
clock.tick(30)
If you just want to count the kills or spawned mobs, you can increment a counter variable and then spawn the enemy boss when it exceeds some limit. The following example just counts the mouse clicks and spawns a block after 3 clicks.
def main():
screen = pg.display.set_mode((640, 480))
clock = pg.time.Clock()
font = pg.font.Font(None, 30)
all_sprites = pg.sprite.Group()
# We'll just count mouse clicks in this example.
# You can replace it with the kill count in your game.
clicks = 0
done = False
while not done:
for event in pg.event.get():
if event.type == pg.QUIT:
done = True
if event.type == pg.MOUSEBUTTONDOWN:
clicks += 1
if clicks >= 3:
all_sprites.add(Block((random.randrange(600),
random.randrange(440))))
clicks = 0
all_sprites.update()
screen.fill(pg.Color('gray15'))
all_sprites.draw(screen)
clicks_surface = font.render(str(clicks), True, pg.Color('yellow'))
screen.blit(clicks_surface, (20, 20))
pg.display.flip()
clock.tick(30)
Related Topics
Python Global Exception Handling
Pytz Localize VS Datetime Replace
Wrapping a C Library in Python: C, Cython or Ctypes
Different Ways of Clearing Lists
Python: the _Imagingft C Module Is Not Installed
Escape Special Characters in a Python String
Non-Ascii Characters in Matplotlib
Import a Python Module Without the .Py Extension
Why Does Defining _Getitem_ on a Class Make It Iterable in Python
Where to Put Freeze_Support() in a Python Script
How to Transpose Dataframe in Pandas Without Index
Multiprocessing.Pool: What's the Difference Between Map_Async and Imap
Pip or Pip3 to Install Packages for Python 3
Merge Multiple Column Values into One Column in Python Pandas