Pong - 当任一玩家达到分数阈值时崩溃而不是重置游戏

Pong - crashes instead of resetting the game when either player reached a score threshold

问题是每次显示获胜者文本后游戏都会崩溃(如果任一玩家的分数达到 10 或更高)并且未能重新调用 main() 函数,正如我所说的那样从我下面的代码中看到。因此,当它显示获胜者文本时,它应该暂停游戏 5 秒,然后跳出 while 循环并调用 main(),但它在显示获胜者文本后立即崩溃(见下面的屏幕截图)。获胜文本的逻辑在我的 main() 函数的顶部附近。

不确定根本原因是什么,因为我在程序顶部调用了 pygame.init()。任何帮助将不胜感激!完整的代码在下面...如果有人可以 运行 它并让我知道它崩溃的根本原因是什么,那就太好了。提前致谢!

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

切勿递归调用应用程序循环,切勿在应用程序循环中“等待”。参见 How to wait some time in pygame? and

创建一个 init 函数来初始化所有游戏状态:

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)

需要重启游戏时调用init函数:

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

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

        # [...]

    # main() <--- DELETE

添加一个game_over状态并使用pygame.time.get_ticks() to get the number of milliseconds since pygame.init()被调用。当游戏结束时,设置状态game_over并计算必须重新开始游戏的时间。到时候,重置 game_over 并调用 init。根据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

        # [...]

使用pygame.time.Clock控制每秒帧数,从而控制游戏速度。

方法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.

这意味着循环:

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

每秒运行 100 次。


完整示例:

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

首先让我们深入了解一下您的 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()

现在只要有人赢了 paddle1.points = 10 或 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

现在,一旦有人获胜,您的这部分代码就会被执行:

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

因此,您只是显示“蓝色获胜”或“红色获胜”之类的文本,然后跳出 while 循环并再次调用 main() 函数,但您并未重置paddle1.pointspaddle2.points 各为 0。

所以您的 main() 函数应该类似于:

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

这是完整的工作代码:

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

虽然这可行,但我建议您按照@Rabbid76 所说的去做,因为递归调用应用程序循环从来都不是一个好的做法