创建多个按钮,但该程序将在一步之内

时间:2019-09-08 01:17:10

标签: python performance pygame

我正在尝试创建多个按钮,每个按钮在Pygame中都包含2个矩形。但是,该程序可能会在某个步骤中停留一秒钟,这可能是由于嵌套循环造成的。 特别是,只有在第一次执行时,在主循环中由代码围栏标记的“ while循环”才可以正常工作。如果在第一次之后执行,程序将停止响应一秒钟。反正有没有纠正? 我目前正在10 64位窗口,Python 3.7.4,PyGame 1.9.6上对其进行测试。

import pygame
import time
import random

pygame.init()

# Screen for displaying everything
display_width = 1200
display_height = 700
game_display = pygame.display.set_mode((display_width,display_height),pygame.RESIZABLE)
pygame.display.set_caption('Currently testing')

# Color for later use
red = (200,0,0)
green = (0,200,0)
bright_red = (255,0,0)
bright_green = (0,255,0)
black= (0,0,0)
white= (225,225,225)
grey = (166,166,166)
font_color= black
background_color= white

def create_button(x,y,w,h,name,callback):
    # lower part of the button
    button_upper= pygame.Rect(x, y, w, h)
    # upper part of the button
    button_lower= pygame.Rect(x, y+ h, w, h)
    # define the area of the button for later interaction
    interactable= pygame.Rect(x, y, w, 2*h)
    button_info= {'button_lower':button_lower,
                  'button_upper':button_upper, 
                  'button_name':name,
                  'interaction':interactable,
                  'button function':callback,
                  'color_upper':red, 'color_lower':green}
    return button_info

def draw_button(button_info):
    # Drawing lower part of the button
    pygame.draw.rect(game_display, 
                    button_info['color_lower'],
                    button_info['button_lower'])
    # Drawing upper part
    pygame.draw.rect(game_display, 
                    button_info['color_upper'],
                    button_info['button_upper'])

# Text object for later use
def text_object(text,font):
    textSurface= font.render(text,True,font_color)
    return textSurface, textSurface.get_rect()
def central_text(text,size,pos):
    largeText = pygame.font.Font('freesansbold.ttf',size)
    textSurface, textRect = text_object(text,largeText)
    textRect.center = (pos)
    game_display.blit(textSurface,textRect)

def main():

    # The function of the button, temporarily
    def print_name():
        nonlocal button
        nonlocal done
        game_display.fill(background_color)
        central_text('Button'+' '+button['button_name']+' '+'clicked',80,(display_width/2,display_height/2))
        pygame.display.update()
        time.sleep(3)         
        done= True
    # function of non-interactable block
    def do_nothing():
        pass

    # Actually create those button
    button_1= create_button(display_width*0.3,display_height*0.5,40,40,'1',print_name)
    button_2= create_button(display_width*0.35,display_height*0.5,50,50,'2',print_name)
    button_3= create_button(display_width*0.4,display_height*0.5,30,30,'3',print_name)
    button_4= create_button(display_width*0.45,display_height*0.5,20,20,'4',print_name)
    button_5= create_button(display_width*0.5,display_height*0.5,40,30,'5',print_name)
    button_6= create_button(display_width*0.55,display_height*0.5,50,40,'6',print_name)
    button_7= create_button(display_width*0.6,display_height*0.5,50,30,'7',print_name)
    button_8= create_button(display_width*0.65,display_height*0.5,50,50,'8',print_name)
    button_9= create_button(display_width*0.7,display_height*0.5,30,40,'9',print_name)
    button_10= create_button(display_width*0.75,display_height*0.5,60,70,'10',print_name)
    # Create non-interactable rectangles
    block_1= create_button(display_width*0.75,display_height*0.8,40,40,'10',do_nothing)
    block_2= create_button(display_width*0.7,display_height*0.8,40,40,'9',do_nothing)
    block_3= create_button(display_width*0.7,display_height*0.8,40,40,'8',do_nothing)
    # Select and store those button in different list, with a non-interactable 
    # rectangles in each list
    list_1=[button_1, button_2, button_3, button_4, button_5, 
            button_6, button_7, button_8, button_9, button_10]
    list_2=[button_1, button_2, button_3, button_4, button_5, 
            button_6, button_7, block_3, button_9, button_10]
    list_3=[button_1, button_2, button_3, button_4, button_5, 
            button_6, button_7, button_8, block_2, button_10]
    list_4=[button_1, button_2, button_3, button_4, button_5, 
            button_6, button_7, button_8, button_9, block_1]

    # Attempt to control how many times and in what sequence those
    # button list would be used
    index= [1,1,2,2,2,3,3,4,4]
    random.shuffle(index)
    text=' ' # a text that would accompany all the buttons
    for i in range (len(index)):
        done = False
        if index[i] == 1:
            button_list= list_1
            text= 'text 1'
        elif index[i] == 2:
            button_list= list_2
            text= 'text 2'
        elif index[i] == 3:
            button_list= list_3
            text= 'text 3'
        else:
            button_list= list_4
            text= 'text 4'
        # display message 1
        game_display.fill(background_color)
        central_text('test_text 1',80,(display_width/2,display_height/2))
        pygame.display.update()
        time.sleep(2)
        # display message 2
        game_display.fill(background_color)
        central_text('test_text 2',80,(display_width/2,display_height/2))
        pygame.display.update()       
        time.sleep(3)
       '''This is where the program would stuck for a second'''
        game_display.fill(background_color)
        central_text(text,30,(display_width/2,display_height*0.2))
        while not done:
            for event in pygame.event.get():
                if event.type == pygame.KEYDOWN:
                    if event.key == pygame.K_ESCAPE:
                        pygame.display.quit()
                        pygame.quit()
                        quit()     
                # block that would be executed when left mouse button is pressed
                elif event.type == pygame.MOUSEBUTTONDOWN:
                    if event.button == 1:
                        for button in button_list:
                            if button['interaction'].collidepoint(event.pos):
                                button['button function']()
                elif event.type == pygame.MOUSEMOTION:
                    # When the mouse gets moved, change the color of the
                    # buttons if they collide with the mouse.
                    for button in button_list:
                        if not button['button function']== do_nothing:
                            if button['interaction'].collidepoint(event.pos):
                                button['color_upper']= red
                                button['color_lower']= green
                            else:
                                button['color_upper']= bright_red
                                button['color_lower']= bright_green
            for button in button_list:
                # Turn non-interactable blocks into grey
                if button['button function']== do_nothing:
                    button['color_upper']= grey
                    button['color_lower']= grey                    
                draw_button(button)
            pygame.display.update()
        '''the block above would some times stucks the program'''
