Seite 1 von 1

Problem mit event in pygame

Verfasst: Mittwoch 18. November 2020, 09:35
von kaihawai
Guten Morgen,
ich habe online einen python Kurs gefunden und wollte diesen gerne durcharbeiten. Das Ganze soll dazu dienen, das Programmieren etwas zu lernen, ich bin also noch ganz am Anfang.
Nun bin ich bei dem Spiel Pong angelangt und habe dazu pygame benutzt.

am linken Rand habe ich nun einen "Schläger" also quasi ein Rechteck definiert, das ich mit den Tasten "W" und "S" hoch, bzw. runter bewegen kann. Das funktioniert auch soweit gut.
Allerdings hätte ich ein anderes Verhalten erwartet. Im Moment ist es so, dass beim Drücken der Inhalt der IF Schleife eimal abgelaufen wird und damit der "Schläger" um einen Schritt bewegt wird.
Mein Ziel war es aber, dass der Schläger sich so lange bewegt, wie ich die Taste gedrückt halte.
Was habe ich da falsch gemacht? Wäre schön, wenn mir da jemand einen Tipp geben könnte :-)

Hier der Codeschnipsel:
while spielaktiv:
# Überprüfen, ob Nutzer eine Aktion durchgeführt hat
for event in pygame.event.get():
if event.type == pygame.QUIT:
spielaktiv = False
print("Spieler hat Quit-Button angeklickt")
elif event.type == pygame.KEYDOWN:
print("Spieler hat Taste gedrückt")
# Taste für Spieler 1inks
if event.key == pygame.K_UP:
if schlaeger_l_y >0 :
schlaeger_l_y = schlaeger_l_y -10

print("Spieler hat Pfeiltaste hoch gedrückt")
if event.key == pygame.K_DOWN:
if schlaeger_l_y < spielfeldgroesse_y - schlaeger_l_hoehe:
schlaeger_l_y = schlaeger_l_y +10
print("Spieler hat Pfeiltaste runter gedrückt")
# Taste für Spieler rechts
if event.key == pygame.K_w:
if schlaeger_r_y >0 :
schlaeger_r_y = schlaeger_r_y -10
print("Spieler hat Pfeiltaste hoch gedrückt")
if event.key == pygame.K_s:
if schlaeger_r_y < spielfeldgroesse_y - schlaeger_r_hoehe:
schlaeger_r_y = schlaeger_r_y +10
print("Spieler hat Pfeiltaste runter gedrückt")

Re: Problem mit event in pygame

Verfasst: Mittwoch 18. November 2020, 12:06
von __blackjack__
@kaihawai: Anmerkungen zum Quelltext: Eingerückt wird in Python vier Leerzeichen pro Ebene. Das ist sehr unsauber in dem Code, fast so als würdest Du tatsächlich mit der Leertaste einrücken. Das macht man üblicherweise mit der Tab-Taste und stellt den Editor so ein, dass da keine Tabulatorzeichen sondern eben vier Leerzeichen verwendet werden.

Kommentare sollen dem Leser einen Mehrwert über den Code geben. Faustregel: Kommentare beschreiben nicht *was* der Code macht, denn das steht da bereits als Code, sondern warum er das macht. Sofern das nicht offensichtlich ist. Offensichtlich ist in aller Regel auch was in der Dokumentation von Python und den verwendeten Bibliotheken steht.

Bei `spielaktiv` fehlt der Unterstrich um die beiden Worte zu trennen.

Namen sollten keine kryptischen Abkürzungen enthalten.

Bei den `print()`-Ausgaben für den rechten Spieler stimmt das mit den Pfeiltasten nicht

Bei den Vergleichen auf `event.key` sollte man ``elif`` verwenden, damit nicht unnötigerweise alle Varianten geprüft werden, wo ja klar ist, dass es da nur *einen* Wert geben kann.

”Magische” Zahlen im Code sollte man vermeiden. Statt da mehrfach eine 10 in den Quelltext zu schreiben die für die Schlägergeschwindigkeit steht (beziehungsweise die Distanz die bei einem Spielschritt zurückgelegt werden kann), würde man dafür eine Konstante definieren. Damit ist die Bedeutung für den Leser klarer, man kann diese 10nen von anderen 10en mit anderer Bedeutung im Programm leicht unterscheiden, und sollte man den Wert einmal ändern wollen oder müssen, kann man das zentral an einer Stelle im Code machen.

Zwischenstand:

Code: Alles auswählen

SCHLAEGER_GESCHWINDIGKEIT = 10

...

def ...:
    ...

    while spiel_aktiv:
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                spiel_aktiv = False
                print("Spieler hat Quit-Button angeklickt")

            elif event.type == pygame.KEYDOWN:
                print("Spieler hat Taste gedrückt")
                #
                # Tasten für Spieler links.
                #
                if event.key == pygame.K_UP:
                    if linker_schlaeger_y > 0:
                        linker_schlaeger_y = (
                            linker_schlaeger_y - SCHLAEGER_GESCHWINDIGKEIT
                        )
                        print("Spieler hat Pfeiltaste hoch gedrückt")
                elif event.key == pygame.K_DOWN:
                    if (
                        linker_schlaeger_y
                        < spielfeldgroesse_y - linker_schlaeger_hoehe
                    ):
                        linker_schlaeger_y = (
                            linker_schlaeger_y + SCHLAEGER_GESCHWINDIGKEIT
                        )
                        print("Spieler hat Pfeiltaste runter gedrückt")
                #
                # Tasten für Spieler rechts.
                #
                elif event.key == pygame.K_w:
                    if rechter_schlaeger_y > 0:
                        rechter_schlaeger_y = (
                            rechter_schlaeger_y - SCHLAEGER_GESCHWINDIGKEIT
                        )
                    print("Spieler hat w gedrückt")
                elif event.key == pygame.K_s:
                    if (
                        rechter_schlaeger_y
                        < spielfeldgroesse_y - rechter_schlaeger_hoehe
                    ):
                        rechter_schlaeger_y = (
                            rechter_schlaeger_y + SCHLAEGER_GESCHWINDIGKEIT
                        )
                    print("Spieler hat s gedrückt")
    ...
