Zum Inhalt

Gruppenkollisionen und verschiedene Fensterausgaben

Lernziele

  • Mehrer Sprite Objekte der gleichen Sorte erstellen
  • Gruppenkollisionen
  • Verschiedene Bildschirme anzeigen lassen
  • Event Tastendruck und Taste loslassen

Einführung

Als Vorlage dient das folgende Programm. Lade es herunter und starte es.

Es finden sich eine Maus, welche mit den Pfeiltasten bewegt werden kann, eine Münze und etwas Text auf dem Spielfenster. Beim Endprogramm liegen dann mehrere Münzen herum, welche von der Maus eingesammelt werden sollen. Nachdem die Maus alle Münzen eingesammelt hat, soll ein Endbildschirm erscheinen, welcher das Ende des Spiels anzeigt. Um dies alles zu implementieren, gehen wir die folgenden Schritte durch.

Mehrer Sprite Objekte der gleichen Sorte erstellen

Zuerst sollen mehr als nur eine Münze auf dem Bildschirm erscheinen, z.B. 6 Münzen. Dies wird im Abschnitt "Globale Variabeln initialisieren" durchgeführt.

Für die eine Münze wird aktuell einfach die Variable munze verwendet. Wenn wir nun aber insgesamt 6 Münzen oder auch mehr erstellen wollen, macht es keinen Sinn dafür 6 verschienden Variablen (z.B. munze1, munze2, munze3,...) zu gebrauchen. Eine Liste, hier eine Sprite-Group, ist für unseren Fall die ideale Datenstruktur.

  1. Passe den Wert der Variable anzahl_munzen auf die gewünschte Anzahl Münzen an.
  2. Erstelle gleich danach eine neue Sprite Gruppe und speichere sie z.B. unter dem Namen all_munzen.
  3. In einer for-Schleife sollen nun anzahl_munzen Münzen erstellt werden und der Gruppe all_munzen hinzugefügt werden. Dies kann wie folgt in der for-Schleife gemacht werden:

    all_munzen.add(Coin())
    
    4. Nachdem alle Münzen erstellt worden sind, müssen sich der Gruppe all_sprites hinzuefügt werden, damit sie dann auch auf dem Bildschirm dargestellt werden. Dies geht ganz einfach mittels
    all_sprites.add(all_munzen)
    
    5. Teste das Programm.

    Gruppe mit Münzen

Gruppenkollisionen

Jetzt schicken wir die Maus auf Münzenjagd.

Um Kollisionen zwischen Sprites festzustellen findest du in der Pygame Dokumentation eine ganze Liste an verschiedenen Kollisionfunktionen. Im letzten Kapitel haben wir die beiden Funktionen collide_rect() und collide_mask() kennengelernt. Dabei konnte eine Berührung zwischen zwei Sprites festgestellt werden. Da wir nun aber eine ganze Liste (hier alle Münzen in der Liste all_munzen) auf Berührung mit der Maus überprüfen müssen, eigenen sich dafür andere Funktionen viel besser.

  1. Welche Collide-Funktionen in der Pygame Dokumentation würdest du für unsere Situation als geeignet betrachten?

    Lösung

    spritecollide() passt perfekt für unsere Sitauation.

    Beschreibung der Funktion:

    Find sprites in a group that intersect another sprite.

  2. Verwende diese Kollisionsfunktion in unserer globalen Funktion is_a_collision(). Implementiere deren Funktionalität. Hier ein paar Erklärungen.

    spritecollide(sprite, group, dokill, collided = None) -> Sprite_list
    

    Erklärung der Parameter:

    • sprite: Mit welchem Sprite Objekt soll eine Kollision festgestellt werden?

      In unserem Code wäre das maus.

    • group: Die Liste der Sprite Objekte mit der eine Kollision mit sprite festgestellt werden soll.

      In unserem Code wäre das all_munzen.

    • dokill: Soll bei einer Kollision das Sprite Objekte aus group aus dem Spiel entfernt werden?

      Bei uns lautet die Antwort "ja" (also True), da die Münze, welche die Maus berührt, nach einer Berührung nicht mehr auf dem Bildschirm erscheinen soll.

    • collided = None: Welche Kollisionsüberprüfung soll verwendet werden?

      Wir wollen bei einer Pixelkollision eine Berührung feststellen und d.h. in unserem Code collided = py.sprite.collide_mask.

    Als Rückgabeparameter erhalten wir eine Liste mit allen Sprites aus group, mit denen eine Kollision festgestellt wurde. Wenn diese Liste also nicht leer ist, hat unsere Maus eine Münze berührt.

  3. Teste, ob die globale Funktion is_a_collision() funktioniert. Falls bei einer Berührung der Maus mit einer Münze das Spiel beendet wird, so funktioniert sie.

  4. Ändere das Programm nun so ab, dass bei einer Berührung der Maus mit einer Münze nicht das Programm beendet wird, sondern sich der Zähler im Text auf dem Bildschrim A"nzahl verbleibende Münzen" entsprechend anpasst.

    Hinweis: Mit len(all_munzen) können wir die Anzahl der übrigen Münzen im Spiel abfragen.

  5. Wurden alle Münzen eingesammelt, so soll das Programm beendet werden.

