при запуске кода для отображения изображений на экране мое окно игры временно зависает - PullRequest
0 голосов
/ 19 февраля 2019

В игре, которую я создаю, пользователь может принять участие в битвах на аренах.После победы на арене их нужно поздравить с помощью текста на экране и, в зависимости от того, очистили ли они арену раньше или нет, получают трофей или сообщают, сколько раз они выиграли.

Код, описывающий, что происходит, если игроки выиграли раньше, и если игрок выиграл только в первый раз, практически аналогичен, но игра останавливается, когда достигает части моего утверждения if / elif, в котором изложены условия дляесли игрок уже одолел арену один раз.

Код для контекста [текст - это отдельный файл, используемый для записи текста на мой экран с использованием логических значений для определения, выделен ли он жирным шрифтом, его положением и т. д.]:

import pygame, time, text

if battleWon == True and x == len(enemyList)-1: #if the user won the fight and they were on the last enemy
    screen.fill(bg_colour) #code for clearing screen
    screen.blit(prevwindow,(150,100)) #drawing menus
    text.textDisplay("arena champion!",300,140,True,14,"center",screen) #informing they've won
    timesCompleted += 1 #incrementing their win count
    pygame.display.update() #updating diaplay
    time.sleep(2) #keeping message on screen

    if arenaComplete == False: #if they havent beaten the arena before
        arenaComplete = True #set to true
        screen.fill(bg_colour)
        screen.blit(prevwindow,(150,100))
        text.textDisplay("you got a trophy!",300,140,False,14,"center",screen)# tell them they got a trophy
        screen.blit(trophy, (250,160)) #show image of trophy
        pygame.display.update()
        time.sleep(2)

    elif arenaComplete == True: #if already beaten
        screen.fill(bg_colour)
        screen.blit(prevwindow,(150,100))
        text.textDisplay("you've now won "+str(timesCompleted)+" times!",300,140,False,14,"center",screen) #show how many times they've beaten arena
        screen.blit(trophy, (250,160)) #trophy...
        text.textDisplay("x" + str(timesCompleted),375,160,False,14,"topleft",screen) # ... x(no. of times won)
        pygame.display.update()
        time.sleep(2)

elif battleWon == False: #if player loses
    print(":(") #placeholder

Я много раз перечитывал код и не понимаю, почему это происходит.Кажется, в синтаксисе нет ошибок, как это было бы в оболочке.Если бы код по какой-то причине пропустил часть elif arenaComplete == True, он просто вернулся бы к основному циклу моей игры без остановки.

РЕДАКТИРОВАТЬ: из-за некоторой путаницы в комментариях я поясню, что код time.sleep, насколько я знаю, не вызывает проблему, которую я описываю.Я хочу, чтобы сообщение отображалось на экране в течение заданного промежутка времени, проблема в том, что код в выражении "elif arenaComplete == True" вообще ничего не отображает.Вот что я сказал в комментарии, если он объясняет лучше:

Я имею в виду, что код под экраном «elif arenaComplete == True» на самом деле ничего не скрывает на экране, несмотря на"pygame.display.update", который появляется ДО кода time.sleep, что означает, что что-то должно происходить заранее, но я не уверен, что именно.Код в предыдущем разделе «if arenaComplete == False» практически идентичен, и по этой причине я не могу думать ни о какой причине, почему это происходит.

1 Ответ

0 голосов
/ 20 февраля 2019

Как отметили комментаторы, игра блокируется во время вызовов 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 eyeball_32.png

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...