When I make a new screen in pygame, you have to repeatedly click the exit button to close the window

Question:

In my PyGame game, when the player dies, I run a game_over() function, which displays a screen with "GAME OVER Press E to exit or R to restart." But, while this is running, if you try to close the window, you have to click multiple times, and same goes for pressing E. Even if you press R and restart, you have to do this, but not before game_over() has run.

Here is game_over():

def game_over():
running = False
while not running:
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            running = True
            pygame.QUIT
        if event.type == pygame.KEYDOWN:
            if event.key == pygame.K_r:
                player.score = 0
                main()
                break
            if event.key == pygame.K_e:
                running = True
                pygame.QUIT

    WIN.fill(BLACK)
    game_over = BIGFONT.render('GAME OVER', False, WHITE)
    WIN.blit(game_over, (200, 175))

    instructions = SMALLFONT.render(f'You scored {player.score}. Press R to restart or E to exit', False, WHITE)
    WIN.blit(instructions, (145, 290))

    pygame.display.flip()

And here is the full code:

import pygame, random

pygame.init()

WIDTH, HEIGHT = 900, 500
WIN = pygame.display.set_mode((WIDTH, HEIGHT))
pygame.display.set_caption('game')

BLOCK_MOVEMENT_TIMER_EVENT = pygame.USEREVENT + 1
BLOCK_MOVEMENT_TIME_DELAY = 15
pygame.time.set_timer(BLOCK_MOVEMENT_TIMER_EVENT , BLOCK_MOVEMENT_TIME_DELAY)

BLOCK_CREATE_TIMER_EVENT = pygame.USEREVENT + 2
BLOCK_CREATE_TIME_DELAY = 1500
pygame.time.set_timer(BLOCK_CREATE_TIMER_EVENT, BLOCK_CREATE_TIME_DELAY)

FPS = 60

#colors
WHITE = (255, 255, 255)
BLACK = (0, 0, 0)
RED = (255, 0, 0)

BIGFONT = pygame.font.Font('Retro.ttf', 65)
SMALLFONT = pygame.font.Font('Retro.ttf', 20)

FLOOR = pygame.Rect(0, 400, 900, 5)

class Player:
    def __init__(self):
        self.size = 20
        self.x = 75
        self.y = 380
        self.jumping = False
        self.fall = False
        self.update_rect()
        self.score = 0

    def update_rect(self):
        self.rect = pygame.Rect(self.x, self.y, self.size, self.size)

    def jump(self):
        pressed = pygame.key.get_pressed()
        if pressed[pygame.K_SPACE] and not self.fall:
            self.jumping = True
        
        if self.jumping:
            self.y -= 3
            if self.y < 300:
                self.y = 300
                self.fall = True
                self.jumping = False

        if self.fall:
            self.y += 3
            if self.y > 380:
                self.y = 380
                self.fall = False
        
        self.update_rect()

player = Player()

class Obstacles:
    def __init__(self, width, height):
        self.x = 900
        self.y = HEIGHT - height - 100
        self.width = width
        self.height = height
        self.update_rect()

    def update_rect(self):
        self.rect = pygame.Rect(self.x, self.y, self.width, self.height)

def move_obstacles(obstacle_list):
    for obstacle in obstacle_list:
        obstacle.x -= 3
        if obstacle.x < -100:
            obstacle_list.pop(0)
            player.score += 1
        
        obstacle.update_rect()

def add_obstacle(obstacle_list):
    obstacle1 = Obstacles(20, 40)
    obstacle2 = Obstacles(75, 20)
    obstacle3 = Obstacles(35, 35)
    obstacle4 = Obstacles(50, 25)
    obstacle5 = Obstacles(80, 10)
    obstacle6 = Obstacles(40, 20)
    obstacle7 = Obstacles(20, 30)

    obstacle_options = [obstacle1, obstacle2, obstacle3, obstacle4, obstacle5, obstacle6, obstacle7]

    obstacle_list.append(obstacle_options[random.randint(0, 6)])

def is_game_over(obstacle_list):
    for obstacle in obstacle_list:
        if player.rect.colliderect(obstacle):
            return True

def game_over():
    running = False
    while not running:
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                running = True
                pygame.QUIT
            if event.type == pygame.KEYDOWN:
                if event.key == pygame.K_r:
                    player.score = 0
                    main()
                    break
                if event.key == pygame.K_e:
                    running = True
                    pygame.QUIT

        WIN.fill(BLACK)
        game_over = BIGFONT.render('GAME OVER', False, WHITE)
        WIN.blit(game_over, (200, 175))

        instructions = SMALLFONT.render(f'You scored {player.score}. Press R to restart or E to exit', False,  WHITE)
        WIN.blit(instructions, (145, 290))

        pygame.display.flip()

def draw_window(obstacle_list, is_game_over):
    WIN.fill(BLACK)
    pygame.draw.rect(WIN, WHITE, FLOOR)

    pygame.draw.rect(WIN, WHITE, player.rect)

    for obstacle in obstacle_list:
        pygame.draw.rect(WIN, RED, obstacle.rect)
    
    score_counter = BIGFONT.render(f'SCORE  {player.score}', False, (255, 255, 255))
    WIN.blit(score_counter, (240, 100))

    pygame.display.flip()

def main():
    clock = pygame.time.Clock()
    
    obstacle_list = []

    done = False
    while not done:
        clock.tick(FPS)
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                done = True
                pygame.QUIT
            if event.type == BLOCK_MOVEMENT_TIMER_EVENT:
                move_obstacles(obstacle_list)
            if event.type == BLOCK_CREATE_TIMER_EVENT:
                add_obstacle(obstacle_list)

        draw_window(obstacle_list, is_game_over(obstacle_list))
        player.jump()
        is_game_over(obstacle_list)

        if is_game_over(obstacle_list):
            game_over()
            done = True

        pygame.display.flip()

if __name__ == '__main__':
    main()

Thank you in advance!

Asked By: birdiechap

||

Answers:

Here, and with e, you set running to True, which breaks that loop. The following line pygame.QUIT does nothing; pygame.QUIT is a variable, not a function. You probably meant pygame.quit().

while not running:
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            running = True
            pygame.QUIT

It would, however, be better if game_over() were to return a restart bool, then main handles either restarting or just sets done to True and lets the main loop end naturally.

 if game_over():
     done = True
 else:
     reset()
Answered By: JeffUK
Categories: questions Tags: ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.