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
- Dalen28 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.
- Dalen28 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 Knechtel28 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 Knechtel28/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 Knechtel28 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)".
-L393nd2 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)".
-L393nd2 de abril de 2021 a las 20:22