Я не могу заставить мои кнопки работать на мою программу в Pygame - PullRequest
0 голосов
/ 26 февраля 2019

Итак, моя программа - игра, и я занимаюсь разработкой меню, я использую ООП-подход и понимаю, как мне уже говорили, что класс Button, вероятно, должен просто обрабатывать нажатия с помощью методов.У меня есть кнопки загрузки, где кнопки play / quit должны загружаться сначала как первый этап меню, затем второй этап имеет 4 кнопки для легкого / med / hard / vet затруднения, а третий этап - уровень номер 1/2/3/ 4.Раньше у меня это работало с сочетанием ООП, а не ООП, который я не хочу, чтобы я мог нажимать кнопки для каждой из них.Однако теперь у меня есть проблема в том, что я не уверен, как поступить с ООП, получая ответы для загрузки каждого набора кнопок, а также чтобы остановить щелчок, проходящий через каждую кнопку.Помогите 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 голосов
/ 26 февраля 2019

Так что я думаю, что здесь есть две проблемы.Во-первых, как сгруппировать кнопки в некоторый логический и функциональный набор.Другой проблемой является отслеживание состояния пользователя и использование его для определения того, что он видит на экране.Мое понимание наборов кнопок состоит в том, что для данного состояния игры показан другой набор кнопок.

Поэтому, когда вы думаете «состояние» в программировании, вы, как правило, также думаете « enum»».В приведенном ниже коде я создал два набора перечислимых типов, один для состояния игры, другой для событий кнопок.В основном перечисляемые типы - просто числа с красивыми именами.Их использование делает код более читабельным - но это немного больше работы.

Например:

if ( game_state == 6 ): 

гораздо менее понятен, чем:

if ( game_state == GameState.GAME_OVER ):

Любая пользовательская программа имеет подразделы, где управление не обязательно связано с«основной бизнес» программы.Это может быть открытие файла или выбор сложности - по сути, управляющий ввод (движения мыши, щелчки и события клавиатуры и т. Д.) Должен обрабатываться по-разному.Итак, мы отслеживаем game_state, чтобы знать, с какой частью мы сейчас работаем.Для этой конкретной программы она позволяет нам контролировать, какое меню ButtonSet выводить на экран.

Чтобы сгруппировать набор из Button объектов в какую-то группу, я создал не-образное имя ButtonSet.По сути, это объект-обертка вокруг списка кнопок, с вспомогательными функциями для одновременной работы со всем набором.

Пока я писал для них демо-код, мне пришло в голову, что для обработки событий онбыло много времени, чтобы написать (и для python, чтобы интерпретировать) много if button_name == "blah" повсюду.Поэтому я создал набор уникальных событий кнопок с перечислением ButtonEvent.Когда кнопка нажата, она теперь публикует уникальный номер события, а не одно событие щелчка для всех кнопок.Затем я понял, что поля ширины, высоты и т. Д. Могут быть сохранены в PyGame rect , и щелчок проверяется с помощью функции столкновения точек класса rect.Это немного упростило код.

#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()

Честно говоря, Button и ButtonSet внимательно следят за внешним видом и использованием классов PyGame Sprite и 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 ...
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...