PyGameExamplesAndAnswers

StackOverflow            reply.it reply.it

“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


Time, timer event and clock

Related Stack Overflow questions:

If you want to control something over time in Pygame you have two options:

  1. Use pygame.time.get_ticks() to measure time and and implement logic that controls the object depending on the time.

  2. 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.

time example

📁 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

Wait for a period of time

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

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.

After a certain time

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

Lock for a period of time

Related Stack Overflow questions:

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))

For a period of time

Related Stack Overflow questions:

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

Triggered by a time interval

Related Stack Overflow questions:

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())

Display application (game) time

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()

Timer event

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.

Counter based on timer event

Related Stack Overflow questions:

📁 Minimal example - Counter

📁 Minimal example - Count down

📁 Minimal example - Timer callback

repl.it/@Rabbid76/PyGame-TimerCallback

Action trigger based on timer event

Related Stack Overflow questions:

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.

Display time

Related Stack Overflow questions:

Clock and frames per second

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.

See pygame.time.Clock.tick():

This method should be called once per frame. It will compute how many milliseconds have passed since the previous call.

Time per frame

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  

    # [...]

Frames per second

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().

Delay, Sleep, Wait

Related Stack Overflow questions: