Reputation: 14276
I've made a start menu and a menu screen. The start screen has a single Button Sprite
. After clicking, it creates the correction animation then goes to menu screen as expected. However, this menu screen has two Button Sprites
. It seems whichever Button Sprite
I add first to the group is clickable, but the other isn't. I'm not sure why this odd behavior exists.
At this piece of the code is where I notice it messes up:
elif event.type == MOUSEBUTTONDOWN:
#HERE IS THE ISSUE
print("size of all_sprites after clicking: " + str(len(all_sprites)))
print(" info on all_sprites: " + str(all_sprites))
for but in all_sprites:
print(" sprite info: " + str(but))
if isinstance(but, (Button)) and mouse.click(but):
but.set_clicked()
print(" clicked: " + str(but))
When iterating over the Sprites
group and calling this method:
mouse.click(but)
It only works for the first Button
added. When I try clicking the other Button
, it never returns True
. I'm not sure why.
Here is the relevant objects:
#seperate this into an object class
class Mouse(pygame.sprite.Sprite):
"""moves a hand on the screen, following the computer mouse"""
def __init__(self):
pygame.sprite.Sprite.__init__(self) #call Sprite initializer
self.clicking = 0
self.image, self.rect = util.load_image('cursor.png',-1)
def update(self):
"move the hand based on the computer mouse position"
pos = pygame.mouse.get_pos()
self.rect.midtop = pos
if self.clicking:
pygame.mixer.Sound("data/high_tone_sword.wav").play()
self.rect.move_ip(5, 10)
def click(self, target):
"returns true if the hand collides with the target"
if not self.clicking:
self.clicking = 1
hitbox = self.rect.inflate(-5, -5)
return hitbox.colliderect(target.rect)
def unclick(self):
self.clicking = 0
#seperate this into an object/menu class
class Button(pygame.sprite.Sprite):
"""Button class used for all menus. Handles events"""
def __init__(self, original_image_source, clicked_image_source, location_coordinates):
pygame.sprite.Sprite.__init__(self) #call Sprite intializer
self.original_image_source = original_image_source
self.clicked_image_source = clicked_image_source
self.location_coordinates = location_coordinates
self.clicked = 0
self.image, self.rect = util.load_image(original_image_source, None)
self.rect.midtop = location_coordinates
def update(self):
"update on click or unclick"
if self.clicked:
self.image, self.rect = util.load_image(self.clicked_image_source, None)
self.rect.midtop = self.location_coordinates
else:
self.image, self.rect = util.load_image(self.original_image_source, None)
self.rect.midtop = self.location_coordinates
def set_clicked(self):
self.clicked = 1
def set_unclicked(self):
self.clicked = 0
Here is the driver:
def main():
#initialise screen
pygame.init()
screen = pygame.display.set_mode((1000, 600))
pygame.display.set_caption('States of Matter')
pygame.mouse.set_visible(0)
#music!!!
pygame.mixer.music.load("music/Glorious Morning 2.mp3")
pygame.mixer.music.play(-1, 53.0)
background = pygame.Surface(screen.get_size(), pygame.SRCALPHA, 32)
background = background.convert()
background.fill((250, 250, 250))
elements_background = util.load_image("4-elements-background.jpg")
if pygame.font:
font = pygame.font.Font(None, 180)
font.set_italic(True)
text = font.render("States of Matter", 1, (20, 125, 120))
textpos = text.get_rect(centerx=elements_background[0].get_width()/2, centery=elements_background[0].get_height()/4)
elements_background[0].blit(text, textpos)
background.blit(elements_background[0],(-450, -200))
screen.blit(background, (0, 0))
pygame.display.flip()
flamethrower = Animation('animation/Flames/flamethrower_/flamethrower_', 29, (-50,0))
bolt_tsela = Animation('animation/voltage_0/bolt_tesla/bolt_tesla_', 10, (600, 200))
mouse = Mouse()
start_button = Button("start_button_original.jpg", "start_button_clicked.jpg", (background.get_width()/2, 12.5*background.get_height()/17))
#all_sprites = pygame.sprite.RenderPlain((flamethrower, bolt_tsela, mouse, start_button)) #arbitary order
all_sprites = pygame.sprite.OrderedUpdates((flamethrower, bolt_tsela, start_button, mouse)) #order based on how they are added!
clock = pygame.time.Clock()
battle_button = Button("battle_menu_button_original.jpg", "battle_menu_button_clicked.jpg", (background.get_width()/2,4.5*background.get_height()/17))
options_button = Button("options_menu_button_original.jpg", "options_menu_button_clicked.jpg", (background.get_width()/2, 10.5*background.get_height()/17))
menu_control = MenuControl()
menu_control.set_in_start_screen(True)
#game driver
while 1:
clock.tick(60)
for event in pygame.event.get():
if event.type == QUIT:
return
elif event.type == KEYDOWN and event.key == K_ESCAPE:
return
elif event.type == MOUSEBUTTONDOWN:
#HERE IS THE ISSUE
print("size of all_sprites after clicking: " + str(len(all_sprites)))
print(" info on all_sprites: " + str(all_sprites))
for but in all_sprites:
print(" sprite info: " + str(but))
if isinstance(but, (Button)) and mouse.click(but):
but.set_clicked()
print(" clicked: " + str(but))
elif event.type == MOUSEBUTTONUP:
mouse.unclick()
if menu_control.bools["in_start_screen"] and start_button.clicked:
#enter main manu
menu_control.set_in_main_menu(True)
start_button.set_unclicked()
all_sprites.empty()
all_sprites.add(battle_button, options_button, mouse)
print("size of all_sprites after enting main menu: " + str(len(all_sprites)))
elif menu_control.bools["in_start_screen"]:
start_button.set_unclicked()
if menu_control.bools["in_main_menu"] and battle_button.clicked:
battle_button.set_unclicked()
if menu_control.bools["in_main_menu"] and options_button.clicked:
options_button.set_unclicked()
all_sprites.update()
#redraw everything
screen.blit(background, (0, 0))
all_sprites.draw(screen)
pygame.display.flip()
if __name__ == '__main__': main()
Upvotes: 1
Views: 237
Reputation: 11170
This is because you reset you clicked state on your mouse on a MOUSE_UP
event.
The variable clicking
is set while checking the first button, and the condition not self.clicking
is never fulfilled for other buttons.
EDIT:
I would advice to split this method into the part that sets the clicking
variable for the mouse, and another one that checks for collisions. They are two separate actions. Right now, if you will add no buttons, the mouse will never be in the clicking
state. I don't think that was your intention.
Upvotes: 1
Reputation: 123443
For eachMOUSEBUTTONDOWN
event that occurs, themouse.click()
method is called for everyButton
instance inall_sprites
. Theclick()
method only setsself.clicking = 1
when it hasn't already been set. This means that when it's called again for buttons later on inall_sprites
after the first one, it'll see thatself.clicking
has already been set and not do anything.
Hope that's clear.
Perhaps you want something along these lines [untested] -- which would only set the mouse's self.clicking
attribute once in thefor
loop (assuming two buttons don't overlap):
def click(self, target):
"returns true if the hand collides with the target"
hitbox = self.rect.inflate(-5, -5)
if hitbox.colliderect(target.rect):
self.clicking = 1
return True
Upvotes: 0