我无法让我的按钮在pygame中为我的程序工作

时间:2019-02-25 21:41:28

标签: python pygame

所以我的程序是一个游戏,我正在开发菜单,我在采用OOP方法,并且据我之前所了解的那样,我知道Button类应该只使用方法来处理单击。我加载了按钮,其中播放/退出按钮应该首先加载为菜单的第一阶段,然后第二阶段具有4个按钮,用于轻松/中/硬/兽医难度,第三阶段是级别数字1/2/3 / 4。我以前使用过OOP而不是OOP的方式来运行它,我不希望在哪里可以单击每个按钮。但是现在我有一个问题,就是我不确定如何进行OOP获取加载每个按钮集的响应以及是否停止对每个按钮的单击。救命 PS我正在上传的代码没有每个按钮上的文字图像以及背景和标题等。我将语音标记或将其删除,因为文本图像除外,因为需要用它们来区分每个按钮

BUTTON_CLICK_EVENT = pygame.USEREVENT + 1



class Button: #This class contains methods for buttons including display and functionality

    def __init__(self, buttonname, buttonx, buttony, buttonwidth, buttonheight, textfile, textx, texty): #Methods used to allow classes to intialise attributes

        self.buttonname = buttonname # Name of the button
        self.buttonx = buttonx # X-axis position
        self.buttony = buttony # Y-axis position
        self.buttonwidth = buttonwidth # Width of the button
        self.buttonheight = buttonheight # Height of the button
        self.text_image = pygame.image.load( textfile+".png" ) # Button Label
        self.textx = textx # X-axis positioning of the text
        self.texty = texty # Y-axis positioning of the text

    def drawButton(self, screen): #Method which creates a button for the menu

        pygame.draw.rect(screen, (0,0,0), [self.buttonx, self.buttony, self.buttonwidth, self.buttonheight]) #Draws a rectangular button which is black and given the size and coordinates which were attributes 
        screen.blit(self.text_image, (self.textx,self.texty)) #Displays the text given coordinates

    def checkClick( self, mouse_position ):
        #If the mouse-click is inside our rectangle, post a message to the queue
        if ( self.buttonx + self.buttonwidth > mouse_position[0] > self.buttonx and self.buttony + self.buttonheight > mouse_position[1] > self.buttony ):
            pygame.event.post( pygame.event.Event( BUTTON_CLICK_EVENT, { "button_name" : self.buttonname } ) )




PlayButton = Button('playbutton',133,477,756,223,'PlayText',387,545) or ButtonAction(1) #Creates play button      
QuitButton = Button('quitbutton',133,731,756,223,'QuitText',387,806) #Creates quit button

EasyButton = Button('easybutton',133,477,362,223,'EasyText',214,548) #Creates easy button
MediumButton = Button('mediumbutton',533,477,362,223,'MediumText',560,548) #Creates medium button
HardButton = Button('hardbutton',133,731,362,223,'HardText',214,806) #Creates hard button
VeteranButton = Button('veteranbutton',533,731,362,223,'VeteranText',537,806) #Creates veteran button

OneButton = Button('onebutton',133,477,362,223,'OneText',287,550) #Creates the level 1 button
TwoButton = Button('twobutton',533,477,362,223,'TwoText',693,550) #Creates the level 2 button
ThreeButton = Button('threebutton',133,731,362,223,'ThreeText',285,810) #Creates the level 3 button
FourButton = Button('fourbutton',533,731,362,223,'FourText',685,810) #Creates the level 4 button

all_buttons = [ PlayButton, QuitButton, EasyButton, MediumButton, HardButton, VeteranButton, OneButton, TwoButton, ThreeButton, FourButton ]




stage = 1

while True:

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

        if event.type == pygame.MOUSEBUTTONUP:
            click_location = pygame.mouse.get_pos()

        if event.type == BUTTON_CLICK_EVENT:
            print("Clicked "+ event.button_name )

    #ButtonBox.LoadImage()

    for b in all_buttons:
        b.drawButton( gameDisplay )

    pygame.display.flip()
    clock.tick_busy_loop( 60 ) # Limit FPS