Endbildschirm

Bei einem "normalen" Spiel ist es üblich, dass z.B. vor dem eigentlichen Spielbeginn ein Startbild erscheint, mit gewissen Instruktionen. Bei Spielende könnte auch ein entsprechendes Fenster erscheinen, welches uns über das Ende des Spiels informiert. Ein solches Verhalten wollen wir nun in unserem Spiel implementieren.

Bildschirmwechsel

Es gibt gewiss unzählige Methoden, um einen solchen Fensterwechsel zu implementieren. Hier wird eine vorgestellt.

  1. Definieren im Abschnitt "Globale Variabeln initialisieren" eine neue Variable which_window, deren Wert bestimmt, auf welchem Bildschirm wir uns befinden.

    • 0: Bildschirm während des Spiels
    • 1: Endschirm, erscheint wenn alle Muenzen eingesammelt wurden
  2. Schaue dir im Programmcode die "Hauptschleife" an und überlege dir, welche Codezeilen auch beim Endbildschirm ausgeführt werden sollen und welche nicht.

    Setze alle Zeilen, welche nur beim Spielbildschirm ausgeführt werden sollen, in einen if-Block. Hier if which_window == 0:. Wenn du das Programm ausführst, sollte alles noch genau gleich ablaufen.

    Hinweis

    Die beiden Codeblöcke

    ##########################################
    # Events abfragen
    ##########################################
    for event in py.event.get():
        # Wird das Fenster geschlossen, soll
        # auch das Spiel beendet werden
        if event.type == py.QUIT:
            game_is_running = False
    

    und

    py.display.update()
    clock.tick(fps)
    

    gehören nicht in den if-Block und sollten somit immer ausgeführt werden.

  3. Erweitere den if which_window == 0: Block mit einem else-Block, in welchem die Darstellung des Endbildschirms implementiert werden soll. Dieser könnte so aussehen.

    Endbildschirm

  4. Teste die Implementation deines Endbildschirms, indem du which_window vor der Hauptschleife auf 1 setzt. Setze für diesen Test einen beliebigen Wert für die Endzeit.

  5. Setze which_window wieder auf 0.

    Ergänze nun den if which_window == 0: Block so, dass, nachdem alle Münzen eingesammelt wurden, der Endbildschirm erscheint.

Tastendruck

Wir bauen noch eine kleine Zusatzfunktion ein. Das Programm kann ja nur durch Schliessen des Fensters beendet werden. Zusätzlich soll dies auch durch Drücken der ESC-Taste geschehen, jedoch erst dann, wenn die ESC-Taste losgelassen wird.

Ergänze im for event in py.event.get(): Block, das if Statement um die folgende Funktionalität:

event.type == py.KEYUP and event.key == py.K_ESCAPE

Der Befehl event.type == py.KEYUP sorgt dafür, dass das Programm erst beim Loslassen der ESC-Taste beendet wird.