Pygame-Taste Einzelklick

Ich habe Schaltflächen in pygame für Click-Ereignisse erstellt, aber es gibt ein Problem. Wenn ich mit der Maustaste klicke und die Maus zwischen den Schaltflächengrenzen bewege, wiederholt sich das Klickereignis. Ich möchte nur einen einzigen Klick, bis ich die Maustaste loslasse. Wie kann ich das machen?

import pygame,time pygame.init() x,y = (200,300) pencere = pygame.display.set_mode((x,y)) pygame.display.set_caption("Click") white = (255,255,255) black = (0,0,0) black2 = (30,30,30) class Counter: count = 0 def click(self): self.count += 1 number = Counter() def text_objects(text, font, color): textSurface = font.render(text, True, color) return textSurface, textSurface.get_rect() def button(msg,x,y,w,h,c,ic,action=None): mouse = pygame.mouse.get_pos() click = pygame.mouse.get_pressed() pygame.draw.rect(pencere, c,(x,y,w,h)) smallText = pygame.font.Font("freesansbold.ttf",20) textSurf, textRect = text_objects(msg, smallText, white) textRect.center = ( (x+(w/2)), (y+(h/2)) ) pencere.blit(textSurf, textRect) if x+w > mouse[0] > x and y+h > mouse[1] > y: pygame.draw.rect(pencere, ic,(x,y,w,h)) if click[0] == 1 != None: action() smallText = pygame.font.Font("freesansbold.ttf",20) textSurf, textRect = text_objects(msg, smallText, white) textRect.center = ( (x+(w/2)), (y+(h/2)) ) pencere.blit(textSurf, textRect) def loop(): cikis = False while not cikis: for event in pygame.event.get(): if event.type == pygame.QUIT: cikis = True pygame.quit() quit() pencere.fill(white) smallText = pygame.font.Font("freesansbold.ttf",50) textSurf, textRect = text_objects(str(number.count), smallText, black) textRect.center = ((x/2)), (30) pencere.blit(textSurf, textRect) button("Click",0,100,200,200,black,black2,number.click) pygame.display.update() loop() pygame.quit() quit() 

Es gibt mehrere Dinge, die geändert werden sollten:

Der Zeichen- und Schaltflächencode sollte nicht in der Ereignisschleife, sondern in der äußeren While-Schleife sein. Sie rufen die button jedes Mal auf, wenn ein Ereignis eintritt (z. B. wenn sich die Maus bewegt).

Die button macht zu viel. Er erstellt und blendet Textoberflächen, zeichnet Retrakte, prüft auf Kollisionen und ruft die Klickmethode auf.

Sie sollten pygame.mouse.get_pressed() nicht verwenden und stattdessen die MOUSEBUTTONDOWN Ereignisse in der Ereignisschleife behandeln. mouse.get_pressed prüft nur, ob eine Maustaste gedrückt ist und nicht, ob ein einzelner Klick aufgetreten ist.

Ich zeige dir einfach eine einfache Lösung ohne function und mit einem Rect als Button hier. Ich handhabe die Kollision und aktualisiere die Nummer in der Ereignisschleife. Wenn Sie mehrere Schaltflächen erstellen möchten, würde ich vorschlagen, sie objektorientiert neu zu schreiben (ich kann Ihnen ein Beispiel zeigen, wenn Sie möchten).

 import pygame pygame.init() width, height = (200,300) screen = pygame.display.set_mode((width, height)) WHITE = (255, 255, 255) BLACK = (0, 0, 0) GRAY = (30, 30, 30) FONT = pygame.font.Font("freesansbold.ttf", 50) def loop(): clock = pygame.time.Clock() number = 0 # The button is just a rect. button = pygame.Rect(0, 100, 200, 200) done = False while not done: for event in pygame.event.get(): if event.type == pygame.QUIT: done = True # This block is executed once for each MOUSEBUTTONDOWN event. elif event.type == pygame.MOUSEBUTTONDOWN: # 1 is the left mouse button, 2 is middle, 3 is right. if event.button == 1: # `event.pos` is the mouse position. if button.collidepoint(event.pos): # Increment the number. number += 1 screen.fill(WHITE) pygame.draw.rect(screen, GRAY, button) text_surf = FONT.render(str(number), True, BLACK) # You can pass the center directly to the `get_rect` method. text_rect = text_surf.get_rect(center=(width/2, 30)) screen.blit(text_surf, text_rect) pygame.display.update() clock.tick(30) loop() pygame.quit() 