pygame.quit()
quit()

LoadImage是一个仅将图像绘制到屏幕上的类。

1 个答案:

答案 0 :(得分:1)

所以我认为这里有两个问题。第一个是如何将按钮分组为某种逻辑和功能集。另一个问题是跟踪用户的状态,并使用该状态来确定他们在屏幕上看到的内容。我对按钮组的理解是,对于给定的游戏状态,将显示不同的按钮组。

因此,当您在编程中认为“陈述”时,通常也会想到“ enum”。在下面的代码中,我创建了两组枚举类型,一组用于游戏状态,另一组用于按钮事件。基本上枚举的类型只是名字很好的数字。它们的使用使代码更具可读性-但这还需要更多工作。

例如:

if ( game_state == 6 ): 

不如:

if ( game_state == GameState.GAME_OVER ):

任何用户驱动的程序都有子节,其中的控制不一定与程序的“主要业务”有关。它可能是打开文件,还是选择难度-本质上,控制输入(鼠标移动,单击和键盘键事件等)需要以不同的方式处理。因此,我们一直在跟踪game_state,以了解当前正在处理哪一部分。对于此特定程序,它允许我们控制要绘制到屏幕上的ButtonSet菜单。

要将一组Button对象分组为某种类型的分组,我创建了一个没有想象力的名称ButtonSet。基本上,它是按钮列表周围的包装对象,具有辅助功能,可立即对整个按钮进行操作。

