Es gibt mehrere Dinge, die geändert werden sollen:
Die Zeichnung und Button-Code nicht in der Ereignisschleife sein sollte, aber in den äußeren while-Schleife. Sie rufen die Funktion button
jedes Mal auf, wenn ein Ereignis auftritt (z. B. wenn sich die Maus bewegt).
Die button
Funktion macht zu viel. Er erstellt und blendet Textoberflächen, zeichnet Reklikationen, prüft auf Kollisionen und ruft die Methode click
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, wenn ein einzelner Klick aufgetreten ist.
Ich zeige Ihnen einfach eine einfache Lösung ohne eine Funktion und mit einem Rect als die Taste 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
-Klasse, die eine Unterklasse von pygame.sprite.Sprite
und kann zu Sprite Gruppen hinzugefügt werden. Sie können Ihre eigenen Bilder an die Klasse Button
übergeben oder die Standardbilder verwenden. Sie müssen auch eine Callback-Funktion oder -Methode an jede Button-Instanz übergeben, die in der handle_event
-Methode aufgerufen 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 Tasten 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()
Können Sie den von Ihnen verwendeten Code angeben? – serge1peshcoff
müssen Sie zu Tastenvariable dh hinzufügen. 'clicked = True', um sich daran zu erinnern und es zu überprüfen, bevor Sie die der Schaltfläche zugewiesene Funktion ausführen. Und löschen Sie es, wenn Sie die Maustaste loslassen. – furas
BTW: verwenden Sie 'event.type == MOUSEBUTTONDOWN' oder' pygame.mouse.get_pressed() '? Mit 'MOUSEBUTTONDOWN' sollte dieses Problem nicht auftreten, da ein Ereignis nur dann erzeugt wird, wenn die Maustaste den Zustand für 'nicht gedrückt' in 'gedrückt' ändert, aber nicht erzeugt wird, wenn die Maustaste gedrückt gehalten wird. 'get_pressed()' gibt 'True' zurück, wenn die Maustaste gedrückt gehalten wird. – furas