简体   繁体   中英

Game Over Screen using Pygame

I am trying to finish up a version of pong and I want the game to end when the player misses the ball and it hits the wall behind the paddle. I have been trying to build a Game Over screen for this and have had no luck. I want the user to have an option to restart a new game or quit the screen all together.

# Import the pygame library and initialise the game 
# the scoring system will be inside of the main game
# the paddle and ball classes are imported into the main game
# still trying to work out how to make the other paddle move without keyboard input
import pygame
from Paddle import Paddle
from Ball import Ball
#while running loop
     #rest of the code

pygame.init()
black = (0,0,0)
white = (255,255,255)


paddle2 = Paddle(white, 10, 100)
paddle2.rect.x = 670
paddle2.rect.y = 200

ball = Ball(white,8,8)
ball.rect.x = 345
ball.rect.y = 195

width = 700
height = 500

size = (700, 500)
screen = pygame.display.set_mode(size)
pygame.display.set_caption("Pong")

user_lose = False

sprites_list = pygame.sprite.Group()

sprites_list.add(paddle2)
sprites_list.add(ball)

carryOn = True

clock = pygame.time.Clock()

#Set inital player scores:
scoreA = 0
scoreB = 0



# -------- Main Program Loop -----------
while carryOn:
    for event in pygame.event.get(): 
        if event.type == pygame.QUIT: 
              carryOn = False 
        elif event.type==pygame.KEYDOWN:
                if event.key==pygame.K_x: 
                     carryOn=False 


    keys = pygame.key.get_pressed()
    if keys[pygame.K_UP]:
        paddle2.up(5)
    if keys[pygame.K_DOWN]:
        paddle2.down(5)

    sprites_list.update()

    if ball.rect.x>=690:
        scoreA+=1
        ball.velocity[0] = -ball.velocity[0]
    if ball.rect.x<=0:
        scoreB+=1
        ball.velocity[0] = -ball.velocity[0]
    if ball.rect.y>490:
        ball.velocity[1] = -ball.velocity[1]
    if ball.rect.y<0:
        ball.velocity[1] = -ball.velocity[1]  

    if pygame.sprite.collide_mask(ball, paddle2):
        ball.bounce()

    screen.fill(black)

    sprites_list.draw(screen) 

    #Scores displayed:
    font = pygame.font.Font(None, 74)
    font2 = pygame.font.Font(None, 55)
    """
    text = font.render(str(scoreA), 1, white)
    screen.blit(text, (250,10))
    """
    text = font.render(str(scoreB), 1, white)
    screen.blit(text, (430,10))
    text = font.render(str("Score: "), 1 ,white)
    screen.blit(text, (250, 10))
    text = font2.render(str("Keep The Ball Bouncing"), 1, white)
    screen.blit(text, (155,60))

    pygame.display.flip()
    clock.tick(60)

pygame.quit()

You can use variable state = 'INTRO'/'GAME'/'GAMEOVER' or separated variables is_intro = True/False , is_game = True/False , is_gameover = True/False , is_pause = True/False to control what code execute in main loop

It would need also function reset_all_values which use global to reset external/global values.

Something like this.

import pygame

def reset_all_values():
    global scoreA
    global scoreB

    scoreA = 0
    scoreB = 0

    # reset other values

# --- main ---

size = (700, 500)

pygame.init()
screen = pygame.display.set_mode(size)

clock = pygame.time.Clock()

font = pygame.font.Font(None, 50)

reset_all_values()

# -------- Main Program Loop -----------

state = 'INTRO'

carry_on = True