Nun zum ja eigentlich offensichtlichen Problem: Das KEYDOWN-Ereignis wird einmal erzeugt wenn die Taste runtergedrückt wird. Der Code der darauf reagiert wird dann halt auch nur einmal abgearbeitet. Statt beim KEYDOWN-Ereignis also sofort etwas zu machen, muss man sich den Zustand merken und auch Code schreiben der auf das KEYUP-Ereignis reagiert und dort den Zustand wieder zurücksetzen. Und dann reagiert man ausserhalb der Schleife die die Ereignisse abarbeitet auf den gemerkten Zustand.

Man könnte sich die Tasten einzeln merken ob sie gedrückt sind oder nicht oder die jeweils zwei Tasten pro Schläger zusammenfassen als Geschwindigkeit des jeweiligen Schlägers. Also -10, 0, oder 10, je nachdem in welche Richtung der sich aufgrund der Tasten bewegen soll.

Ungetestet:

Code: Alles auswählen

    ...

    linker_schlaeger_geschwindigkeit = 0
    rechter_schlaeger_geschwindigkeit = 0

    while spiel_aktiv:
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                spiel_aktiv = False
                print("Spieler hat Quit-Button angeklickt")

            elif event.type == pygame.KEYDOWN:
                print("Spieler hat Taste gedrückt")
                #
                # Tasten für Spieler links.
                #
                if event.key == pygame.K_UP:
                    linker_schlaeger_geschwindigkeit = (
                        -SCHLAEGER_GESCHWINDIGKEIT
                    )
                    print("Spieler hat Pfeiltaste hoch gedrückt")
                elif event.key == pygame.K_DOWN:
                    linker_schlaeger_geschwindigkeit = (
                        SCHLAEGER_GESCHWINDIGKEIT
                    )
                    print("Spieler hat Pfeiltaste runter gedrückt")
                #
                # Tasten für Spieler rechts.
                #
                elif event.key == pygame.K_w:
                    rechter_schlaeger_geschwindigkeit = (
                        -SCHLAEGER_GESCHWINDIGKEIT
                    )
                    print("Spieler hat w gedrückt")
                elif event.key == pygame.K_s:
                    rechter_schlaeger_geschwindigkeit = (
                        SCHLAEGER_GESCHWINDIGKEIT
                    )
                    print("Spieler hat s gedrückt")
            
            elif event.type == pygame.KEYUP:
                print("Spieler hat Taste losgelassen")
                if event.key in [pygame.K_UP, pygame.K_DOWN]:
                    linker_schlaeger_geschwindigkeit = 0
                elif event.key in [pygame.K_w, pygame.K_s]:
                    rechter_schlaeger_geschwindigkeit = 0

        neues_y = linker_schlaeger_y + linker_schlaeger_geschwindigkeit
        if 0 <= neues_y <= spielfeldgroesse_y - linker_schlaeger_hoehe:
            linker_schlaeger_y = neues_y
        
        neues_y = rechter_schlaeger_y + rechter_schlaeger_geschwindigkeit
        if 0 <= neues_y <= spielfeldgroesse_y - rechter_schlaeger_hoehe:
            rechter_schlaeger_y = neues_y

        ...
Eine einfache Vereinfachung wäre übrigens nicht so viele Einzelwerte für die Schläger (und das Spielfeld) zu verwenden sondern `pygame.Rect`-Objekte. Damit kann man Position und Grösse in einem Wert speichern, über Properties auf alle möglichen Punkte/Koordinaten(teile) zugreifen, und praktische Methoden bieten `Rect`-Objekte auch noch:

Code: Alles auswählen

    ...

    spielfeld_rect = ...  # `Surface`-Objekte haben da eine Methode…

    linker_schlaeger_rect = pygame.Rect(...)
    linker_schlaeger_geschwindigkeit = 0
    
    rechter_schlaeger_rect = pygame.Rect(...)
    rechter_schlaeger_geschwindigkeit = 0

    while spiel_aktiv:
        for event in pygame.event.get():
            ...

        neues_rect = linker_schlaeger_rect.move(
            0, linker_schlaeger_geschwindigkeit
        )
        if spielfeld_rect.contains(neues_rect):
            linker_schlaeger_rect = neues_rect

        neues_rect = rechter_schlaeger_rect.move(
            0, rechter_schlaeger_geschwindigkeit
        )
        if spielfeld_rect.contains(neues_rect):
            rechter_schlaeger_rect = neues_rect

        ...

Re: Problem mit event in pygame

Verfasst: Donnerstag 19. November 2020, 07:23
von kaihawai
Wow, vielen Dank für die ausführliche Antwort.
Ok, das Problem habe ich verstanden, mit den ganzen anderen Sachen muss ich mich beschäftigen, das dauert etwas. :-(