所以我的程序是一个游戏,我正在开发菜单,我在采用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是一个仅将图像绘制到屏幕上的类。
答案 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()
说实话,Button
和ButtonSet
紧跟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 ...