while carry_on:

    # --- events ---

    for event in pygame.event.get(): 
        if event.type == pygame.QUIT: 
            carryOn = False 
        elif event.type == pygame.KEYDOWN:
            if event.key == pygame.K_x: 
                carry_on = False

            if state == 'INTRO':
                if event.key == pygame.K_ESCAPE:                    
                    state = 'GAME'
                    reset_all_values() # 
                # other keys for intro
            elif state == 'GAME':
                if event.key == pygame.K_ESCAPE: 
                    state = 'GAMEOVER'
                # other keys for game
            elif state == 'GAMEOVER':
                if event.key == pygame.K_ESCAPE: 
                    state = 'INTRO'
                    #reset_all_values() # TODO
                # other keys for gameover


    # --- changes/moves/collisions ---

    if state == 'INTRO':
        pass
    elif state == 'GAME':
        scoreA += 1
        if scoreA >= 100:
            state = 'GAMEOVER'
    elif state == 'GAMEOVER':
        pass

    # --- draws ---

    screen.fill((0,0,0))

    if state == 'INTRO':
        text = font.render('INTRO - Press ESC', True, (255,255,255))
        rect = text.get_rect(center=screen.get_rect().center)
        screen.blit(text, rect)
    elif state == 'GAME':
        text = font.render('GAME - Wait for Score 100', True, (255,255,255))
        rect = text.get_rect(center=screen.get_rect().center)
        screen.blit(text, rect)
        text = font.render(f'SCORE {scoreA}' , True, (255,255,255))
        rect = text.get_rect()
        screen.blit(text, rect)
    elif state == 'GAMEOVER':
        text = font.render('GAMEOVER - Press ESC', True, (255,255,255))
        rect = text.get_rect(center=screen.get_rect().center)
        screen.blit(text, rect)
        text = font.render(f'SCORE {scoreA}' , True, (255,255,255))
        rect = text.get_rect()
        screen.blit(text, rect)

    pygame.display.flip()
    clock.tick(30)

pygame.quit()

You can also put code in functions like intro_event_handle() , intro_change() , intro_draw() , etc. to make it more readable.

import pygame


def intro_handle_event(event):
    global state

    if event.type == pygame.KEYDOWN:
        if event.key == pygame.K_ESCAPE:                    
            state = 'GAME'
            reset_all_values() # 
        # other keys for intro

def game_handle_event(event):
    global state

    if event.type == pygame.KEYDOWN:
        if event.key == pygame.K_ESCAPE: 
            state = 'GAMEOVER'
        # other keys for game

def gameover_handle_event(event):
    global state

    if event.type == pygame.KEYDOWN:
        if event.key == pygame.K_ESCAPE: 
            state = 'INTRO'
        # other keys for gameover

def intro_change():
    pass

def game_change():    
    global state
    global scoreA

    scoreA += 1
    if scoreA >= 100:
        state = 'GAMEOVER'

def gameover_change():
    pass

def intro_draw(screen):
    text = font.render('INTRO - Press ESC', True, (255,255,255))
    rect = text.get_rect(center=screen.get_rect().center)
    screen.blit(text, rect)

def game_draw(screen):
    text = font.render('GAME - Wait for SCORE 100', True, (255,255,255))
    rect = text.get_rect(center=screen.get_rect().center)
    screen.blit(text, rect)
    text = font.render(f'SCORE {scoreA}' , True, (255,255,255))
    rect = text.get_rect()
    screen.blit(text, rect)

def gameover_draw(screen):
    text = font.render('GAMEOVER - Press ESC', True, (255,255,255))
    rect = text.get_rect(center=screen.get_rect().center)
    screen.blit(text, rect)
    text = font.render(f'SCORE {scoreA}' , True, (255,255,255))
    rect = text.get_rect()
    screen.blit(text, rect)

def reset_all_values():
    global scoreA
    global scoreB

    scoreA = 0
    scoreB = 0

    # reset other values

# --- main ---

size = (700, 500)

pygame.init()
screen = pygame.display.set_mode(size)

clock = pygame.time.Clock()

font = pygame.font.Font(None, 50)

reset_all_values()

# -------- Main Program Loop -----------

state = 'INTRO'

carry_on = True

