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.