Nachtrag: Ich empfehle eine objektorientierte Lösung mit einer Button class, die eine Unterklasse von pygame.sprite.Sprite und zu Sprite-Gruppen hinzugefügt werden kann. Sie können eigene Bilder an die Button class übergeben oder die Standardbilder verwenden. Sie müssen auch eine Callback-function oder -Methode an jede Schaltflächeninstanz übergeben, die in der Methode handle_event wird, um die spezifischen Attribute der Spielklasse zu aktualisieren (hier habe ich eine Methode, die einen Zähler erhöht und eine weitere, um das Spiel zu beenden).

 import pygame as pg pg.init() screen = pg.display.set_mode((800, 600)) FONT = pg.font.SysFont('Comic Sans MS', 32) # Default button images/pygame.Surfaces. IMAGE_NORMAL = pg.Surface((100, 32)) IMAGE_NORMAL.fill(pg.Color('dodgerblue1')) IMAGE_HOVER = pg.Surface((100, 32)) IMAGE_HOVER.fill(pg.Color('lightskyblue')) IMAGE_DOWN = pg.Surface((100, 32)) IMAGE_DOWN.fill(pg.Color('aquamarine1')) # Button is a sprite subclass, that means it can be added to a sprite group. # You can draw and update all sprites in a group by # calling `group.update()` and `group.draw(screen)`. class Button(pg.sprite.Sprite): def __init__(self, x, y, width, height, callback, font=FONT, text='', text_color=(0, 0, 0), image_normal=IMAGE_NORMAL, image_hover=IMAGE_HOVER, image_down=IMAGE_DOWN): super().__init__() # Scale the images to the desired size (doesn't modify the originals). self.image_normal = pg.transform.scale(image_normal, (width, height)) self.image_hover = pg.transform.scale(image_hover, (width, height)) self.image_down = pg.transform.scale(image_down, (width, height)) self.image = self.image_normal # The currently active image. self.rect = self.image.get_rect(topleft=(x, y)) # To center the text rect. image_center = self.image.get_rect().center text_surf = font.render(text, True, text_color) text_rect = text_surf.get_rect(center=image_center) # Blit the text onto the images. for image in (self.image_normal, self.image_hover, self.image_down): image.blit(text_surf, text_rect) # This function will be called when the button gets pressed. self.callback = callback self.button_down = False def handle_event(self, event): if event.type == pg.MOUSEBUTTONDOWN: if self.rect.collidepoint(event.pos): self.image = self.image_down self.button_down = True elif event.type == pg.MOUSEBUTTONUP: # If the rect collides with the mouse pos. if self.rect.collidepoint(event.pos) and self.button_down: self.callback() # Call the function. self.image = self.image_hover self.button_down = False elif event.type == pg.MOUSEMOTION: collided = self.rect.collidepoint(event.pos) if collided and not self.button_down: self.image = self.image_hover elif not collided: self.image = self.image_normal class Game: def __init__(self, screen): self.done = False self.clock = pg.time.Clock() self.screen = screen # Contains all sprites. Also put the button sprites into a # separate group in your own game. self.all_sprites = pg.sprite.Group() self.number = 0 # Create the button instances. You can pass your own images here. self.start_button = Button( 320, 70, 170, 65, self.increment_number, FONT, 'Increment', (255, 255, 255), IMAGE_NORMAL, IMAGE_HOVER, IMAGE_DOWN) # If you don't pass images, the default images will be used. self.quit_button = Button( 320, 240, 170, 65, self.quit_game, FONT, 'Quit', (255, 255, 255)) # Add the button sprites to the sprite group. self.all_sprites.add(self.start_button, self.quit_button) def quit_game(self): """Callback method to quit the game.""" self.done = True def increment_number(self): """Callback method to increment the number.""" self.number += 1 print(self.number) def run(self): while not self.done: self.dt = self.clock.tick(30) / 1000 self.handle_events() self.run_logic() self.draw() def handle_events(self): for event in pg.event.get(): if event.type == pg.QUIT: self.done = True for button in self.all_sprites: button.handle_event(event) def run_logic(self): self.all_sprites.update(self.dt) def draw(self): self.screen.fill((30, 30, 30)) self.all_sprites.draw(self.screen) pg.display.flip() if __name__ == '__main__': pg.init() Game(screen).run() pg.quit() 

Addendum 2: Eine Zwischenlösung mit Schaltflächen als Wörterbücher. Es wäre auch möglich, Listen zu verwenden, aber Wörterbücher sind besser lesbar.

 import pygame pygame.init() WHITE = (255, 255, 255) ACTIVE_COLOR = pygame.Color('dodgerblue1') INACTIVE_COLOR = pygame.Color('dodgerblue4') FONT = pygame.font.Font(None, 50) def draw_button(button, screen): """Draw the button rect and the text surface.""" pygame.draw.rect(screen, button['color'], button['rect']) screen.blit(button['text'], button['text rect']) def create_button(x, y, w, h, text, callback): """A button is a dictionary that contains the relevant data. Consists of a rect, text surface and text rect, color and a callback function. """ # The button is a dictionary consisting of the rect, text, # text rect, color and the callback function. text_surf = FONT.render(text, True, WHITE) button_rect = pygame.Rect(x, y, w, h) text_rect = text_surf.get_rect(center=button_rect.center) button = { 'rect': button_rect, 'text': text_surf, 'text rect': text_rect, 'color': INACTIVE_COLOR, 'callback': callback, } return button def main(): screen = pygame.display.set_mode((640, 480)) clock = pygame.time.Clock() done = False number = 0 def increment_number(): # A callback function for the button. """Increment the `number` in the enclosing scope.""" nonlocal number number += 1 print(number) def quit_game(): # A callback function for the button. nonlocal done done = True button1 = create_button(100, 100, 250, 80, 'Click me!', increment_number) button2 = create_button(100, 200, 250, 80, 'Me too!', quit_game) # A list that contains all buttons. button_list = [button1, button2] while not done: for event in pygame.event.get(): if event.type == pygame.QUIT: done = True # This block is executed once for each MOUSEBUTTONDOWN event. elif event.type == pygame.MOUSEBUTTONDOWN: # 1 is the left mouse button, 2 is middle, 3 is right. if event.button == 1: for button in button_list: # `event.pos` is the mouse position. if button['rect'].collidepoint(event.pos): # Increment the number by calling the callback # function in the button list. button['callback']() 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 button['rect'].collidepoint(event.pos): button['color'] = ACTIVE_COLOR else: button['color'] = INACTIVE_COLOR screen.fill(WHITE) for button in button_list: draw_button(button, screen) pygame.display.update() clock.tick(30) main() pygame.quit()