while carry_on:

    # --- events ---

    for event in pygame.event.get(): 
        if event.type == pygame.QUIT: 
            carryOn = False 
        elif event.type == pygame.KEYDOWN:
            if event.key == pygame.K_x: 
                carry_on = False

        if state == 'INTRO':
            intro_handle_event(event)
        elif state == 'GAME':
            game_handle_event(event)
        elif state == 'GAMEOVER':
            gameover_handle_event(event)

    # --- changes/moves/collisions ---

    if state == 'INTRO':
        intro_change()
    elif state == 'GAME':
        game_change()
    elif state == 'GAMEOVER':
        gameover_change()

    # --- draws ---

    screen.fill((0,0,0))

    if state == 'INTRO':
        intro_draw(screen)
    elif state == 'GAME':
        game_draw(screen)
    elif state == 'GAMEOVER':
        gameover_draw(screen)

    pygame.display.flip()
    clock.tick(30)

pygame.quit()

You may keep functions in dictionary

handle_event = {
   'INTRO': intro_handle_event,
   'GAME': game_handle_event,
   'GAMEOVER': gameover_handle_event,
}

and use only state (instead of if/elif ) to execute correct function.

handle_event[state](event) 

import pygame


def intro_handle_event(event):
    global state

    if event.type == pygame.KEYDOWN:
        if event.key == pygame.K_ESCAPE:                    
            state = 'GAME'
            reset_all_values() # 
        # other keys for intro

def game_handle_event(event):
    global state

    if event.type == pygame.KEYDOWN:
        if event.key == pygame.K_ESCAPE: 
            state = 'GAMEOVER'
        # other keys for game

def gameover_handle_event(event):
    global state

    if event.type == pygame.KEYDOWN:
        if event.key == pygame.K_ESCAPE: 
            state = 'INTRO'
        # other keys for gameover

def intro_change():
    pass

def game_change():    
    global state
    global scoreA

    scoreA += 1
    if scoreA >= 100:
        state = 'GAMEOVER'

def gameover_change():
    pass

def intro_draw(screen):
    text = font.render('INTRO - Press ESC', True, (255,255,255))
    rect = text.get_rect(center=screen.get_rect().center)
    screen.blit(text, rect)

def game_draw(screen):
    text = font.render('GAME - Wait for SCORE 100', True, (255,255,255))
    rect = text.get_rect(center=screen.get_rect().center)
    screen.blit(text, rect)
    text = font.render(f'SCORE {scoreA}' , True, (255,255,255))
    rect = text.get_rect()
    screen.blit(text, rect)

def gameover_draw(screen):
    text = font.render('GAMEOVER - Press ESC', True, (255,255,255))
    rect = text.get_rect(center=screen.get_rect().center)
    screen.blit(text, rect)
    text = font.render(f'SCORE {scoreA}' , True, (255,255,255))
    rect = text.get_rect()
    screen.blit(text, rect)

def reset_all_values():
    global scoreA
    global scoreB

    scoreA = 0
    scoreB = 0

    # reset other values

# --- main ---

size = (700, 500)

pygame.init()
screen = pygame.display.set_mode(size)

clock = pygame.time.Clock()

font = pygame.font.Font(None, 50)

reset_all_values()

handle_event = {
   'INTRO': intro_handle_event,
   'GAME': game_handle_event,
   'GAMEOVER': gameover_handle_event,
}

change = {
   'INTRO': intro_change,
   'GAME': game_change,
   'GAMEOVER': gameover_change,
}

draw = {
   'INTRO': intro_draw,
   'GAME': game_draw,
   'GAMEOVER': gameover_draw,
}

# -------- Main Program Loop -----------

state = 'INTRO'

