Skip to content
Advertisement

How to run multiple while loops at a time in Pygame

I’m trying to work on a simple Pygame program for a project that simply displays some faces and talks in a text to speech voice, but there is a while loop at the end that is necessary for the code to run but blocks another while loop that I need for the program from running. The while loop I’m trying to add uses time.sleep(), so if I try to put it into the same block as the first one which needs to be constantly running the program crashes. I’m sure I’m probably looking over something obvious but any help would be appreciated, thanks!

Here’s the code:

from random import randint
from time import sleep
import pygame
import pygame.freetype
import time
import random
run = True
pygame.init()

#faces
face = ['^-^', '^v^', '◠◡◠', "'v'", '⁀◡⁀']
talkingFace = ['^o^', '^▽^', '◠▽◠', "'▽'", '⁀ᗢ⁀']
currentFace = random.choice(face)

#background
screen = pygame.display.set_mode((800,600))
screen.fill((0,0,0))

#font and size
myFont = pygame.font.Font('unifont.ttf', 100)

#face render
faceDisplay = myFont.render(str(currentFace), 1, (0,255,0))

#center and draw face
text_rect = faceDisplay.get_rect(center=(800/2, 600/2))
screen.blit(faceDisplay, text_rect)

#prevent crashes
while run:
    for e in pygame.event.get():
        if e.type == pygame.QUIT:
            run = False
    pygame.display.flip()

#loop i'm trying to add
while run:
    faceDisplay = myFont.render(str(currentFace), 1, (0,255,0))
    screen.blit(faceDisplay, text_rect)
    time.sleep(randint(5, 10))

Advertisement

Answer

No, that’s not the way it goes. You have an application loop so use it. time.sleep, pygame.time.wait() or pygame.time.delay 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.

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.

Calculate the point in time when the text needs to be changed. Get a new random text and render the text Surface after the current time is greater than the calculated point of time. Calculate a new random point in time which is greater than the current time:

next_render_time = 0

while run:
    current_time = pygame.time.get_ticks()

    # [...]

    if current_time >= next_render_time:
        currentFace = random.choice(face)
        faceDisplay = myFont.render(str(currentFace), 1, (0,255,0))
        next_render_time = current_time + randint(5, 10) * 1000

    screen.fill((0,0,0))
    screen.blit(faceDisplay, text_rect)
    pygame.display.flip()

The time is multiplied by 1000 (randint(5, 10) * 1000), since the time unit of pygame.time.get_ticks() is milliseconds.

Actually, you can also use a timer event. See How do I use a PyGame timer event?.
In your case, however, the time interval is not constant. It’s a bit odd to use a timer event on a dynamically changing interval. I would prefer to use a timer event for an action that needs to be performed at constant intervals. But that’s just my opinion.


Complete example:

from random import randint
from time import sleep
import pygame
import pygame.freetype
import time
import random
run = True
pygame.init()

#faces
face = ['^-^', '^v^', '◠◡◠', "'v'", '⁀◡⁀']
talkingFace = ['^o^', '^▽^', '◠▽◠', "'▽'", '⁀ᗢ⁀']
currentFace = random.choice(face)

#background
screen = pygame.display.set_mode((800,600))

#font and size
myFont = pygame.font.Font('unifont.ttf', 100)

#face render
faceDisplay = myFont.render(str(currentFace), 1, (0,255,0))

#center and draw face
text_rect = faceDisplay.get_rect(center=(800/2, 600/2))

next_render_time = 0

#prevent crashes
while run:
    current_time = pygame.time.get_ticks()
    for e in pygame.event.get():
        if e.type == pygame.QUIT:
            run = False
 
    if current_time >= next_render_time:
        currentFace = random.choice(face)
        faceDisplay = myFont.render(str(currentFace), 1, (0,255,0))
        next_render_time = current_time + randint(5, 10) * 1000

    screen.fill((0,0,0))
    screen.blit(faceDisplay, text_rect)
    pygame.display.flip()
User contributions licensed under: CC BY-SA
3 People found this is helpful
Advertisement