Maussteuerung von Breakout

Wenn du dir nicht sicher bist, in welchem der anderen Foren du die Frage stellen sollst, dann bist du hier im Forum für allgemeine Fragen sicher richtig.
Antworten
kwon
User
Beiträge: 43
Registriert: Samstag 2. Mai 2020, 11:48

Hallo, liebe Gemeinde!
Ich bin ein Python-Neuling (habe aber in anderen Sprachen etwas Programmierkenntnisse).
Ich würde gerne das Spiel "Breakout" mit einer Maussteuerung programmieren.
Zuerst möchte ich mich auf die Mausbewegung des Paddels konzentrieren - habe mich dazu an einem Code orientiert, den ich online gefunden habe.
Leider sehe ich nur einen schwarzen Bildschirm wenn ich das Skript ausführe...
Woran könnte das liegen?
Über Hilfe würde ich mich sehr freuen...
Vielen Dank!

Code: Alles auswählen

import pygame
#colors
blue = (0,0,255)
white = (255,255,255)

class Paddel(pygame.sprite.Sprite):
    def __init__(self):
        super().__init__()
        self.width = 80
        self.height = 15
        self.image = pygame.Surface([self.width,self.height])
        self.image.fill(blue)
        self.rect = self.image.get_rect()
        self.screenheight = pygame.display.get_surface().get_height()
        self.screenwidth = pygame.display.get_surface().get_width()

        self.rect.x = 0
        self.rect.y = self.screenheight - self.height

    def update(self):
        pos = pygame.mouse.get_pos()
        self.rect.x = pos[0] - int(self.width/2)
        if self.rect.x > self.screenwidth - self.width:
            self.rect.x = self.screenwidth - self.width
    
pygame.init()
screen = pygame.display.set_mode([800, 600])
pygame.mouse.set_visible(1)
background = pygame.Surface(screen.get_size())
allsprites = pygame.sprite.Group()
paddel = Paddel()
allsprites.add(paddel)

clock = pygame.time.Clock()

game_over = False
exit_program = False

while not exit_program:
    clock.tick(30)
    screen.fill(white)
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            exit_program = True
        
        if not game_over:
            paddel.update()
allsprites.draw(screen)
pygame.display.flip()
pygame.quit()
kwon
User
Beiträge: 43
Registriert: Samstag 2. Mai 2020, 11:48

Hallo,
habe es selbst geschafft - es war ein Einrückungsfehler!

Code: Alles auswählen

while not exit_program:
    clock.tick(60)
    screen.fill(white)
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            exit_program = True
        
        if not game_over:
            paddel.update()
            allsprites.draw(screen)
            pygame.display.flip()
pygame.quit()
Benutzeravatar
__blackjack__
User
Beiträge: 14078
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@kwon: Das ist auch an der neuen Stelle falsch eingerückt, weil jetzt pro Ereignis ein `update()` stattfindet — also auch wenn es gar kein Mausereignis ist. Und noch schlimmer: die Anzeige wird pro Ereignis neu gezeichnet, was auf der einen Seite das `clock`-Objekt ”aushebelt” und auf der anderen Seite die GUI nicht aktualisiert wenn keine Ereignisse kommen. Was zu Darstellungsproblemen/Fehlern führen kann wenn man beispielsweise ein anderes Fenster vor/über das Pygame-Fenster bewegt, weil dabei eventuell zerstörte Teile der Anzeige eventuell nicht neu gezeichnet/repariert werden.

Weitere Anmerkungen zum Quelltext:

Auf Modulebene sollte nur Code stehen der Konstanten, Funktionen, und Klassen definiert. Das Hauptprogramm steht üblicherweise in einer Funktion die `main()` heisst.

Konstanten werden per Konvention KOMPLETT_GROSS geschrieben.

Das `pygame.quit()` könnte man mit ``try``/``finally`` ein bisschen besser garantieren.

`background` wird definiert, aber nirgends verwendet.

Ich vermute mal ganz stark, dass `Paddel` eigentlich `Paddle` heissen soll. 😎

