运行代码以在屏幕上显示图像时,我的游戏窗口暂时冻结

时间:2019-02-19 14:22:53

标签: python pygame

在我制作的游戏中,用户可以参加竞技场战斗。赢得比赛场地后,他们将通过屏幕上的文字向他们表示祝贺,并且取决于他们是否曾经离开比赛场地,获得奖杯或被告知获胜的次数。

概述了如果玩家之前赢过以及如果玩家仅第一次赢了会发生什么的代码实际上是相似的,但是当游戏到达我的if / {{1}部分时,游戏就会冻结}声明概述了玩家是否已经击败过竞技场的条件。

上下文代码[文本是一个单独的文件,用于将文本写到我的屏幕上,使用布尔值确定其是否为粗体,位置等,以此类推:

elif

我已经阅读了很多遍代码,我看不出为什么会发生这种情况。语法似乎没有任何错误,就像在shell中显示的那样。如果代码由于某种原因而跳过了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 部分,它将直接跳回游戏的主循环而不会冻结。

编辑:由于注释中有些混乱,我将澄清一下,据我所知,time.sleep代码不会引起我正在描述的问题。我希望消息在给定的时间内显示在屏幕上,问题是elif arenaComplete == True语句下的代码根本没有显示任何内容。这是我在评论中说得更好的话:

  

我的意思是“ elif arenaComplete == True”下的代码   尽管屏幕确实   在time.sleep代码之前的“ pygame.display.update”   意思是必须先发生一些事情,但是我不确定。   前面的“ if arenaComplete == False”部分中的代码几乎是   完全相同,因此我无法想到任何原因   这正在发生。

1 个答案:

答案 0 :(得分:1)

正如评论员指出的那样,游戏在time.sleep(2)通话期间处于锁定状态。

实际上,在任何类型的事件驱动程序中,用睡眠周期锁定整个过程都是不理想的。尤其是在处理事件的循环中,因为这可能会带来其他屏幕绘画后果。

那么您如何编码呢?带有State Machine

上面概述的游戏似乎具有三个状态:“战斗”,“结果”和“游戏结束”(大概)。根据游戏所处的状态,显示方式不同,并且用户输入的处理方式也不同。

首先,让我们使用python Enumerated Type为游戏定义一些状态。

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()的延迟就可以向用户显示某些内容。它可以一直停留在“赢得”屏幕上,直到按下某个键为止。

在这里,我给出一个简单的示例,其中按任意键可在三种状态之间循环。每个状态显示一个不同的屏幕。 las,我目前也没有时间实现状态驱动的按键处理程序。

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