尽管我正在为它们编写一些演示代码,但我发现对于事件处理,在各处编写({和python解释)许多if button_name == "blah"都是很费时间的。因此,我创建了一组带有ButtonEvent枚举的唯一按钮事件。现在,单击按钮后,它将为所有按钮发布唯一的事件号,而不是单个单击事件。接下来,我意识到可以将width,height等字段全部存储在PyGame rect中,并使用rect类的点碰撞功能对click进行检查。这样可以简化代码。

#Stealth Assassin
import pygame #Imports the pygame module inclulding many in built functions that aids in game design
import time #Imports the time module for which I can implement delays into my program
import enum

pygame.init() #Runs pygame
clock = pygame.time.Clock() #Intialises the variable to control the game clock (FPS)
#gameDisplay = pygame.display.set_mode((1920,1080),pygame.FULLSCREEN) #Variable which will set the resolution of the game window and put the window into fullscreen mode
gameDisplay = pygame.display.set_mode((800,800)) #Variable which will set the resolution of the game window and put the window into fullscreen mode
pygame.display.set_caption("Stealth Assassin") #Sets the title of the pygame window for the game


### All states the game-screen can be in
class GameState( enum.Enum ):
    MENU_PLAYQUIT    = 1,
    MENU_DIFFICULTY  = 2,
    MENU_LEVELSELECT = 3,
    GAME_PLAYING     = 4,
    GAME_OVER        = 5

### All the event-codes the buttons send back
class ButtonEvent( enum.IntEnum ):  # IntEnum so we can convert back to an int for Event poting
    QUIT    = pygame.USEREVENT + 1
    PLAY    = pygame.USEREVENT + 2
    EASY    = pygame.USEREVENT + 3
    MEDIUM  = pygame.USEREVENT + 4
    HARD    = pygame.USEREVENT + 5
    VETERAN = pygame.USEREVENT + 6
    LEVEL1  = pygame.USEREVENT + 7
    LEVEL2  = pygame.USEREVENT + 8
    LEVEL3  = pygame.USEREVENT + 9
    LEVEL4  = pygame.USEREVENT +10


class Button: #This class contains methods for buttons including display and functionality

    def __init__(self, buttonname, event_code, buttonx, buttony, buttonwidth, buttonheight, textfile, textx, texty): #Methods used to allow classes to intialise attributes

        self.buttonname = buttonname # Name of the button
        self.rect       = pygame.Rect( buttonx, buttony, buttonwidth, buttonheight )
        self.text_image = pygame.image.load( textfile+".png" ) # Button Label
        self.textx      = textx # X-axis positioning of the text
        self.texty      = texty # Y-axis positioning of the text
        self.event_code = event_code

    def drawButton(self, screen): #Method which creates a button for the menu
        pygame.draw.rect(screen, (0,0,0), self.rect ) #Draws a rectangular button which is black and given the size and coordinates which were attributes 
        screen.blit(self.text_image, (self.textx,self.texty)) #Displays the text given coordinates

    def checkClick( self, mouse_position ):
        """ Check if the given point is inside our button-rectangle.
            If the click was, post a BUTTON_CLICK_EVENT to the PyGame Event queue and return True
            return False otherwise """
        result = False
        if ( self.rect.collidepoint( mouse_position ) ):
            #If the mouse-click is inside our rectangle, post a message to the queue
            pygame.event.post( pygame.event.Event( int( self.event_code), { "button_name" : self.buttonname } ) )
            result = True
        return result

###
### A container class for a bunch of buttons
###
class ButtonSet:
    def __init__( self, *buttons ):
        self.buttons = list( buttons )

    def addButton( self, b ):
        """ Add a new button to our set, but not if we have it already """
        if ( b not in self.buttons ):
            self.buttons.append( b )

    def anyClicked( self, click_location ):
        """ For every button in the group, check to see if the mouse click was inside it. """
        result = False
        for b in self.buttons:
            if ( b.checkClick( click_location ) == True ):
                result = True
        return result

    def draw( self, screen ):
        """ Paint the entire button set to the screen """
        for b in self.buttons:
            b.drawButton( screen )







PlayButton = Button('playbutton',ButtonEvent.PLAY,133,477,756,223,'button_text',387,545) or ButtonAction(1) #Creates play button      
QuitButton = Button('quitbutton',ButtonEvent.QUIT,133,731,756,223,'button_text',387,806) #Creates quit button
play_quit_buttons = ButtonSet( PlayButton, QuitButton )

EasyButton    = Button('easybutton',    ButtonEvent.EASY,     133,477,362,223,'button_text',214,548) #Creates easy button
MediumButton  = Button('mediumbutton',  ButtonEvent.MEDIUM,   533,477,362,223,'button_text',560,548) #Creates medium button
HardButton    = Button('hardbutton',    ButtonEvent.HARD,     133,731,362,223,'button_text',214,806) #Creates hard button
VeteranButton = Button('veteranbutton', ButtonEvent.VETERAN, 533,731,362,223,'button_text',537,806) #Creates veteran button
difficulty_buttons = ButtonSet( EasyButton, MediumButton, HardButton, VeteranButton )

OneButton   = Button('onebutton',   ButtonEvent.LEVEL1, 133,477,362,223,'button_text',287,550) #Creates the level 1 button
TwoButton   = Button('twobutton',   ButtonEvent.LEVEL2, 533,477,362,223,'button_text',693,550) #Creates the level 2 button
ThreeButton = Button('threebutton', ButtonEvent.LEVEL3, 133,731,362,223,'button_text',285,810) #Creates the level 3 button
FourButton  = Button('fourbutton',  ButtonEvent.LEVEL4, 533,731,362,223,'button_text',685,810) #Creates the level 4 button
level_buttons = ButtonSet( OneButton, TwoButton, ThreeButton, FourButton )


### What game-state is displayed to the user
game_state      = GameState.MENU_PLAYQUIT
game_difficulty = 1
game_level      = 1
done            = False
while not done:

    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            print("Quit Event")
            done = True

        elif event.type == pygame.MOUSEBUTTONUP:
            click_location = pygame.mouse.get_pos()
            #print("Mouse-Up Event -> (%3d, %3d)" % ( click_location[0], click_location[1] ) )

            # send the mouse-click location to the correct button-set depending on the state
            if ( game_state == GameState.MENU_PLAYQUIT ):
                play_quit_buttons.anyClicked( click_location )
            elif ( game_state == GameState.MENU_DIFFICULTY ):
                difficulty_buttons.anyClicked( click_location )
            elif ( game_state == GameState.MENU_LEVELSELECT ):
                level_buttons.anyClicked( click_location )
            elif ( game_state == GameState.GAME_PLAYING ):
                # TODO
                pass
            elif ( game_state == GameState.GAME_OVER ):
                # TODO
                pass

        ###
        ### Handle all the mouse-click button events
        ###
        elif event.type == ButtonEvent.QUIT:
            done = True
        elif event.type == ButtonEvent.PLAY:
            # user clicked "play", trainsition to next state
            game_state = GameState.MENU_DIFFICULTY
        elif event.type in [ ButtonEvent.EASY, ButtonEvent.MEDIUM, ButtonEvent.HARD, ButtonEvent.VETERAN ]:
            game_state = GameState.MENU_LEVELSELECT
            # NOTE: This could be simpler with a dictionary of { event : difficulty-level }
            if event.type == ButtonEvent.EASY:
                game_difficulty = 1
            elif event.type == ButtonEvent.MEDIUM:
                game_difficulty = 2
            elif event.type == ButtonEvent.HARD:
                game_difficulty = 3
            elif event.type == ButtonEvent.VETERAN:
                game_difficulty = 4
        elif event.type in [ ButtonEvent.LEVEL1, ButtonEvent.LEVEL2, ButtonEvent.LEVEL3, ButtonEvent.LEVEL4 ]:
            game_state = GameState.GAME_PLAYING
            if event.type == ButtonEvent.LEVEL1:
                game_level = 1
            ### etc



    #ButtonBox.LoadImage()
    ###
    ### Depending on the Game State, render the screen
    ###
    if ( game_state == GameState.MENU_PLAYQUIT ):
        gameDisplay.fill( ( 128, 128, 128 ) )  # Temorarily fill with grey to see button locations better
        play_quit_buttons.draw( gameDisplay )
    elif ( game_state == GameState.MENU_DIFFICULTY ):
        gameDisplay.fill( ( 188, 188, 188 ) )  # Temorarily fill with grey to see button locations better
        difficulty_buttons.draw( gameDisplay )
    elif ( game_state == GameState.MENU_LEVELSELECT ):
        gameDisplay.fill( ( 240, 240, 240 ) )  # Temorarily fill with grey to see button locations better
        level_buttons.draw( gameDisplay )
    elif ( game_state == GameState.GAME_PLAYING ):
        gameDisplay.fill( ( 0, 0, 0 ) )  # Temorarily fill with grey to see button locations better
        # TODO paint game sprites
    elif ( game_state == GameState.GAME_OVER ):
        gameDisplay.fill( ( 200, 0, 0 ) )  # Temorarily fill with grey to see button locations better
        # TODO paint game-over screen
        # TODO play wah-wah-wahhh sound

    pygame.display.flip()
    clock.tick_busy_loop( 60 ) # Limit FPS


pygame.quit()
#quit()

说实话,ButtonButtonSet紧跟PyGame的Sprite and SpriteGroup类的外观和用法。如果Button类继承了pygame.sprite.Sprite,可能会带来更好的代码,但是我认为按钮并不是真正必要的,而在彩色背景上使用文本位图的实现略有不同。设置也不同。

编辑:

如果发现代码用完了用户空间事件代码,请考虑使用带有额外事件参数的组类型事件。例如,事件pygame.KEYDOWN包括事件的一堆参数,例如.scancode.unicode

对于级别事件,类似地,可能会有一个NEW_LEVEL事件,并且在发布事件时,将数字指示符添加到事件参数,例如:

pygame.event.post( pygame.event.Event( int( ButtonEvent.NEW_LEVEL ), { "level_num" : 1 } ) )

...

# Handle user-input
for event in pygame.event.get():
    if ( event.type == ButtonEvent.NEW_LEVEL ):
        if ( event.level_num == 1 ):
            pass # TODO
        elif ( event.level_num == 2 ):
            pass # TODO
        elif ...