Die Attribute `width` und `height` sind in `Paddle` redundant, weil diese Informationen bereits in `rect` stecken.

Im `Paddle` auf das Display-Surface zuzugreifen ist nicht gut. Das Paddle sollte nicht selbst ermitteln wo es dargestellt wird, denn so verbaut man sich Möglichkeiten. Beispielsweise eine Begrenzung um das Spielfeld oder andere Anzeigen wie Punktestand, ein Logo, und so weiter. Also braucht mindestens die `__init__()`- und vielleicht auch die `update()`-Methode mindestens ein `Rect`-Objekt als Argument in welchen Grenzen es sich bewegen kann/darf.

Man muss bei `Rect`-Objekten auch weniger eigene Rechnungen mit der Höhe und der Breite anstellen und bekommt damit auch leichter lesbaren Code. Um beispielsweise die Unterseite eines `Rect` an der Unterseite eines anderen auszurichten, muss man nicht selbst die `y`-Position anhand der Höhe ausrechenen, sondern kann direkt schreiben was man will: ``paddle_rect.bottom = playfield_rect.bottom``. Und man muss auch keine Höhe oder Breite durch 2 teilen, weil es `center`, `centerx`, `centery`, `mitbottom`, `midleft`, `midright`, und `midtop` als Attribute auf `Rect`-Objekten gibt. Und um die Position eines `Rect`-Objektes komplett in ein anderes zu verschieben, sollte es ausserhalb liegen, gibt es die `clamp()`-Methode.

Auch das `Paddle`-Objekte die Mausposition selbst abfragen ist unsauber. Die sollte beim `update()` übergeben werden, und auch nur wenn ein Mausereignis verarbeitet wird. Eventuell sogar auch erst nachdem alle Ereignisse abgearbeitet werden, damit man sich `update()`-Aufrufe spart, die sowieso nicht gezeichnet werden.

Zwischenstand:

Code: Alles auswählen

#!/usr/bin/env python3
import pygame

BLUE = (0, 0, 255)
WHITE = (255, 255, 255)


class Paddle(pygame.sprite.Sprite):
    def __init__(self, playfield_rect):
        pygame.sprite.Sprite.__init__(self)
        self.playfield_rect = playfield_rect
        self.image = pygame.Surface((80, 15))
        self.image.fill(BLUE)
        self.rect = self.image.get_rect()
        self.rect.bottom = self.playfield_rect.bottom

    def update(self, position):
        x, _ = position
        self.rect.centerx = x
        self.rect = self.rect.clamp(self.playfield_rect)


def main():
    pygame.init()
    try:
        screen = pygame.display.set_mode((800, 600))
        pygame.mouse.set_visible(1)
        all_sprites = pygame.sprite.Group()
        paddle = Paddle(screen.get_rect())
        all_sprites.add(paddle)

        clock = pygame.time.Clock()
        while True:
            clock.tick(30)
            screen.fill(WHITE)
            
            for event in pygame.event.get():
                if event.type == pygame.QUIT:
                    return
                elif event.type == pygame.MOUSEMOTION:
                    paddle.update(event.pos)

            all_sprites.draw(screen)
            pygame.display.flip()
    finally:
        pygame.quit()


if __name__ == "__main__":
    main()
“Vir, intelligence has nothing to do with politics!” — Londo Mollari
kwon
User
Beiträge: 43
Registriert: Samstag 2. Mai 2020, 11:48

Hallo __blackjack__,
vielen, vielen Dank für deine ausführliche und sehr gute Antwort!
Anhand deiner Antwort merke ich, dass es keine gute Idee ist, einfach so draufloszuprogrammieren... Ich merke auch, dass ich mich noch etwas mehr in die Thematik einlesen muss. Ich werde mir deine Antwort für eine spätere Lektüre aufheben (sollte mein Brightness-Button dann funktioniert haben)... :-)
Ich wünsche dir einen schönen Abend... Viele Grüße, kwon
Antworten