“So if you want to go fast, if you want to get done quickly, if you want your code to be easy to write, make it easy to read.”
Robert C. Martin, Clean Code: A Handbook of Agile Software Craftsmanship
Related Stack Overflow questions:
If you want to control something over time in Pygame you have two options:
Use pygame.time.get_ticks()
to measure time and and implement logic that controls the object depending on the time.
Use the timer event. Use pygame.time.set_timer()
to repeatedly create a USEREVENT
in the event queue. Change object states when the event occurs.
📁 Minimal example - get_ticks
▶ run minimal example - get_ticks
📁 Minimal example - timer event
▶ run minimal example - timer event
📁 Minimal example - different frame rates
▶ run minimal example - different frame rates
Related Stack Overflow questions:
If you just wait for some time, you can use pygame.time.wait
or pygame.time.delay
. However, if you want to display a message and then wait some time, you need to update the display beforehand. The display is updated only if either pygame.display.update()
or pygame.display.flip()
is called. See pygame.display.flip()
:
This will update the contents of the entire display.
Further you’ve to handles the events with pygame.event.pump()
, before the update of the display becomes visible in the window. See pygame.event.pump()
:
For each frame of your game, you will need to make some sort of call to the event queue. This ensures your program can internally interact with the rest of the operating system.
This all means that you have to call pygame.display.flip()
and pygame.event.pump()
before pygame.time.wait()
:
screen.blit(text, (x, y))
pygame.display.flip()
pygame.event.pump()
pygame.time.delay(delay * 1000) # 1 second == 1000 milliseconds
In any case, this is not the way to wait or delay something in a typical application. The game does not respond while you wait. Use pygame.time.get_ticks()
to measure the time.
For instance if you want to show a message on the display, get the current time and calculate the point in time after that the message has to disappear. Display the message as long as the current time is below the calculated time:
message_end_time = 0
run = True
while run:
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
# [...]
current_time = pygame.time.get_ticks()
if something_has_happened:
message_surf = font.render('Important message!', True, (255, 0, 0))
message_end_time = pygame.time.get_ticks() + 3000 # display for 3 seconds
window.fill('black')
# [...]
if current_time < message_end_time:
window.blit(message_surf, (x, y))
pygame.display.flip()
📁 Minimal example - Display a message for a period of time
▶ run minimal example - Display a message for a period of time
repl.it/@Rabbid76/PyGame-MessageDelay
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.
Related Stack Overflow questions:
You have to draw the image in the main application loop. Use pygame.time.get_ticks()
to get the number of milliseconds since pygame.init()
was called. When the MOUSEBUTTONDOWN
event occurs, then calculate the point in time after that the image has to be displayed. Display the image after the current time is greater than the calculated point of time:
image_time = 0
run = True
while run:
current_time = pygame.time.get_ticks()
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
if image_time > 0 and current_time >= image_time:
image_time = current_time + 1000 # 1000 milliseconds == 3 seconds
# [...]
if current_time >= image_time:
# draw object or do action
# [...]
📁 Minimal example - Until a certain time
repl.it/@Rabbid76/PyGame-TimerAfterACertainTime
Related Stack Overflow questions:
How do I stop more than 1 bullet firing at once?
📁 minimal example - Shoot bullet
repl.it/@Rabbid76/PyGame-ShootBullet
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 delta 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))
Related Stack Overflow questions:
How can I show explosion image when collision happens?
📁 Minimal example - Timer for a period of time
▶ run minimal example - Timer for a period of time
Adding a particle effect to my clicker game
📁 Minimal example - Spawn text on click
▶ run minimal example - Spawn text on click
Pygame “pop up” text - How to show an image only for a period of time?
📁 Minimal example - pop up text
▶ run minimal example - Spawn text on click
How can i controll collision from sprites?
📁 Minimal example - random pop up text
▶ run minimal example - random pop up text
Calculate the point in time after that the explosion image has to be removed. Add the coordinates of the explosion and the end time point to the head of a list (explosionList
). Draw the explosion(s) in the main application loop. Remove the expired explosions from the tail of the list.
With this algorithm it is possible to manage multiple explosions.
Test algorithm:
current_time = 0.5
explosionList = [(4, 1, 1), (3, 2, 2), (2, 3, 3), (1, 4, 4)]
while len(explosionList) > 0:
for i in range(len(explosionList)):
if current_time < explosionList[i][0]:
print(explosionList[i][1:])
else:
explosionList = explosionList[:i]
break
print(explosionList)
current_time += 1
📁 Minimal example - For a period of time
Related Stack Overflow questions:
Spawning multiple instances of the same object concurrently in python
📁 Minimal example - Spawn objects
▶ run minimal example - Spawn objects
How do I make the image repeat - for example 10x in a row - in Pygame
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. Add the newly created objects to a list. Redraw all of the objects and the entire scene in each frame.
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.5 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())
Related Stack Overflow questions:
Use pygame.time.get_ticks
to get the current time in milliseconds and set the start time, before the main loop:
start_time = pygame.time.get_ticks()
while True:
# [...]
Calculate the elapsed time in every frame. To convert from milliseconds to seconds, the time difference has to be divided by 1000.
Render the time text to a surface by (font.render
). Note, .render()
returns a pygame.Surface
object and a pygame.Rect
. The rectangle can be used to calculate the position of the text. In the following the text is placed at the bottom right, with a margin of 20 to the border of the window:
while True:
# [...]
if inventory[coins] < 100:
current_time = pygame.time.get_ticks()
delta_time_s = (current_time - start_time) // 1000
text_surf, text_rect = font.render(str(delta_time_s), (255, 255, 255), size=30)
margin = 20 # margin to the window
size = (200, 200) # window size
text_pos = (size[0] - text_rect.width - margin, size[1] - text_rect.height - margin)
displaysurf.blit(text_surf, text_pos)
If you want to show the tenths of seconds, then the calculation of delta_time_s
has to be changed:
delta_time_s = (current_time - start_time) // 100 / 10
Store the milliseconds when MOUSEBUTTONDOWN
and calculate the time difference in the main loop:
from pygame import *
screen = display.set_mode((160, 90))
clock = time.Clock()
run = True
started = False
while run:
for new_event in event.get():
if new_event.type == QUIT:
run = False
if new_event.type == MOUSEBUTTONDOWN:
start_time = time.get_ticks()
started = True
if new_event.type == MOUSEBUTTONUP:
started = False
if started:
current_time = time.get_ticks()
sec = (current_time - start_time) / 1000.0
print(sec)
display.update()
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.
For a timer event you need to define a unique user events 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:
timer_interval = 500 # 0.5 seconds
timer_event_id = pygame.USEREVENT + 1
pygame.time.set_timer(timer_event_id, timer_interval)
running = True
while running:
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
elif event.type == timer_event_id:
# [...]
The timer event can be stopped by passing 0 to the time argument of pygame.time.set_timer
.
Related Stack Overflow questions:
📁 Minimal example - Count down
📁 Minimal example - Timer callback
repl.it/@Rabbid76/PyGame-TimerCallback
Related Stack Overflow questions:
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. Add the newly created objects to a list. Redraw all of the objects and the entire scene in each frame.
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.5 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
.
Related Stack Overflow questions:
Related Stack Overflow questions:
Use pygame.time.Clock
to control the frames per second and thus the game speed.
The method tick()
of a pygame.time.Clock
object, delays the game in that way, that every iteration of the loop consumes the same period of time. See pygame.time.Clock.tick()
:
This method should be called once per frame.
That means that the loop:
clock = pygame.time.Clock() run = True while run: clock.tick(60)
runs 60 times per second.
This method should be called once per frame. It will compute how many milliseconds have passed since the previous call.
Related Stack Overflow questions:
You have to calculate the movement per frame depending on the frame rate.
pygame.time.Clock.tick
returns the number of milliseconds since the last call. When you call it in the application loop, this is the number of milliseconds that have passed since the last frame. Multiply the objects speed by the elapsed time per frame to get constant movement regardless of FPS.
Define the distance in pixels that the player should move per second (move_per_second
). Then compute the distance per frame in the application loop:
move_per_second = 500
FPS = 60
run = True
clock = pygame.time.Clock()
while run:
ms_frame = clock.tick()
move_per_frame = move_per_second * ms_frame / 1000
# [...]
Related Stack Overflow questions:
See get_fps()
:
Compute your game’s framerate (in frames per second). It is computed by averaging the last ten calls to
Clock.tick()
.
Related Stack Overflow questions: