Python - Pong - se bloquea en lugar de reiniciar el juego cuando cualquiera de los jugadores alcanza un umbral de puntuación

CorePress2024-01-25  13

El problema es que el juego se bloquea cada vez que se muestra el texto del ganador (si cualquiera de los jugadores alcanza una puntuación de 10 o más) y no vuelve a llamar la función main() cuando dije que debería hacerlo, como puedes ver en mi código a continuación. Entonces, cuando muestra el texto del ganador, debe pausar el juego durante 5 segundos, luego salir del ciclo while y recuperar main() pero falla justo después de que se muestra el texto del ganador (ver captura de pantalla a continuación). La lógica para el texto ganador está cerca de la parte superior de mi función main().

No estoy seguro de cuál es la causa raíz ya que llamé a pygame.init() en la parte superior de mi programa. ¡Cualquier ayuda sería muy apreciada! El código completo se encuentra a continuación... si alguien puede ejecutarlo y decirme cuál es la causa principal de su falla, sería genial. gracias en el anuncio¡vance!

import pygame

pygame.init()

WIDTH, HEIGHT = 750, 500
WINDOW = pygame.display.set_mode((WIDTH, HEIGHT))   

pygame.display.set_caption("PONG")  

WINNER_FONT = pygame.font.SysFont('comicsans', 100)

BLUE = (0, 0, 255)          
RED = (255, 0, 0)           
YELLOW = (255, 255, 0)     
WHITE = (255, 255, 255)     
BLACK = (0, 0, 0)           

class Paddle(pygame.sprite.Sprite):     
    def __init__(self):
        pygame.sprite.Sprite.__init__(self)    
        self.image = pygame.Surface([10, 75])
        self.image.fill(WHITE)
        self.rect = self.image.get_rect()   
        self.paddle_speed = 4          
        self.points = 0

class Ball(pygame.sprite.Sprite):
    def __init__(self):
        pygame.sprite.Sprite.__init__(self)
        self.image = pygame.Surface([10, 10])
        self.image.fill(YELLOW)
        self.rect = self.image.get_rect()
        self.speed = 2
        self.dx = 1    
        self.dy = 1

paddle1 = Paddle()
paddle1.image.fill(BLUE)
paddle1.rect.x = 25      
paddle1.rect.y = 225     

paddle2 = Paddle()
paddle2.image.fill(RED)
paddle2.rect.x = 715
paddle2.rect.y = 225

ball = Ball()
ball.rect.x = 375
ball.rect.y = 250

all_sprites = pygame.sprite.Group() 
all_sprites.add(paddle1, paddle2, ball)


def redraw():
    WINDOW.fill(BLACK)
    font = pygame.font.SysFont("Consolas", 35)
    text = font.render("---[PONG]---", 1, YELLOW)
    textRect = text.get_rect()      
    textRect.center = (WIDTH//2, 25)  
    WINDOW.blit(text, textRect)     

    #Player 1 score
    p1_score = font.render(str([paddle1.points]), 1, WHITE)
    p1Rect = p1_score.get_rect()    
    p1Rect.center = (50, 50)        
    WINDOW.blit(p1_score, p1Rect)   
    
    #Player 2 score
    p2_score = font.render(str([paddle2.points]), 1, WHITE)
    p2Rect = p2_score.get_rect()
    p2Rect.center = (700, 50)      
    WINDOW.blit(p2_score, p2Rect)
    
    all_sprites.draw(WINDOW)    
    pygame.display.update()     


def draw_winner(text): 
    draw_text = WINNER_FONT.render(text, 1, WHITE)
    WINDOW.blit(draw_text, (WIDTH//2 - draw_text.get_width()/2, HEIGHT//2 - draw_text.get_height()/2))
    pygame.display.update()
    pygame.time.delay(5000)


def main():
    run = True

    while run:
        pygame.time.delay(10)   
       
        for event in pygame.event.get():    
            if event.type == pygame.QUIT:  
                run = False
                pygame.quit()

        winner_text = ""
        if paddle1.points >= 10:
            winner_text = "Blue Wins!"

        if paddle2.points >= 10:
            winner_text = "Red Wins!"

        if winner_text != "":
            draw_winner(winner_text)
            break
            
        # Paddle movement        
        key = pygame.key.get_pressed()  
        if key[pygame.K_w]:
            paddle1.rect.y -= paddle1.paddle_speed  
        if key[pygame.K_s]:
            paddle1.rect.y += paddle1.paddle_speed  
        if key[pygame.K_UP]:
            paddle2.rect.y -= paddle2.paddle_speed
        if key[pygame.K_DOWN]:
            paddle2.rect.y += paddle2.paddle_speed

        # Ensures paddles never move off the screen
        if paddle1.rect.y < 0:
            paddle1.rect.y = 0
        
        if paddle1.rect.y > 425:
            paddle1.rect.y = 425

        if paddle2.rect.y < 0:
            paddle2.rect.y = 0
        
        if paddle2.rect.y > 425:
            paddle2.rect.y = 425

        # Ball movement
        ball.rect.x += ball.speed * ball.dx     
        ball.rect.y += ball.speed * ball.dy     

        # Setting up collision detection with the walls by changing ball's direction
        if ball.rect.y > 490:   
            ball.dy = -1        

        if ball.rect.x > 740:   
            ball.rect.x, ball.rect.y = 375, 250     
            paddle1.points += 1

        if ball.rect.y < 10:    
            ball.dy = 1         

        if ball.rect.x < 10:    
            ball.rect.x, ball.rect.y = 375, 250
            paddle2.points += 1

        # Setting up collision detection with the paddles
        if paddle1.rect.colliderect(ball.rect):
            ball.dx = 1     

        if paddle2.rect.colliderect(ball.rect):
            ball.dx = -1

        redraw()    
        
    main()

if __name__ == "__main__":
    main()

No estoy seguro de si eso es un problema o no, ya que el texto se borra en la pantalla, pero si estás usando Python 3.x, tu rectángulo en draw_winner() obtiene dos flotantes en lugar de los 4 enteros debido a la verdadera división. Eso es lo que veo a primera vista. Sin embargo, no estoy seguro de querer jugar a tu juego, ya que no hay posibilidad de que ambos jugadores obtengan la misma cantidad de puntos. ¡Eso no es justo! :D

- Dalen

28 de marzo de 2021 a las 4:02

¡Lo siento! Pasas la posición a WINDOW.blit() como tupla de dos flotantes en lugar de enteros. Tengo sueño, no sé de qué rectángulo estaba hablando antes. :D Simplemente olvida esa parte.

- Dalen

28 de marzo de 2021 a las 4:11

"Entonces, cuando muestre el texto del ganador, debe pausar el juego durante 5 segundos, luego salir del bucle while y recuperar main()" Así lo hace. Sin embargo, volver a llamar a main() no restablece nada y no entiendoy por qué esperarías que así fuera. En particular, no entiendo por qué espera que cambie el texto ganador. El resultado es una recursividad infinita. De todos modos, no deberías usar una llamada recursiva; usa otro bucle while.

- Karl Knechtel

28 de marzo de 2021 a las 9:23

Intenta explicar, en palabras sencillas, el conjunto completo de pasos que crees que son necesarios para "restablecer" el juego. (Pista: ¿qué pasos tuviste que seguir inicialmente para poder iniciar el ciclo principal del juego?) Ahora, intenta envolver esa lógica en una función. ¿Queda claro cuándo y cuándo?¿Quieres usar eso?

- Karl Knechtel

28/03/2021 a las 9:25



------------------------------------

Primero echemos un vistazo profundo a su función main():

def main():
    run = True

    while run:
        #[...]some part of code here

        if winner_text != "":
            draw_winner(winner_text)
            break

        #[...]some part of code here
    main()

Ahora siempre que alguien gane paddle1.points = 10 o paddle2.points = 10

if paddle1.points >= 10:
    winner_text = "Blue Wins!"

if paddle2.points >= 10:
    winner_text = "Red Wins!"

if winner_text != "":
    draw_winner(winner_text)
    break

Ahora, tan pronto como alguien gane, se ejecuta esta parte de su código:

if winner_text != "":
    draw_winner(winner_text)
    break

Entonces, solo estás mostrando el texto como "El azul gana" o "El rojo gana" y luego sales del ciclo while y vuelves a llamar a la función main(), pero no restableces paddle1.points y paddle2.points a 0 cada uno.

Entonces tu función main() debería verse así:

def main():
    run = True
    paddle1.points = 0
    paddle2.points = 0
    while run:
        #[...]while loop here

Aquí está el código de trabajo completo:

import pygame,sys

pygame.init()

WIDTH, HEIGHT = 750, 500
WINDOW = pygame.display.set_mode((WIDTH, HEIGHT))   

pygame.display.set_caption("PONG")  

WINNER_FONT = pygame.font.SysFont('comicsans', 100)

BLUE = (0, 0, 255)          
RED = (255, 0, 0)           
YELLOW = (255, 255, 0)     
WHITE = (255, 255, 255)     
BLACK = (0, 0, 0)           

class Paddle(pygame.sprite.Sprite):     
    def __init__(self):
        pygame.sprite.Sprite.__init__(self)    
        self.image = pygame.Surface([10, 75])
        self.image.fill(WHITE)
        self.rect = self.image.get_rect()   
        self.paddle_speed = 4          
        self.points = 0

class Ball(pygame.sprite.Sprite):
    def __init__(self):
        pygame.sprite.Sprite.__init__(self)
        self.image = pygame.Surface([10, 10])
        self.image.fill(YELLOW)
        self.rect = self.image.get_rect()
        self.speed = 2
        self.dx = 1    
        self.dy = 1

paddle1 = Paddle()
paddle1.image.fill(BLUE)
paddle1.rect.x = 25      
paddle1.rect.y = 225     

paddle2 = Paddle()
paddle2.image.fill(RED)
paddle2.rect.x = 715
paddle2.rect.y = 225

ball = Ball()
ball.rect.x = 375
ball.rect.y = 250

all_sprites = pygame.sprite.Group() 
all_sprites.add(paddle1, paddle2, ball)


def redraw():
    WINDOW.fill(BLACK)
    font = pygame.font.SysFont("Consolas", 35)
    text = font.render("---[PONG]---", 1, YELLOW)
    textRect = text.get_rect()      
    textRect.center = (WIDTH//2, 25)  
    WINDOW.blit(text, textRect)     

    #Player 1 score
    p1_score = font.render(str([paddle1.points]), 1, WHITE)
    p1Rect = p1_score.get_rect()    
    p1Rect.center = (50, 50)        
    WINDOW.blit(p1_score, p1Rect)   
    
    #Player 2 score
    p2_score = font.render(str([paddle2.points]), 1, WHITE)
    p2Rect = p2_score.get_rect()
    p2Rect.center = (700, 50)      
    WINDOW.blit(p2_score, p2Rect)
    
    all_sprites.draw(WINDOW)    
    pygame.display.update()     


def draw_winner(text):
    draw_text = WINNER_FONT.render(text, 1, WHITE)
    WINDOW.blit(draw_text, (WIDTH//2 - draw_text.get_width()/2, HEIGHT//2 - draw_text.get_height()/2))
    pygame.display.update()
    pygame.time.delay(5000)


def main():
    run = True
    paddle1.points = 0
    paddle2.points = 0
    while run:
        pygame.time.delay(10)   
       
        for event in pygame.event.get():    
            if event.type == pygame.QUIT:  
                pygame.quit()
                sys.exit()

        winner_text = ""
        if paddle1.points >= 1:
            winner_text = "Blue Wins!"

        if paddle2.points >= 1:
            winner_text = "Red Wins!"

        if winner_text != "":
            draw_winner(winner_text)
            break
            
        # Paddle movement        
        key = pygame.key.get_pressed()  
        if key[pygame.K_w]:
            paddle1.rect.y -= paddle1.paddle_speed  
        if key[pygame.K_s]:
            paddle1.rect.y += paddle1.paddle_speed  
        if key[pygame.K_UP]:
            paddle2.rect.y -= paddle2.paddle_speed
        if key[pygame.K_DOWN]:
            paddle2.rect.y += paddle2.paddle_speed

        # Ensures paddles never move off the screen
        if paddle1.rect.y < 0:
            paddle1.rect.y = 0
        
        if paddle1.rect.y > 425:
            paddle1.rect.y = 425

        if paddle2.rect.y < 0:
            paddle2.rect.y = 0
        
        if paddle2.rect.y > 425:
            paddle2.rect.y = 425

        # Ball movement
        ball.rect.x += ball.speed * ball.dx     
        ball.rect.y += ball.speed * ball.dy     

        # Setting up collision detection with the walls by changing ball's direction
        if ball.rect.y > 490:   
            ball.dy = -1        

        if ball.rect.x > 740:   
            ball.rect.x, ball.rect.y = 375, 250     
            paddle1.points += 1

        if ball.rect.y < 10:    
            ball.dy = 1         

        if ball.rect.x < 10:    
            ball.rect.x, ball.rect.y = 375, 250
            paddle2.points += 1

        # Setting up collision detection with the paddles
        if paddle1.rect.colliderect(ball.rect):
            ball.dx = 1     

        if paddle2.rect.colliderect(ball.rect):
            ball.dx = -1

        redraw()    
    main()

if __name__ == "__main__":
    main()

Aunque esto funciona, te sugiero que sigas lo que ha dicho @Rabbid76, ya que invocar el bucle de la aplicación de forma recursiva nunca se considera una buena práctica

2

No solo es necesario restablecer los puntos, sino también el texto del ganador, etc. Hay todo un conjunto de técnicas que se deben aprender aquí, de verdad.

- Karl Knechtel

28 de marzo de 2021 a las 9:24

Gracias por el consejo, no puedo creer que no haya reiniciado los puntos, debo haber estado medio dormido cuando hice esta estúpida pregunta. Así lo solucioné para evitar el uso de una llamada recursiva. Incrusté "paddle1.points = 0" y "paddle2.points = 0" en el campo "si ganador_texto!=" " " después de "draw_winner(winner_text)".

-L393nd

2 de abril de 2021 a las 20:12



------------------------------------

Nunca invoque la aplicación loop de forma recursiva y nunca "esperar" en el bucle de la aplicación. Ver ¿Cómo esperar un tiempo en pygame? y Python: ¿Cuál es la mejor manera de crear múltiples bucles en pygame?

Crea una función de inicio que inicialice todos los estados del juego:

def init():
    global paddle1, paddle2, ball, all_sprites

    paddle1 = Paddle()
    paddle1.image.fill(BLUE)
    paddle1.rect.x = 25      
    paddle1.rect.y = 225     

    paddle2 = Paddle()
    paddle2.image.fill(RED)
    paddle2.rect.x = 715
    paddle2.rect.y = 225

    ball = Ball()
    ball.rect.x = 375
    ball.rect.y = 250

    all_sprites = pygame.sprite.Group() 
    all_sprites.add(paddle1, paddle2, ball)

Llame a la función init cuando sea necesario reiniciar el juego:

def main():
    run = True
    while run:
        # [...]

        if winner_text != "":
            draw_winner(winner_text)
            init()

        # [...]

    # main() <--- DELETE

Agregue un estado game_over y use pygame.time.get_ticks() para obtener el número de milisegundos desde que se llamó a pygame.init(). Cuando finalice el juego, establezca el estado game_over y calcule la hora a la que se debe reiniciar el juego. Cuando llegue el momento, reinicia game_over y llama a init. Dibuja la escena dependiendo del game_over:

def main():
    init()
  
    winner_text = ""
    restart_time = 0
    game_over = False

    clock = pygame.time.Clock()
    run = True
    while run:
        clock.tick(100)
        current_time = pygame.time.get_ticks()
       
        # [...]

        if not game_over:
            if paddle1.points >= 10:
                winner_text = "Blue Wins!"
                restart_time = current_time + 5000
                game_over = True

            if paddle2.points >= 10:
                winner_text = "Red Wins!"
                restart_time = current_time + 5000
                game_over = True

        if game_over:
            draw_winner(winner_text)
            if current_time > restart_time:
                init()
                game_over = False
            else: 
                continue

        # [...]

Utiliza pygame.time.Clock para controlar los fotogramas por segundo y, por tanto, la velocidad del juego.

El método tick() de un objeto pygame.time.Clock, delJuega el juego de esa manera, que cada iteración del ciclo consume el mismo período de tiempo. Ver pygame.time.Clock.tick():

Este método debe llamarse una vez por fotograma.

Eso significa que el bucle:

clock = pygame.time.Clock()
run = True
while run:
   clock.tick(100)

se ejecuta 100 veces por segundo.

Ejemplo completo:

import pygame

pygame.init()

WIDTH, HEIGHT = 750, 500
WINDOW = pygame.display.set_mode((WIDTH, HEIGHT))   

pygame.display.set_caption("PONG")  

WINNER_FONT = pygame.font.SysFont('comicsans', 100)

BLUE = (0, 0, 255)          
RED = (255, 0, 0)           
YELLOW = (255, 255, 0)     
WHITE = (255, 255, 255)     
BLACK = (0, 0, 0)           

class Paddle(pygame.sprite.Sprite):     
    def __init__(self):
        pygame.sprite.Sprite.__init__(self)    
        self.image = pygame.Surface([10, 75])
        self.image.fill(WHITE)
        self.rect = self.image.get_rect()   
        self.paddle_speed = 4          
        self.points = 0

class Ball(pygame.sprite.Sprite):
    def __init__(self):
        pygame.sprite.Sprite.__init__(self)
        self.image = pygame.Surface([10, 10])
        self.image.fill(YELLOW)
        self.rect = self.image.get_rect()
        self.speed = 2
        self.dx = 1    
        self.dy = 1

def init():
    global paddle1, paddle2, ball, all_sprites

    paddle1 = Paddle()
    paddle1.image.fill(BLUE)
    paddle1.rect.x = 25      
    paddle1.rect.y = 225     

    paddle2 = Paddle()
    paddle2.image.fill(RED)
    paddle2.rect.x = 715
    paddle2.rect.y = 225

    ball = Ball()
    ball.rect.x = 375
    ball.rect.y = 250

    all_sprites = pygame.sprite.Group() 
    all_sprites.add(paddle1, paddle2, ball)

def redraw():
    WINDOW.fill(BLACK)
    font = pygame.font.SysFont("Consolas", 35)
    text = font.render("---[PONG]---", 1, YELLOW)
    textRect = text.get_rect()      
    textRect.center = (WIDTH//2, 25)  
    WINDOW.blit(text, textRect)     

    #Player 1 score
    p1_score = font.render(str([paddle1.points]), 1, WHITE)
    p1Rect = p1_score.get_rect()    
    p1Rect.center = (50, 50)        
    WINDOW.blit(p1_score, p1Rect)   
    
    #Player 2 score
    p2_score = font.render(str([paddle2.points]), 1, WHITE)
    p2Rect = p2_score.get_rect()
    p2Rect.center = (700, 50)      
    WINDOW.blit(p2_score, p2Rect)
    
    all_sprites.draw(WINDOW)    
    pygame.display.update()     


def draw_winner(text): 
    draw_text = WINNER_FONT.render(text, 1, WHITE)
    WINDOW.blit(draw_text, (WIDTH//2 - draw_text.get_width()/2, HEIGHT//2 - draw_text.get_height()/2))
    pygame.display.update()


def main():
    init()
  
    winner_text = ""
    restart_time = 0
    game_over = False

    clock = pygame.time.Clock()
    run = True
    while run:
        clock.tick(100)
        current_time = pygame.time.get_ticks()
       
        for event in pygame.event.get():    
            if event.type == pygame.QUIT:  
                run = False
                pygame.quit()

        if not game_over:
            if paddle1.points >= 10:
                winner_text = "Blue Wins!"
                restart_time = current_time + 5000
                game_over = True

            if paddle2.points >= 10:
                winner_text = "Red Wins!"
                restart_time = current_time + 5000
                game_over = True

        if game_over:
            draw_winner(winner_text)
            if current_time > restart_time:
                init()
                game_over = False
            else: 
                continue
            
        # Paddle movement        
        key = pygame.key.get_pressed()  
        if key[pygame.K_w]:
            paddle1.rect.y -= paddle1.paddle_speed  
        if key[pygame.K_s]:
            paddle1.rect.y += paddle1.paddle_speed  
        if key[pygame.K_UP]:
            paddle2.rect.y -= paddle2.paddle_speed
        if key[pygame.K_DOWN]:
            paddle2.rect.y += paddle2.paddle_speed

        # Ensures paddles never move off the screen
        if paddle1.rect.y < 0:
            paddle1.rect.y = 0
        
        if paddle1.rect.y > 425:
            paddle1.rect.y = 425

        if paddle2.rect.y < 0:
            paddle2.rect.y = 0
        
        if paddle2.rect.y > 425:
            paddle2.rect.y = 425

        # Ball movement
        ball.rect.x += ball.speed * ball.dx     
        ball.rect.y += ball.speed * ball.dy     

        # Setting up collision detection with the walls by changing ball's direction
        if ball.rect.y > 490:   
            ball.dy = -1        

        if ball.rect.x > 740:   
            ball.rect.x, ball.rect.y = 375, 250     
            paddle1.points += 1

        if ball.rect.y < 10:    
            ball.dy = 1         

        if ball.rect.x < 10:    
            ball.rect.x, ball.rect.y = 375, 250
            paddle2.points += 1

        # Setting up collision detection with the paddles
        if paddle1.rect.colliderect(ball.rect):
            ball.dx = 1     

        if paddle2.rect.colliderect(ball.rect):
            ball.dx = -1

        redraw()    

if __name__ == "__main__":
    main()

1

¡Gracias por la ayuda! Utilicé una solución rápida para evitar una llamada recursiva: incrusté "paddle1.points = 0" y "paddle2.points = 0" en el campo "si ganador_texto!=" " " después del "draw_winner(winner_text)".

-L393nd

2 de abril de 2021 a las 20:22

Su guía para un futuro mejor - libreflare
Su guía para un futuro mejor - libreflare