carry_on = True

    while carry_on:

        # --- events ---

        for event in pygame.event.get(): 
            if event.type == pygame.QUIT: 
                carryOn = False 
            elif event.type == pygame.KEYDOWN:
                if event.key == pygame.K_x: 
                    carry_on = False

            handle_event[state](event)

        # --- changes/moves/collisions ---

        change[state]()

        # --- draws ---

        screen.fill((0,0,0))

        draw[state](screen)

        pygame.display.flip()
        clock.tick(30)

    pygame.quit()

In similar way you can keep it in classes.

import pygame

class Intro():

    def __inti__(self):
        self.reset()

    def reset(self):
        pass

    def handle_event(self, event):
        global state

        if event.type == pygame.KEYDOWN:
            if event.key == pygame.K_ESCAPE:                    
                state = 'GAME'
                scene[state].reset() 
            # other keys for intro

    def change(self):
        pass

    def draw(self, screen):
        text = font.render('INTRO - Press ESC', True, (255,255,255))
        rect = text.get_rect(center=screen.get_rect().center)
        screen.blit(text, rect)

class Game():

    def __inti__(self):
        self.reset()

    def reset(self):
        global scoreA
        global scoreB

        scoreA = 0
        scoreB = 0

        # reset other values

    def handle_event(self, event):
        global state

        if event.type == pygame.KEYDOWN:
            if event.key == pygame.K_ESCAPE: 
                state = 'GAMEOVER'
                scene[state].reset()
            # other keys for game

    def change(self):    
        global state
        global scoreA

        scoreA += 1
        if scoreA >= 100:
            state = 'GAMEOVER'
            scene[state].reset()

    def draw(self, screen):
        text = font.render('GAME - Wait for SCORE 100', True, (255,255,255))
        rect = text.get_rect(center=screen.get_rect().center)
        screen.blit(text, rect)
        text = font.render(f'SCORE {scoreA}' , True, (255,255,255))
        rect = text.get_rect()
        screen.blit(text, rect)


class GameOver():

    def __inti__(self):
        self.reset()

    def reset(self):
        pass

    def handle_event(self, event):
        global state

        if event.type == pygame.KEYDOWN:
            if event.key == pygame.K_ESCAPE: 
                state = 'INTRO'
                scene[state].reset()
            # other keys for gameover

    def change(self):
        pass

    def draw(self, screen):
        text = font.render('GAMEOVER - Press ESC', True, (255,255,255))
        rect = text.get_rect(center=screen.get_rect().center)
        screen.blit(text, rect)
        text = font.render(f'SCORE {scoreA}' , True, (255,255,255))
        rect = text.get_rect()
        screen.blit(text, rect)

# --- main ---

size = (700, 500)

pygame.init()
screen = pygame.display.set_mode(size)

clock = pygame.time.Clock()

font = pygame.font.Font(None, 50)

scene = {
   'INTRO': Intro(),
   'GAME': Game(),
   'GAMEOVER': GameOver(),
}

# -------- Main Program Loop -----------

state = 'INTRO'

carry_on = True

while carry_on:

    # --- events ---

    for event in pygame.event.get(): 
        if event.type == pygame.QUIT: 
            carryOn = False 
        elif event.type == pygame.KEYDOWN:
            if event.key == pygame.K_x: 
                carry_on = False

        scene[state].handle_event(event)

    # --- changes/moves/collisions ---

    scene[state].change()

    # --- draws ---

    screen.fill((0,0,0))

    scene[state].draw(screen)

    pygame.display.flip()
    clock.tick(30)

pygame.quit()

This way you build Finite State Machine


Other method can be create separated loops for every states and put it in external loop which will run selected loop or it will exit game.

import pygame

def intro_loop(screen):

    print('start intro loop')

    carry_on = True
    next_state = None

    while carry_on and not next_state:

        # --- events ---

        for event in pygame.event.get(): 
            if event.type == pygame.QUIT: 
                carry_on = False 
            elif event.type == pygame.KEYDOWN:
                if event.key == pygame.K_x: 
                    carry_on = False

                if event.key == pygame.K_ESCAPE:                    
                    next_state = 'GAME'

        # --- changes/moves/collisions ---


        # --- draws ---

        screen.fill((0,0,0))

        text = font.render('INTRO - Press ESC', True, (255,255,255))
        rect = text.get_rect(center=screen.get_rect().center)
        screen.blit(text, rect)

        pygame.display.flip()
        clock.tick(30)

    return carry_on, next_state 

