Как отметили комментаторы, игра блокируется во время вызовов time.sleep(2)
.
На самом деле в любой программе, управляемой событиями, не идеально блокировать весь процесс с помощью цикла ожидания,Особенно в цикле, который обрабатывает события, так как это может иметь другие последствия для рисования на экране.
Так как же вы кодируете это?С State Machine .
Представленная выше игра, кажется, имеет три состояния: «битва», «результаты» и «геймплей» (предположительно).В зависимости от того, в каком состоянии находится игра, дисплей может отличаться, а пользовательский ввод обрабатывается по-разному.
Во-первых, давайте определим некоторые состояния для игры, используя python Перечислимый тип .
import enum
class GameState( enum.Enum ):
BATTLE = 1
WONGAME = 2
GAMEOVER = 3
STARTMENU = 4
Это позволяет нам связывать текущее состояние с понятным для человека именем:
game_state = GameState.BATTLE
...
if ( game_state == GameState.GAMEOVER ):
...
Использовать перечисляемый тип не обязательноэто, просто описание строки будет достаточно.Но использование перечислимого типа - это, как правило, то, как такого рода вещи обрабатываются в автомате (и обычно для программного обеспечения в целом).В качестве бонуса он ловит опечатки с ошибками компиляции.
Так или иначе, поэтому во время рисования окна и обработки событий код проверяет текущее состояние игры, чтобы увидеть, как рисовать экран:
### Main Loop
# Re-draw the screen
if ( game_state == GameState.STARTMENU ):
WINDOW.fill( NAVY_BLUE )
drawMenu( start_menu )
elif ( game_state == GameState.BATTLE ):
SPRITES.update()
WINDOW.fill( INKY_BLACK )
SPRITES.draw( WINDOW )
...
elif ( game_state == GameState.GAMEOVER ):
WINDOW.fill( INKY_BLACK )
# tell user they failed
...
elif ( game_state == GameState.WONGAME ):
WINDOW.fill( bg_colour ) #code for clearing screen
WINDOW.blit( prevwindow, ( 150,100 ) ) #drawing menus
#informing they've won
text.textDisplay( "arena champion!", 300, 140, True, 14, "center", WINDOW )
if ( arenaComplete == False ):
...
# Update the window, but not more than 60fps
pygame.display.flip()
clock.tick_busy_loop( 60 )
Аналогично, при обработке событий, например, при нажатии клавиш, иногда требуется другое, в зависимости от состояния.Например, если мы находимся в GameState.STARTMENU
нажатие клавиш со стрелками может изменить выделение меню, но в GateState.BATTLE
они перемещают игрока.Это немного связано, но это не сложно.У этого также есть побочный эффект принудительной жесткой простоты обработки, что делает исходный код более аккуратным.
# Handle user input
for event in pygame.event.get():
if ( event.type == pygame.QUIT ):
done = True
elif (event.type == pygame.KEYDOWN):
keys = pygame.key.get_pressed()
if ( game_state == GameState.SOMEMENU ):
# Up/Down changes selecton
if ( keys[pygame.K_UP] ):
my_menu.selectPrevious()
elif ( keys[pygame.K_DOWN] ):
my_menu.selectNext()
elif ( keys[pygame.K_ENTER] ):
# Do item from menu
...
elif ( game_state == GameState.GAMEOVER ):
# any key-press go back to main menu
game_state = GameState.STARTMENU
elif ( game_state == GameState.BATTLE ):
if ( keys[pygame.K_UP] ):
player_sprite.moveUp()
elif ( keys[pygame.K_DOWN] ):
player_sprite.moveDown()
...
Этот метод конечного автомата позволяет легко переключать контекст экрана программы.Это означает, что нет необходимости в time.sleep()
задержках, чтобы показать что-то пользователю.Он может оставаться на экране «выиграл», пока не будет нажата клавиша.
Здесь я привожу простой пример, когда нажатие любой клавиши циклически переключает три состояния.Каждое состояние показывает свой экран.Увы, в настоящее время у меня нет времени и для реализации обработчика нажатий клавиш, управляемого государством.
import pygame
import random
import time
import enum
# Window size
WINDOW_WIDTH = 400
WINDOW_HEIGHT = 400
# background colours
NAVY_BLUE = ( 28, 20, 186)
INKY_BLACK = ( 0, 0, 0)
CRIMSON = (195, 11, 41)
BAD_SNOW = (255, 252, 216)
class GameState( enum.Enum ):
MOVING = 1
MENU = 2
GAMEOVER = 3
class MovingSprite( pygame.sprite.Sprite ):
""" A bouncing Eyeball, just because """
def __init__( self ):
pygame.sprite.Sprite.__init__( self )
self.image = pygame.image.load("eyeball_32.png").convert_alpha()
self.rect = self.image.get_rect()
self.newPosition()
def newPosition( self ):
# Position to somewhere random
self.rect.center = ( random.randrange( 0, WINDOW_WIDTH ), random.randrange( 0, WINDOW_HEIGHT ) )
def update( self ):
self.newPosition()
### MAIN
pygame.init()
pygame.font.init()
WINDOW = pygame.display.set_mode( ( WINDOW_WIDTH, WINDOW_HEIGHT ), pygame.HWSURFACE|pygame.DOUBLEBUF|pygame.RESIZABLE )
pygame.display.set_caption("State Machine Example")
# Add some sprites
MOVERS = pygame.sprite.Group() # a group, for a single sprite
for i in range(3):
MOVERS.add( MovingSprite() )
# Font for menus (and whatever)
text_font = pygame.font.Font( None, 60 ) # just a default font
clock = pygame.time.Clock()
done = False
game_state = GameState.MOVING
while not done:
# Handle user-input
for event in pygame.event.get():
if ( event.type == pygame.QUIT ):
done = True
elif ( event.type == pygame.VIDEORESIZE ):
WINDOW_WIDTH = event.w
WINDOW_HEIGHT = event.h
WINDOW = pygame.display.set_mode( ( WINDOW_WIDTH, WINDOW_HEIGHT ), pygame.HWSURFACE|pygame.DOUBLEBUF|pygame.RESIZABLE )
elif ( event.type == pygame.KEYDOWN ):
# Move to next state on any keypress, with loop
if ( game_state == GameState.MOVING ):
game_state = GameState.MENU
elif ( game_state == GameState.MENU ):
game_state = GameState.GAMEOVER
elif ( game_state == GameState.GAMEOVER ):
game_state = GameState.MOVING
# Repaint the screen, depending on the state
if ( game_state == GameState.MOVING ):
MOVERS.update() # re-position the flower-pot
WINDOW.fill( INKY_BLACK )
MOVERS.draw( WINDOW ) # draw the flower-pot
elif ( game_state == GameState.MENU ):
WINDOW.fill( NAVY_BLUE )
menu_items = [ "1. Something", "2. Something Else", "3. Third Choice" ]
total_height = 0 # use to tally menu height (for centering)
max_width = 0
# Make the menu images, work out the size it needs
for i in range( len( menu_items ) ):
# convert text into image, in-place
menu_items[i] = text_font.render( menu_items[i], True, BAD_SNOW )
max_width = max( max_width, menu_items[i].get_width() )
total_height = total_height + menu_items[i].get_height()
# Now draw to screen
cursor_x = ( WINDOW_WIDTH - max_width ) // 2 # centred on largest menu item
cursor_y = ( WINDOW_HEIGHT - total_height ) // 2
for i in range( len( menu_items ) ):
WINDOW.blit( menu_items[i], ( cursor_x, cursor_y ) )
cursor_y += 5 + menu_items[i].get_height() # move down height, plus a bit
elif ( game_state == GameState.GAMEOVER ):
WINDOW.fill( CRIMSON )
# Write "Game Over" text to middle of screen (image could be pre-generated)
game_over_text = text_font.render( "* Game Over *", True, BAD_SNOW )
centred_x = ( WINDOW_WIDTH - game_over_text.get_width() ) // 2
centred_y = ( WINDOW_HEIGHT - game_over_text.get_height() ) // 2
WINDOW.blit( game_over_text, ( centred_x, centred_y ) )
pygame.display.flip()
# Update the window, but not more than 60fps
clock.tick_busy_loop( 60 )
pygame.quit()
eyeball_32.png