main()

1 个答案:

答案 0 :(得分:0)

市长的问题是,当您延迟并显示更新时,您希望通过pygame.event.pump()pygame.event.get()处理事件。当您使用pygame时,您应该使用pygame.time.delay()pygame.time.wait()

我建议更改程序。使用1个主循环和1个事件循环。在此循环中执行游戏过程。代替

  
for i in range (len(index)):

   # [...]

   while not done:
       for event in pygame.event.get():

           # [...]

   # [...]

将过程更改为

done = False
run = True
i = 0
while run:

    if done:
        done = False
        if i < len(index)-1:
            i += 1
        else:
            run = False

    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            run = False

        # [...]

    # [...]

要实现游戏的不同状态,我建议使用游戏state。例如:

from enum import Enum
class GameState(Enum):
    START = 0
    TEXT1 = 1
    TEXT2 = 2
    RUN = 3
    BUTTON = 4

state = GameState.START

使用计时器事件(pygame.time.set_timer())切换游戏状态。例如:

clicked_button = None
my_event_id = pygame.USEREVENT + 1
while run:

    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            run = False

        # timer event
        elif event.type == my_event_id:
             if state == GameState.TEXT1:
                pygame.time.set_timer(my_event_id, 3000)
                state = GameState.TEXT2
            elif state == GameState.TEXT2:
                pygame.time.set_timer(my_event_id, 0)
                state = GameState.RUN
            else:
                pygame.time.set_timer(my_event_id, 0)
                state = GameState.START
                done = True

        if state == GameState.RUN:
            if event.type == pygame.MOUSEBUTTONDOWN:
                if event.button == 1:
                    for button in button_list:
                        if button['interaction'].collidepoint(event.pos):
                            pygame.time.set_timer(my_event_id, 3000)
                            state = GameState.BUTTON
                            clicked_button = button
            # [...]

根据游戏state绘制场景:

while run:

    # [...]

    game_display.fill(background_color)

    if state == GameState.START:
        pygame.time.set_timer(my_event_id, 2000)
        state = GameState.TEXT1
    elif state == GameState.TEXT1:
        # display message 1
        central_text('test_text 1',80,(display_width/2,display_height/2))
    elif state == GameState.TEXT2:
        # display message 2
        central_text('test_text 2',80,(display_width/2,display_height/2))
    elif state == GameState.RUN:
        central_text(text,30,(display_width/2,display_height*0.2))
        # [...]
    else:
        clicked_button['button function']()

    pygame.display.update()

游戏循环和主要游戏过程可能如下所示:

from enum import Enum
class GameState(Enum):
    START = 0
    TEXT1 = 1
    TEXT2 = 2
    RUN = 3
    BUTTON = 4

def main():

    # The function of the button, temporarily
    def print_name():
        nonlocal clicked_button
        central_text('Button'+' '+clicked_button['button_name']+' '+'clicked',80,(display_width/2,display_height/2))        
    # function of non-interactable block
    def do_nothing():
        pass

    # Actually create those button
    button_1= create_button(display_width*0.3,display_height*0.5,40,40,'1',print_name)
    button_2= create_button(display_width*0.35,display_height*0.5,50,50,'2',print_name)
    button_3= create_button(display_width*0.4,display_height*0.5,30,30,'3',print_name)
    button_4= create_button(display_width*0.45,display_height*0.5,20,20,'4',print_name)
    button_5= create_button(display_width*0.5,display_height*0.5,40,30,'5',print_name)
    button_6= create_button(display_width*0.55,display_height*0.5,50,40,'6',print_name)
    button_7= create_button(display_width*0.6,display_height*0.5,50,30,'7',print_name)
    button_8= create_button(display_width*0.65,display_height*0.5,50,50,'8',print_name)
    button_9= create_button(display_width*0.7,display_height*0.5,30,40,'9',print_name)
    button_10= create_button(display_width*0.75,display_height*0.5,60,70,'10',print_name)
    # Create non-interactable rectangles
    block_1= create_button(display_width*0.75,display_height*0.8,40,40,'10',do_nothing)
    block_2= create_button(display_width*0.7,display_height*0.8,40,40,'9',do_nothing)
    block_3= create_button(display_width*0.7,display_height*0.8,40,40,'8',do_nothing)
    # Select and store those button in different list, with a non-interactable 
    # rectangles in each list
    list_1=[button_1, button_2, button_3, button_4, button_5, 
            button_6, button_7, button_8, button_9, button_10]
    list_2=[button_1, button_2, button_3, button_4, button_5, 
            button_6, button_7, block_3, button_9, button_10]
    list_3=[button_1, button_2, button_3, button_4, button_5, 
            button_6, button_7, button_8, block_2, button_10]
    list_4=[button_1, button_2, button_3, button_4, button_5, 
            button_6, button_7, button_8, button_9, block_1]

    # Attempt to control how many times and in what sequence those
    # button list would be used
    index= [1,1,2,2,2,3,3,4,4]
    random.shuffle(index)
    text=' ' # a text that would accompany all the buttons


    my_event_id = pygame.USEREVENT + 1
    done = False
    run = True
    state = GameState.START
    i = 0
    clicked_button = None
    while run:

        if done:
            done = False
            if i < len(index)-1:
                i += 1
            else:
                run = False

        if index[i] == 1:
            button_list= list_1
            text= 'text 1'
        elif index[i] == 2:
            button_list= list_2
            text= 'text 2'
        elif index[i] == 3:
            button_list= list_3
            text= 'text 3'
        else:
            button_list= list_4
            text= 'text 4' 

        # event loop

        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                run = False
            if event.type == pygame.KEYDOWN:
                if event.key == pygame.K_ESCAPE:
                    run = False  
            elif event.type == my_event_id:
                if state == GameState.TEXT1:
                    pygame.time.set_timer(my_event_id, 3000)
                    state = GameState.TEXT2
                elif state == GameState.TEXT2:
                    pygame.time.set_timer(my_event_id, 0)
                    state = GameState.RUN
                else:
                    pygame.time.set_timer(my_event_id, 0)
                    state = GameState.START
                    done = True

            if state == GameState.RUN:
                # block that would be executed when left mouse button is pressed
                if event.type == pygame.MOUSEBUTTONDOWN:
                    if event.button == 1:
                        for button in button_list:
                            if button['interaction'].collidepoint(event.pos):
                                pygame.time.set_timer(my_event_id, 3000)
                                state = GameState.BUTTON
                                clicked_button = button

                elif event.type == pygame.MOUSEMOTION:
                    # When the mouse gets moved, change the color of the
                    # buttons if they collide with the mouse.
                    for button in button_list:
                        if not button['button function']== do_nothing:
                            if button['interaction'].collidepoint(event.pos):
                                button['color_upper']= red
                                button['color_lower']= green
                            else:
                                button['color_upper']= bright_red
                                button['color_lower']= bright_green

        # draw

        game_display.fill(background_color)

        if state == GameState.START:
            pygame.time.set_timer(my_event_id, 2000)
            state = GameState.TEXT1
        elif state == GameState.TEXT1:
            # display message 1
            central_text('test_text 1',80,(display_width/2,display_height/2))
        elif state == GameState.TEXT2:
            # display message 2
            central_text('test_text 2',80,(display_width/2,display_height/2))
        elif state == GameState.RUN:
            central_text(text,30,(display_width/2,display_height*0.2))
            for button in button_list:
                # Turn non-interactable blocks into grey
                if button['button function']== do_nothing:
                    button['color_upper']= grey
                    button['color_lower']= grey                    
                draw_button(button)
        else:
            clicked_button['button function']()

        pygame.display.update()