def game_loop(screen):
    global scoreA
    global scoreB

    scoreA = 0
    scoreB = 0

    # reset other values

    print('start game loop')

    carry_on = True
    next_state = None

    while carry_on and not next_state:

        # --- events ---

        for event in pygame.event.get(): 
            if event.type == pygame.QUIT: 
                carry_on = False 
            elif event.type == pygame.KEYDOWN:
                if event.key == pygame.K_x: 
                    carry_on = False

                if event.key == pygame.K_ESCAPE: 
                    next_state = 'GAMEOVER'

        # --- changes/moves/collisions ---

        scoreA += 1
        if scoreA >= 100:
            next_state = 'GAMEOVER'

        # --- draws ---

        screen.fill((0,0,0))

        text = font.render('GAME - Wait for SCORE 100', True, (255,255,255))
        rect = text.get_rect(center=screen.get_rect().center)
        screen.blit(text, rect)
        text = font.render(f'SCORE {scoreA}' , True, (255,255,255))
        rect = text.get_rect()
        screen.blit(text, rect)

        pygame.display.flip()
        clock.tick(30)

    return carry_on, next_state 

def gameover_loop(screen):

    print('start gameover loop')

    carry_on = True
    next_state = None

    while carry_on and not next_state:

        # --- events ---

        for event in pygame.event.get(): 
            if event.type == pygame.QUIT: 
                carry_on = False 
            elif event.type == pygame.KEYDOWN:
                if event.key == pygame.K_x: 
                    carry_on = False

                if event.key == pygame.K_ESCAPE:                    
                    next_state = 'INTRO'

        # --- changes/moves/collisions ---


        # --- draws ---

        screen.fill((0,0,0))

        text = font.render('GAMEOVER - Press ESC', True, (255,255,255))
        rect = text.get_rect(center=screen.get_rect().center)
        screen.blit(text, rect)
        text = font.render(f'SCORE {scoreA}' , True, (255,255,255))
        rect = text.get_rect()
        screen.blit(text, rect)

        pygame.display.flip()
        clock.tick(30)

    return carry_on, next_state 

# --- main ---

size = (700, 500)

pygame.init()
screen = pygame.display.set_mode(size)

clock = pygame.time.Clock()

font = pygame.font.Font(None, 50)

# -------- Main Program Loop -----------

state = 'INTRO'
carry_on = True

while carry_on:

    if state == 'INTRO':
        carry_on, state = intro_loop(screen)
    elif state == 'GAME':
        carry_on, state = game_loop(screen)
    elif state == 'GAMEOVER':
        carry_on, state = gameover_loop(screen)

pygame.quit()

I assume from the way you have phrased this that your problem is the rerunning of the game, not the game over screen that asks the question. Assuming that is the case...

I disagree with the approach of trying to combine different states into a single game loop. It can certainly be done, but there is no real good reason to do it in this type of scenario. It makes the code harder to follow without any benefit gained from structuring it that way. There are situations where a state machine approach makes sense, but this really is not one of them.

Adding a an extra loop with a question about restarting the game can be handled much simpler than that. Take the code from

carryOn = True

to

pygame.display.flip()
clock.tick(60)

except for the line:

clock = pygame.time.Clock()

and move all of it into a method called something like run_game()

You would also need to move the lines that initialize the paddle and ball positions, and the user_lose = False and move that to the beginning of the run_game() method.

Then when you want to run the game you call that. After it exits you can ask if they want to play again and call it again if they do, or exit if they chose not to. You would need that in a loop of course to repeat this more than once.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM