8. Game

Einführung in Pygame

In diesem Kapitel entwickeln wir Schritt für Schritt ein eigenes 2D-Game mit pygame-ce. pygame-ce ist eine moderne, Community-gepflegte Variante von Pygame und wird genau gleich importiert mit import pygame as pg. Sie eignet sich hervorragend, um in Python Grafiken, Animationen, Sound und Interaktionen umzusetzen.

Essentielle Bausteine eines Games sind:

  • Fenster (Auflösung, Titel) und Zeichenfläche (“screen”)
  • Game-Schleife mit Ereignissen (Tastatur, Maus) und Zeitsteuerung ( / “refresh rate”)
  • Zeichnen: Hintergrund, Farben, geometrische Figuren, Bilder (“Sprites”)
  • Zustände und Objekte (z.B. Spieler als Rect), Bewegungen und Kollisionen
  • Medien: Bilder, Icon, Schrift, Sound / Musik
Definition 8.1: Game-Schleife

Ein Game läuft in einer Endlosschleife, bis das Programm beendet wird. In jeder Runde werden der Eingabestatus gelesen (“Events”), der interne Zustand aktualisiert (“Update”) und die Szene gezeichnet (“Render”). Eine Clock (\(=\) Uhr) begrenzt die Bildwiederholrate (), sodass das Game stabil und gleichmässig läuft.

Um pygame-ce in VS Code zu installieren, gehen Sie wie folgt vor:

  1. Öffnen Sie ein Terminal-Fenster in VS Code (“Terminal” \(\rightarrow\) “New Terminal”).
  2. Installieren Sie pygame-ce mit pip: pip install pygame-ce
  3. Überprüfen Sie die Installation, indem Sie eine neue Python-Datei game_test.py mit folgendem Code ausführen:
import pygame as pg
        print(pg.ver)
Note

💡 Hinweis: Dieses Skript verwendet grafische Desktop-Bibliotheken (turtle / pygame). Führen Sie diesen Code in Ihrer lokalen Entwicklungsumgebung (z.B. VS Code) aus.

Dieser Code sollte, sofern pygame-ce korrekt installiert ist, die Versionsnummer von pygame-ce ausgeben. Damit ist pygame-ce einsatzbereit und Sie können mit den unten stehenden Übungen starten.

Wichtiger Hinweis 8.1

Falls die Installation nicht funktioniert, erstellen Sie zuerst ein virtuelles Umfeld (venv) in VS Code und installieren Sie pygame-ce darin:

  • Öffnen Sie ein Terminal-Fenster in VS Code (“Terminal” \(\rightarrow\) “New Terminal”).
  • Erstellen Sie ein virtuelles Environment: python3 -m venv venv Aktivieren Sie das venv: Windows: venv\Scripts\activate macOS: source venv/bin/activate
  • Führen Sie nun nochmals die obigen Schritte zur Installation von pygame-ce durch.
Beispiel 8.1: Minimale Game-Schleife

Folgender Code zeigt den minimalen Aufbau eines Pygame-Programms mit Fenster, Game-Schleife und Ereignisverarbeitung. Kopieren Sie den Code in eine Datei game_intro.py und führen Sie ihn in VS Code aus.

import pygame as pg

pg.init()  # Pygame initialisieren (starten)
HEIGHT = 600  # Höhe des Fensters
WIDTH = 800  # Breite des Fensters
WINDOW = (WIDTH, HEIGHT)  # Fenstergrösse (als Tuple gespeichert)
screen = pg.display.set_mode(WINDOW)  # Fenster erstellen
pg.display.set_caption("Mein erstes Game")  # Fenstertitel setzen
clock = pg.time.Clock()  # Clock für Zeitsteuerung erstellen

running = True  # Hauptschleife
while running:
    # --- Events ---
    for event in pg.event.get():
        if event.type == pg.QUIT:
            running = False

    # --- Update --- (Spielzustand aktualisieren)

    # --- render (zeichnen) ---
    screen.fill((30, 30, 40))
    pg.display.flip()

    # --- Zeitsteuerung ---
    clock.tick(60)  # 60 FPS

pg.quit()
Note

💡 Hinweis: Dieses Skript verwendet grafische Desktop-Bibliotheken (turtle / pygame). Führen Sie diesen Code in Ihrer lokalen Entwicklungsumgebung (z.B. VS Code) aus.

Ein schwarzes Fenster sollte erscheinen, das Sie mit dem Schliessen-Knopf beenden können.

Aufgabe 8.1: Fenster-Titel setzen

Setzen Sie einen passenden Fenstertitel, indem Sie folgende Zeile abändern:

Aufgabe (Challenge) 8.2

Laden Sie ein Fenster-Icon (.png oder .jpg): Laden Sie ein beliebiges Bild aus dem Internet herunter, speichern Sie es unter den Downloads und ziehen Sie es in Ihren Projektordner in VS Code (dort wo auch Ihre Python-Datei ist). Fügen Sie nun diese beiden Zeilen nach pg.display.set_mode(...) ein:

Wenn Ihr Ordner beispielsweise Informatik/ heisst und das Bild unter

Informatik/Game/icon.png

gespeichert ist, dann verwenden Sie

pg.image.load("Game/icon.png").

Bei Windows ändert sich nur das Fenster-Icon, bei macOS wird das Icon nur im Dock angezeigt.

import pygame as pg

pg.init()  # Pygame initialisieren (starten)
WINDOW = (800, 600)  # Fenstergrösse (als Tuple gespeichert)
screen = pg.display.set_mode(WINDOW)  # Fenster erstellen
icon = pg.image.load("Grundlagen_Info/00_Programmieren/Skript/Code/K08_Game/icon.png")
pg.display.set_caption("Mein erstes Game")  # Fenstertitel setzen
pg.display.set_icon(icon)
clock = pg.time.Clock()  # Clock für Zeitsteuerung erstellen

running = True  # Hauptschleife
while running:
    # --- Events ---
    for event in pg.event.get():
        if event.type == pg.QUIT:
            running = False

    # --- Update --- (Spielzustand aktualisieren)

    # --- render (zeichnen) ---
    screen.fill((30, 30, 40))
    pg.display.flip()

    # --- Zeitsteuerung ---
    clock.tick(60)  # 60 FPS

pg.quit()
Note

💡 Hinweis: Dieses Skript verwendet grafische Desktop-Bibliotheken (turtle / pygame). Führen Sie diesen Code in Ihrer lokalen Entwicklungsumgebung (z.B. VS Code) aus.

Aufgabe 8.3: Hintergrund zeichnen

Füllen Sie den Hintergrund pro Frame mit einer Farbe mittels screen.fill((R,G,B)). Experimentieren Sie mit zufällig generierten Farbtönen:

Die Farbe muss in der Hauptschleife, aber noch vor pg.display.flip(), gesetzt werden.

  • Was ändert sich, wenn Sie die drei Zeilen für die Farbwerte r, g, b vor die Hauptschleife setzen?
  • Schreiben Sie den Code so um, dass die Farbe nur alle 10 Frames geändert wird.
import pygame as pg
import random

pg.init()  # Pygame initialisieren (starten)
WINDOW = (800, 600)  # Fenstergrösse (als Tuple gespeichert)
screen = pg.display.set_mode(WINDOW)  # Fenster erstellen
pg.display.set_caption("Mein erstes Game")  # Fenstertitel setzen
clock = pg.time.Clock()  # Clock für Zeitsteuerung erstellen
icon = pg.image.load("Grundlagen_Info/00_Programmieren/Skript/Code/K09_Game/icon.png")
pg.display.set_icon(icon)

frame_counter = 0

running = True  # Hauptschleife
while running:
    # --- Events ---
    for event in pg.event.get():
        if event.type == pg.QUIT:
            running = False

    # --- render (zeichnen) ---
    if frame_counter % 10 == 0:
        # ändere die Farbe bei jedem 10ten Frame
        r = random.randint(0, 255)
        g = random.randint(0, 255)
        b = random.randint(0, 255)
        background_color = (r, g, b)
        screen.fill(background_color)  # Hintergrundfarbe setzen

    pg.display.flip()
    frame_counter += 1

    # --- Zeitsteuerung ---
    clock.tick(60)  # 10 FPS

pg.quit()
Note

💡 Hinweis: Dieses Skript verwendet grafische Desktop-Bibliotheken (turtle / pygame). Führen Sie diesen Code in Ihrer lokalen Entwicklungsumgebung (z.B. VS Code) aus.

Aufgabe 8.4: Geometrische Figuren

Zeichnen Sie geometrische Formen: Rechteck, Kreis, Linie. Nutzen Sie dafür pg.draw.rect, pg.draw.circle und pg.draw.line. Achten Sie darauf, nach dem Zeichnen pg.display.flip() aufzurufen. Verwenden Sie folgende Befehle (versuchen Sie beispielsweise, damit einen Apfel mit Stiel zu zeichnen):

Dabei bezeichnen die Parameter Folgendes:

  • screen: die Zeichenfläche
  • rect_color, circle_color, line_color: Farbe als RGB-Tuple, z.B. (255, 0, 0) für Rot
  • (rect_x, rect_y, rect_width, rect_height): Position \((x,y)\) und Grösse \((\text{Breite},\text{Höhe})\) als Tuple (alternativ kann auch ein pg.Rect-Objekt übergeben werden)
  • (circle_x, circle_y): Mittelpunkt des Kreises
  • circle_radius: Radius des Kreises
  • (start_x, start_y), (end_x, end_y): Start- und Endpunkt der Linie
  • line_width: Dicke der Linie in Pixeln (optional)
Aufgabe 8.5: Sonnenuntergang erstellen

Erzeugen Sie eine einfache Szene mit Himmel, Sonne und Meer. Tipp: Zeichnen Sie einen Farbverlauf am Himmel, indem Sie in einer Schleife schmale horizontale Rechtecke in leicht unterschiedlichen Farbtönen zeichnen. Die Sonne ist ein Kreis; das Meer ein Rechteck in der unteren Hälfte. Das Meer sollte ebenfalls einen Farbverlauf haben (von hellblau zu dunkelblau). Verwenden Sie folgenden Code als Vorlage:

Optional: Lassen Sie die Sonne langsam sinken (Animation über mehrere Frames).

Diese Musterlösung finden Sie in Längere Lösungen.

Bemerkung 8.1: Tuples vs. pg.Rect-Objekte

Zum reinen Zeichnen von statischen Formen (wie in Aufgabe 8.5: Sonnenuntergang erstellen) genügt die Angabe der Koordinaten und Dimensionen als einfaches Tuple (x, y, width, height).

Für bewegliche oder interaktive Spielelemente wird stattdessen ein pg.Rect-Objekt verwendet:

  • Es speichert Position sowie Grösse und erlaubt das direkte Modifizieren der Koordinaten über Attribute (z.B. player.x += speed).
  • Es stellt nützliche Methoden bereit, wie z.B. player.clamp_ip(...) zur Begrenzung auf den Bildschirmrand oder player.colliderect(item) zur Erkennung von Kollisionen (siehe Aufgabe 8.7: Kollisionen).
Aufgabe 8.6: Bewegungen (Tastatur)

Erstellen Sie ein Spieler-Rect (z.B. 50 x 50) und bewegen Sie es mit den Pfeiltasten. Nutzen Sie pg.key.get_pressed() und begrenzen Sie die Bewegung auf das Fenster (“Screen Bounds”). Verwenden Sie folgende Code-Vorlage:

Diese Musterlösung finden Sie in Längere Lösungen.

Aufgabe 8.7: Kollisionen

Legen Sie ein Rect als “Ziel/Item” an (z.B. kleiner Kreis oder Block). Prüfen Sie eine Kollision mit player.colliderect(item). Bei Kollision: Position des Items neu zufällig setzen und optional Punkte zählen.

Diese Musterlösung finden Sie in Längere Lösungen.

Aufgabe 8.8: Highscore anzeigen

Erstellen Sie eine Variable score, die bei jeder Kollision um 1 erhöht wird. Zeichnen Sie den aktuellen Punktestand mit font.render(...) oben links im Fenster. Verwenden Sie folgenden Code, um Text zu zeichnen:

Vergessen Sie nicht, die Variable score zu initialisieren, z.B. score = 0 vor der Game-Schleife. Ausserdem müssen Sie bei jeder Kollision den Wert von score erhöhen, z.B. score += 1.

Diese Musterlösung finden Sie in Längere Lösungen.

Aufgabe 8.9: Ereignisse (Keyboard / Maus)

Reagieren Sie auf Tastatur- und Maus-Events: Bei einem Mausklick liefert das Attribut event.pos die genauen Klick-Koordinaten (x, y). Speichern Sie diese in einer Liste mouse_positions, um an jeder Klickposition dauerhaft einen Kreis zu zeichnen. Bei Drücken der ESC-Taste soll sich das Spiel beenden.

Verwenden Sie folgenden Code:

Wichtiger Hinweis 8.2: Nur eine einzige Event-Schleife!

pg.event.get() leert bei jedem Aufruf die Ereignis-Warteschlange. Verwenden Sie daher in der gesamten Game-Schleife nur genau eine for event in pg.event.get():-Schleife. Fügen Sie neue Ereignisse (Tastatur, Maus, Schliessen) immer als weitere elif-Zweige in diese bestehende Schleife ein, anstatt den Block zu duplizieren.

Bemerkung 8.2: Nutzung von Maus-Koordinaten

Die Klickposition event.pos liefert ein Koordinaten-Tuple (x, y). Dieses kann vielseitig eingesetzt werden:

  • Objekt platzieren/bewegen: player.center = event.pos
  • Button anklicken: button_rect.collidepoint(event.pos)
  • Mauszeiger kontinuierlich folgen: player.center = pg.mouse.get_pos() (ohne Klick)

Diese Musterlösung finden Sie in Längere Lösungen.

Aufgabe 8.10: Medien: Bild und Sound

Laden Sie ein Bild (.png / .jpg) und zeichnen Sie es mit screen.blit(...). Skalieren oder rotieren Sie es optional. Laden Sie einen Sound (.wav / .ogg / .mp3) mit pg.mixer.Sound und spielen Sie ihn bei einer Kollision ab.

Für Bilder:

Die Funktion screen.blit(img, img_rect) kopiert die Pixel des Bildes (img) an die durch das Rechteck img_rect (oder ein Koordinaten-Tuple wie (x, y)) definierte Position.

Tipp: Sie können hier auch Ihren Code aus den vorherigen Aufgaben (Aufgabe 8.6: Bewegungen (Tastatur)) wiederverwenden: Falls Sie dort bereits ein player-Rechteck für Bewegung und Kollision angelegt haben, können Sie screen.blit(img, player) übergeben, um das bisherige schwarze Rechteck direkt durch das Bild zu ersetzen (oder player = img.get_rect(...) setzen).

Für Sounds:

Wichtiger Hinweis 8.3: Relative Pfade in VS Code

Ein relativer Pfad geht immer vom aktuellen Arbeitsverzeichnis (working directory) aus – im Gegensatz zu einem absoluten Pfad (z.B. C:\textbackslash Users\textbackslash Name\textbackslash ... oder /Users/name/...), der fest auf eine bestimmte Festplatte und einen bestimmten Benutzer bezogen ist und auf anderen Computern nicht funktioniert.

In VS Code ist das Arbeitsverzeichnis üblicherweise der Hauptordner, den Sie über “File” \(\rightarrow\) “Open Folder” geöffnet haben. Angenommen, Ihre Ordnerstruktur in VS Code sieht wie folgt aus:

Wenn in VS Code der Ordner MeinGame geöffnet ist und game.py ausgeführt wird, lautet der relative Pfad zu den Mediendateien "assets/player.jpeg" bzw. "assets/motor.wav". Befinden sich die Dateien direkt im selben Ordner wie game.py, lautet der Pfad einfach "player.jpeg".

Wichtig: Achten Sie darauf, in VS Code immer den eigentlichen Projektordner zu öffnen, damit Python die relativen Pfade korrekt auflösen kann.

Hinweis: Die Dateien player.jpeg und motor.wav finden Sie auf Moodle.

Optional: Skalieren Sie das Bild auf eine bestimmte Höhe, wobei das Seitenverhältnis beibehalten wird, indem Sie das Seitenverhältnis mit den Befehlen img.get_width() und img.get_height() berechnen.

Tipp: Viele kostenlose Soundeffekte finden Sie unter https://pixabay.com/sound-effects.

Diese Musterlösung finden Sie in Längere Lösungen.

Aufgabe (Challenge) 8.11: Bonus: Kleines Sammelspiel

Verbinden Sie die vorherigen Bausteine: Bewegen Sie den Spieler, sammeln Sie Items (Punkte zählen), spielen Sie dabei einen Sound ab und zeichnen Sie im Hintergrund Ihren Sonnenuntergang. Begrenzen Sie die Spielzeit auf 60 Sekunden und zeigen Sie die verbleibende Zeit im Fenstertitel an.

Dateiverarbeitung: Speicher- & Highscore-Systeme (File I/O)

Damit ein Game Spielstände, Einstellungen oder Highscores auch nach dem Beenden des Programms behält, müssen Daten dauerhaft auf dem Datenträger gespeichert werden. In Python erfolgt der Dateizugriff am sichersten mit dem with open(...)-Konstrukt, welches sicherstellt, dass die Datei nach dem Bearbeiten automatisch geschlossen wird.

Einfache Textdateien (.txt)

Mit dem Modus "r" (Read) werden Daten gelesen, mit "w" (Write) überschrieben und mit "a" (Append) angefügt.

Definition 8.2: Dateien lesen und schreiben
Aufgabe 8.12: Pong-Spiel mit dauerhaftem Highscore

Erstellen Sie das klassische Pong-Spiel mit zwei Paddles, einem Ball und dauerhaftem Highscore-Speicher in einer Datei highscore.txt.

Das folgende Video zeigt das fertige Pong-Spiel: https://youtu.be/vCoBgJlUg_cDie Paddles werden mit den Tasten W/S (links) und UP/DOWN (rechts) gesteuert. Der Ball bewegt sich automatisch und prallt von den Paddles und den oberen/unteren Bildschirmrändern ab. Zählen Sie die Punkte, wenn ein Spieler den Ball am Gegner vorbei spielt. Gehen Sie Schritt für Schritt vor:

  1. Rechtecke für Paddles und Ball zeichnen
  2. Mittellinie zeichnen
  3. Tastatur-Eingaben für Paddle-Bewegung (W/S und UP/DOWN)
  4. Ball von der Mitte aus in eine zufällige Richtung starten lassen
  5. Kollisionserkennung für Ball und Paddles, Richtung von Ball umkehren bei Kollision oder oberen/unteren Rändern
  6. Punkte zählen und aktuellen Spielstand im Fenster anzeigen
  7. Highscore laden & anzeigen: Laden Sie vor der Game-Schleife den Highscore aus der Datei highscore.txt. Fangen Sie mit try / except FileNotFoundError: den Fall ab, dass die Datei beim allerersten Start noch nicht existiert (in diesem Fall ist highscore = 0). Zeichnen Sie den Highscore unterhalb des aktuellen Spielstands.
  8. Highscore speichern: Sobald der Punktestand eines Spielers den bisherigen Highscore übertrifft, aktualisieren Sie highscore und schreiben den neuen Wert mit with open("highscore.txt", "w") as file: direkt in die Datei.
  9. Soundeffekte bei Kollisionen und Punkten

Diese Musterlösung finden Sie in Längere Lösungen.

Wichtiger Hinweis 8.4: Hinweise zu Pong

Die folgende Sammlung von Hinweisen zeigt kleine Bausteine, die im Pong-Beispiel verwendet werden und Ihnen beim Verständnis oder bei eigenen Varianten helfen.

  • Zufällige Start-Richtung für den Ball:
  • Rect-Ränder nutzen und Kollisionen “sauber” auflösen:
  • Abprall oben/unten (Wände):
  • Trefferwinkel variieren: Je weiter oben/unten am Paddle getroffen wird, desto mehr vertikale Geschwindigkeit.

  • Ball nach Punkt neu zentrieren (als Funktion):
  • Nützliche Rect-Eigenschaften: left, right, top, bottom, center, centery helfen bei Ausrichtung und Kollisionen.
Aufgabe (Challenge) 8.13: Pong-Bonus

Fügen Sie dem Pong-Spiel folgende Features hinzu:

  • Soundeffekte bei Paddle-Kollision und Punktgewinn
  • Startbildschirm mit Anweisungen
  • Game-Over-Bildschirm nach einer bestimmten Punktzahl (z.B. 10 Punkte)
  • Paddle-Geschwindigkeit erhöhen, wenn der Ball getroffen wird
  • Hintergrundmusik während des Spiels
Aufgabe 8.14: Videoaufnahme des Spiels

Nehmen Sie ein kurzes Video (ca. 1-2 Minuten) auf, das das Gameplay Ihres Spiels zeigt. Laden Sie das Video auf eine Plattform (z.B. YouTube, Vimeo) hoch und fügen Sie den Link auf Moodle ein. Für die Bildschirmaufnahme können Sie folgende Tools verwenden:

  • Windows: Verwenden Sie die Bildschirmaufnahme-Funktion von PowerPoint (unter “Einfügen” \(\rightarrow\) “Bildschirmaufnahme”) oder die Xbox Game Bar (+ G), beenden Sie die Aufnahme mit Cmd + + R
  • macOS: Verwenden Sie die integrierte Bildschirmaufnahme (Shift + Cmd + 5), beenden Sie die Aufnahme mit Cmd + Strg + Esc

Strukturierte Tabellen mit CSV (.csv)

Möchte man eine Rangliste (Leaderboard) mit mehreren Spielernamen und Punkteständen speichern, eignet sich das CSV-Format (Comma-Separated Values). Python bietet dafür das eingebaute Modul csv.

Definition 8.3: CSV-Dateien lesen und schreiben
Aufgabe 8.15: Leaderboard-Tabelle mit CSV

Implementieren Sie eine Funktion load_leaderboard(), welche die CSV-Datei highscores.csv einliest, die Einträge nach Punktestand absteigend sortiert und die Top-3-Spieler auf der Konsole oder im Pygame-Fenster ausgibt.

Objektorientierte Spielobjekte mit Pygame-Sprites

In komplexeren Spielen möchten wir häufig viele gleichartige Spielobjekte gleichzeitig verwalten – beispielsweise Dutzende Münzen, Sterne, Hindernisse oder Laser-Schüsse. Bisher mussten wir Positionen, Bewegungen und Kollisionen für jedes Objekt einzeln mit separaten Variablen, Schleifen und Listen steuern.

Pygame stellt dafür mit pg.sprite.Sprite und pg.sprite.Group ein elegantes, objektorientiertes System bereit, das direkt an das Klassen-Konzept aus Abschnitt anknüpft:

  • Sprite (pg.sprite.Sprite): Eine Klasse für ein einzelnes sichtbares Spielelement. Jedes Sprite besitzt zwingend zwei Attribute:

  • self.image: Die Bildfläche (pg.Surface) des Objekts.

  • self.rect: Das pg.Rect-Objekt, welches Position und Hitbox definiert.

Zusätzlich wird meist eine Methode update(self) implementiert, um Position und Verhalten pro Frame zu aktualisieren. - Sprite-Gruppe (pg.sprite.Group): Verwaltet mehrere Sprites gesammelt.

  • gruppe.update() ruft automatisch die update()-Methode aller enthaltenen Sprites auf.

  • gruppe.draw(screen) zeichnet alle Sprites der Gruppe mit einem einzigen Befehl an deren jeweiliger rect-Position.

  • Effiziente Kollisionserkennung: Funktionen wie pg.sprite.spritecollide(spieler, gruppe, dokill) prüfen Kollisionen zwischen einem einzelnen Sprite und einer ganzen Gruppe in einer einzigen Code-Zeile. Ist dokill=True, werden alle kollidierenden Sprites sofort automatisch aus der Gruppe gelöscht.

Beispiel 8.2: Sprites und Sprite-Gruppen

Folgender Code zeigt das Einsammeln mehrerer Münzen-Sprites mit automatischer Kollisionserkennung (spritecollide). Kopieren Sie den Code in eine Datei game_sprites.py und führen Sie ihn aus:

import random
import pygame as pg

pg.init()
WIDTH = 800
HEIGHT = 600
screen = pg.display.set_mode((WIDTH, HEIGHT))
pg.display.set_caption("Sprites und Sprite-Gruppen")
clock = pg.time.Clock()

class Spieler(pg.sprite.Sprite):
    def __init__(self):
        super().__init__()
        # Grafik fuer den Spieler (Rechteck/Surface)
        self.image = pg.Surface((40, 40))
        self.image.fill((0, 150, 255))
        self.rect = self.image.get_rect(center=(WIDTH // 2, HEIGHT // 2))
        self.speed = 5

    def update(self):
        # Steuerung ueber Pfeiltasten
        keys = pg.key.get_pressed()
        if keys[pg.K_LEFT]:
            self.rect.x -= self.speed
        if keys[pg.K_RIGHT]:
            self.rect.x += self.speed
        if keys[pg.K_UP]:
            self.rect.y -= self.speed
        if keys[pg.K_DOWN]:
            self.rect.y += self.speed
        # Im Fenster halten
        self.rect.clamp_ip(screen.get_rect())

class Muenze(pg.sprite.Sprite):
    def __init__(self, x, y):
        super().__init__()
        # Grafik fuer die Muenze
        self.image = pg.Surface((24, 24), pg.SRCALPHA)
        pg.draw.circle(self.image, (255, 215, 0), (12, 12), 12)
        self.rect = self.image.get_rect(center=(x, y))

# Sprite-Gruppen erstellen
spieler = Spieler()
spieler_gruppe = pg.sprite.GroupSingle(spieler)

muenzen_gruppe = pg.sprite.Group()
for _ in range(15):
    x = random.randint(30, WIDTH - 30)
    y = random.randint(30, HEIGHT - 30)
    muenzen_gruppe.add(Muenze(x, y))

font = pg.font.Font(None, 36)
score = 0
running = True

while running:
    for event in pg.event.get():
        if event.type == pg.QUIT:
            running = False

    # 1. Update: Alle Sprites in Gruppen aktualisieren
    spieler_gruppe.update()

    # 2. Kollisionspruefung:
    # dokill=True entfernt getroffene Muenzen automatisch aus muenzen_gruppe
    getroffene_muenzen = pg.sprite.spritecollide(spieler, muenzen_gruppe, True)
    if getroffene_muenzen:
        score += len(getroffene_muenzen)

    # 3. Render (Zeichnen)
    screen.fill((30, 30, 40))

    # Alle Sprites der Gruppen mit nur einem Befehl zeichnen
    muenzen_gruppe.draw(screen)
    spieler_gruppe.draw(screen)

    # Score anzeigen
    score_text = font.render(f"Score: {score}", True, (255, 255, 255))
    screen.blit(score_text, (10, 10))

    pg.display.flip()
    clock.tick(60)

pg.quit()
Note

💡 Hinweis: Dieses Skript verwendet grafische Desktop-Bibliotheken (turtle / pygame). Führen Sie diesen Code in Ihrer lokalen Entwicklungsumgebung (z.B. VS Code) aus.

Aufgabe 8.16: Sammelobjekte mit Sprites verwalten

Bauen Sie ein eigenes Sammelspiel oder erweitern Sie Ihr bestehendes Spiel mit Pygame-Sprites:

  1. Erstellen Sie eine eigene Sprite-Klasse Muenze(pg.sprite.Sprite) (oder Stern / Hindernis). Definieren Sie im Konstruktor __init__() die Attribute self.image (z.B. ein gelber Kreis oder ein geladenes Bild) und self.rect an einer zufälligen Position im Fenster.
  2. Erstellen Sie eine Sprite-Gruppe muenzen_gruppe = pg.sprite.Group() und fügen Sie in einer Schleife 15 bis 20 Münzen mit muenzen_gruppe.add(...) hinzu.
  3. Erstellen Sie eine Spieler-Klasse Spieler(pg.sprite.Sprite), deren update()-Methode die Tastatureingaben (pg.key.get_pressed()) verarbeitet.
  4. Aktualisieren Sie in der Game-Schleife alle Sprites mit update(), prüfen Sie das Einsammeln mit pg.sprite.spritecollide(spieler, muenzen_gruppe, True) und zeichnen Sie alle Elemente mit draw(screen).

Diese Musterlösung finden Sie in Längere Lösungen.

Game-Auftrag

Thema

Im Folgenden wählen Sie in Zweier-Gruppen (bei ungrader Anzahl Personen eine Dreiergruppe möglich) ein Game-Thema aus und entwickeln ein 2D-Game mit pygame-ce. Mögliche Themen sind:

  • Sammelspiel: Bewegen Sie eine Spielfigur (z.B. Auto, Raumschiff) und sammeln Sie Items ein (z.B. Treibstoff, Sterne). Jedes eingesammelte Item gibt Punkte.
  • Endlos-Runner: Steuern Sie eine Spielfigur (z.B. Läufer, Auto) und weichen Sie Hindernissen aus (z.B. Mauern, andere Autos). Jedes überstandene Hindernis gibt Punkte.
  • Fangspiel: Steuern Sie eine Spielfigur (z.B. Katze, Netz) und fangen Sie herumspringende Objekte (z.B. Mäuse, Schmetterlinge). Jedes gefangene Objekt gibt Punkte.
  • Shooter (z.B. Space Invaders): Steuern Sie ein Raumschiff und schiessen Sie auf herannahende Gegner. Jeder abgeschossene Gegner gibt Punkte.

Weitere Themen können in Absprache mit der Lehrperson gewählt werden. Wichtig ist, dass das Spiel die geforderten Python-Konzepte sinnvoll einsetzt (siehe Anforderungen) und die Komplexität angemessen ist (nicht zu einfach, aber auch nicht zu komplex). Das Spiel sollte mehr als nur 10 Sekunden lang Spass machen und nicht zu einfach zu spielen sein. Es sollte eine Herausforderung darstellen, die Spieler motiviert, immer wieder zu spielen und ihre Punktzahl zu verbessern.

Anforderungen

Ihr Projekt sollte folgende, essentielle Python-Konzepte sinnvoll einsetzen

  • Verwendung von pygame-ce für Fenster, Game-Schleife, Ereignisse, Zeichnen, Kollisionen und Medien (Bilder, Sound), Steuerung mit Tastatur und/oder Maus
  • Verwendung physikalischer Formeln für Bewegungen (z.B. Geschwindigkeit, Beschleunigung, Schwerkraft)
  • Verwendung von Variablen zur Speicherung von Spielzuständen (z.B. Spielerposition, Punktestand, verbleibende Zeit)
  • Verwendung von Funktionen zur Strukturierung des Codes (z.B. draw_player(), update_game(), handle_input()), mit sowie ohne return-Wert
  • Verwendung von logischen Ausdrücken und Schleifen zur Steuerung des Spielablaufs (z.B. Kollisionserkennung, Punkte zählen, Spielzeit)
  • Verwendung von Datenstrukturen (Listen, Dictionaries) zur Verwaltung von mehreren Spielobjekten (z.B. Liste von Items, Liste von Hindernissen)
  • Verwendung von Klassen zur Modellierung von Spielobjekten (z.B. class Player, class Item, class Obstacle)
  • Verwendung mehrerer Python-Unterdateien zur besseren Strukturierung des Codes (z.B. game.py, player.py, item.py)

Ihr Game muss Kommentare enthalten, die den Code erklären und die Struktur des Programms verdeutlichen. Verwenden Sie sowohl einzeilige Kommentare (mit #) als auch mehrzeilige Kommentare (mit """...""") für Funktionen und Klassen. Schauen Sie sich die detaillierten Bewertungskriterien auf Moodle an, um sicherzustellen, dass Ihr Code den Anforderungen entspricht.

Bonus

  • Python-spezifisch:

  • Verwendung von try/except zur Behandlung von Fehlern (z.B. Laden von Medien, Division durch Null)

  • Verwendung von with-Anweisung zum sicheren Öffnen und Schliessen von Dateien (z.B. Highscore speichern)

  • Wenn Sie ein Punktesystem mit Highscore (gespeichert in einer Datei) implementieren.

  • Game-spezifisch:

  • Wenn Sie viele Power-Ups (z.B. temporäre Unverwundbarkeit, Doppelte Punkte) einbauen.

  • Wenn Sie viele eigens erstellte Hintergrundmusik und/oder Soundeffekte (z.B. bei Kollision, Item-Sammlung) einbauen.

  • Wenn Sie das Spiel mit einem Gamepad/Controller steuern können.

  • Wenn Sie einen Online-Multiplayer-Modus (z.B. über LAN) implementieren.

  • Weitere Ideen für Bonus:

  • Wenn Sie das Spiel mit einem Level-Editor (zum Erstellen eigener Level) erweitern können.

  • Wenn Sie das Spiel mit einem Story-Modus (mit Handlung und Charakteren) erweitern können.

  • Wenn Sie das Spiel mit einem besonders raffinierten Achievements-System (Erfolge) erweitern können.

  • Wenn Sie das Spiel mit einem Debug-Modus (zum Testen und Entwickeln) erweitern können.

Bewertung

Projektbewertung

Die Bewertung Ihres Projekts erfolgt anhand der auf Moodle aufgelisteten Kriterien.

Gruppen-Besprechung des Spiels

Im Anschluss an Ihre Abgabe findet eine Besprechung Ihres Spiels in Gruppen mit der Lehrperson statt (ca. 5 Minuten pro Person). Dabei werden Ihnen Fragen zu Ihrem Spiel gestellt, um Ihr Verständnis und Ihre Reflexion zu überprüfen. Die Besprechung resultiert in einer individuellen Verständnis-Punktzahl von bis zu 16 Punkten, welche mit Ihrer Endnote multipliziert wird:

Ihre Endnote wird wie folgt berechnet:

\[ \text{Endnote} = \text{Projekt-Note} + \Delta_{\text{Verständnis}} \]

Wobei:

  • Projekt-Note die Note ist, die Sie basierend auf der Bewertung Ihres Projekts erhalten (0–100 Punkte, umgerechnet in Notenstufen)

  • Verständnis-Delta ein Bonus oder Malus ist, abhängig von Ihrer Verständnis-Punktzahl (0–16 Punkte), wie folgt:

  • 14 Punkte: \(\Delta = 0\) (keine Veränderung)

  • 15 Punkte: \(\Delta = +0.1\)

  • 16 Punkte: \(\Delta = +0.2\)

  • Unter 14 Punkte: lineare Verschlechterung, maximal \(\Delta = -1\) Note

Wichtiger Hinweis 8.5: KI-Tools

Es wird von Ihnen erwartet, dass Sie sich aktiv an der Entwicklung des Spiels beteiligen und die geforderten Python-Konzepte verstehen und anwenden können. ChatGPT oder andere KI-Tools dürfen Sie verwenden, solange Sie Ihren gesamten Code verstehen und erklären können. Wie Sie im obigen Benotungsschema erkennen können, kann Ihre Note durch mangelndes Verständnis stark beeinträchtigt werden. Falls Sie ChatGPT oder andere KI-Tools verwenden, müssen Sie dies im Code klar dokumentieren (z.B. mit Kommentaren oder in einer Begleit-Dokumentation). Jede Zeile Code muss durch Sie erklärbar sein!

Längere Lösungen

↩︎ Zurück zu Aufgabe 8.5: Sonnenuntergang erstellen

# File: game_intro_sunset.py
import pygame as pg

pg.init()  # Pygame initialisieren (starten)
HEIGHT = 600  # Höhe des Fensters
WIDTH = 800  # Breite des Fensters
WINDOW = (WIDTH, HEIGHT)  # Fenstergrösse (als Tuple gespeichert)
screen = pg.display.set_mode(WINDOW)  # Fenster erstellen
pg.display.set_caption("Sonnenuntergang")  # Fenstertitel setzen
icon = pg.image.load("Grundlagen_Info/00_Programmieren/Skript/Code/K08_Game/icon.png")
pg.display.set_icon(icon)
clock = pg.time.Clock()  # Clock für Zeitsteuerung erstellen

SUN_CENTER_X = WIDTH // 2 # x-Koorinate Kreismittelpunkt Sonne
sun_center_y = HEIGHT // 3 # y-Koorinate Kreismittelpunkt Sonne
SUN_RADIUS = 60 # Radius der Sonne

running = True  # Hauptschleife
while running:
    # --- Events ---
    for event in pg.event.get():
        if event.type == pg.QUIT:
            running = False

    # --- Update & Render ---
    # Himmel (einfacher Gradient)
    for y in range(0, HEIGHT // 2):
        red = 100 + int(155 * (y / (HEIGHT // 2)))  # 100 ... 255
        pg.draw.rect(screen, (red, 120, 180), (0, y, WIDTH, 1))

    # Sonne sinkt langsam (maximal bis zum Meer)
    # nicht mehr sichtbare Sonne soll nicht mehr berechnet werden
    if sun_center_y < (HEIGHT // 2 + SUN_RADIUS):
        sun_center_y += 0.5  # y-Position der Sonne verändern
        # Sonne
        pg.draw.circle(screen, (255, 200, 0), (SUN_CENTER_X, sun_center_y), SUN_RADIUS)

    # Meer
    pg.draw.rect(screen, (20, 80, 160), (0, HEIGHT // 2, WIDTH, HEIGHT // 2))

    # Bildschirm aktualisieren
    pg.display.flip()

    # --- Zeitsteuerung ---
    clock.tick(60)  # 60 FPS

pg.quit()

# TODO AUFGABEN: 8.6-8.8
Note

💡 Hinweis: Dieses Skript verwendet grafische Desktop-Bibliotheken (turtle / pygame). Führen Sie diesen Code in Ihrer lokalen Entwicklungsumgebung (z.B. VS Code) aus.

↩︎ Zurück zu Aufgabe 8.6: Bewegungen (Tastatur)

# File: game_intro_player_rect.py
import pygame as pg

pg.init()  # Pygame initialisieren (starten)
WINDOW = (800, 600)  # Fenstergrösse (als Tuple gespeichert)
screen = pg.display.set_mode(WINDOW)  # Fenster erstellen
icon = pg.image.load("Grundlagen_Info/00_Programmieren/Skript/Code/K08_Game/icon.png")
pg.display.set_caption("Mein erstes Game")  # Fenstertitel setzen
pg.display.set_icon(icon)
background_color = (50, 80, 120)
clock = pg.time.Clock()  # Clock für Zeitsteuerung erstellen

# Spieler-Rect und Geschwindigkeit
player = pg.Rect(100, 100, 50, 50)
speed = 5

running = True  # Hauptschleife
while running:
    # --- Events ---
    for event in pg.event.get():
        if event.type == pg.QUIT:
            running = False

    # --- Update (Input / Bewegung) ---
    keys = pg.key.get_pressed()
    if keys[pg.K_LEFT]:
        player.x -= speed
    if keys[pg.K_RIGHT]:
        player.x += speed
    if keys[pg.K_UP]:
        player.y -= speed
    if keys[pg.K_DOWN]:
        player.y += speed
    player.clamp_ip(screen.get_rect())  # im Fenster halten

    # --- render (zeichnen) ---
    screen.fill(background_color)  # Hintergrundfarbe setzen
    pg.draw.rect(screen, (255, 255, 255), player)  # Spieler zeichnen

    pg.display.flip()

    # --- Zeitsteuerung ---
    clock.tick(60)  # 60 FPS

pg.quit()
Note

💡 Hinweis: Dieses Skript verwendet grafische Desktop-Bibliotheken (turtle / pygame). Führen Sie diesen Code in Ihrer lokalen Entwicklungsumgebung (z.B. VS Code) aus.

↩︎ Zurück zu Aufgabe 8.7: Kollisionen

# File: game_intro_collision.py
import pygame as pg
import random

pg.init()
WIDTH, HEIGHT = 800, 600
screen = pg.display.set_mode((WIDTH, HEIGHT))
pg.display.set_caption("Kollisions-Game")
icon = pg.image.load("Grundlagen_Info/00_Programmieren/Skript/Code/K08_Game/icon.png")
pg.display.set_icon(icon)

background_color = (50, 80, 120)

player = pg.Rect(WIDTH // 2 - 20, HEIGHT // 2 - 20, 40, 40)
item = pg.Rect(400, 300, 30, 30)
speed = 5

clock = pg.time.Clock()

running = True
while running:
    for event in pg.event.get():
        if event.type == pg.QUIT:
            running = False

    keys = pg.key.get_pressed()
    if keys[pg.K_RIGHT]:
        player.x += speed
    if keys[pg.K_LEFT]:
        player.x -= speed
    if keys[pg.K_DOWN]:
        player.y += speed
    if keys[pg.K_UP]:
        player.y -= speed

    player.clamp_ip(screen.get_rect())

    if player.colliderect(item):
        item.topleft = (
            random.randint(0, WIDTH - item.width),
            random.randint(0, HEIGHT - item.height),
        )

    screen.fill(background_color)
    pg.draw.rect(screen, (240, 220, 90), player)
    pg.draw.ellipse(screen, (200, 60, 60), item)

    pg.display.flip()
    clock.tick(60)

pg.quit()
Note

💡 Hinweis: Dieses Skript verwendet grafische Desktop-Bibliotheken (turtle / pygame). Führen Sie diesen Code in Ihrer lokalen Entwicklungsumgebung (z.B. VS Code) aus.

↩︎ Zurück zu Aufgabe 8.8: Highscore anzeigen

# File: game_intro_highscore.py
import pygame as pg
import random

pg.init()
WIDTH, HEIGHT = 800, 600
screen = pg.display.set_mode((WIDTH, HEIGHT))
pg.display.set_caption("Highscore-Game")
icon = pg.image.load("Grundlagen_Info/00_Programmieren/Skript/Code/K08_Game/icon.png")
pg.display.set_icon(icon)

background_color = (50, 80, 120)

player = pg.Rect(WIDTH // 2 - 20, HEIGHT // 2 - 20, 40, 40)
item = pg.Rect(400, 300, 30, 30)
speed = 5
score = 0
font = pg.font.Font(None, 36)

clock = pg.time.Clock()

running = True
while running:
    for event in pg.event.get():
        if event.type == pg.QUIT:
            running = False

    keys = pg.key.get_pressed()
    if keys[pg.K_RIGHT]:
        player.x += speed
    if keys[pg.K_LEFT]:
        player.x -= speed
    if keys[pg.K_DOWN]:
        player.y += speed
    if keys[pg.K_UP]:
        player.y -= speed

    player.clamp_ip(screen.get_rect())

    if player.colliderect(item):
        score += 1
        item.topleft = (
            random.randint(0, WIDTH - item.width),
            random.randint(0, HEIGHT - item.height),
        )

    screen.fill(background_color)
    pg.draw.rect(screen, (240, 220, 90), player)
    pg.draw.ellipse(screen, (200, 60, 60), item)
    score_surf = font.render(f"Punkte: {score}", True, (255, 255, 255))
    screen.blit(score_surf, (10, 10))

    pg.display.flip()
    clock.tick(60)

pg.quit()
Note

💡 Hinweis: Dieses Skript verwendet grafische Desktop-Bibliotheken (turtle / pygame). Führen Sie diesen Code in Ihrer lokalen Entwicklungsumgebung (z.B. VS Code) aus.

↩︎ Zurück zu Aufgabe 8.9: Ereignisse (Keyboard / Maus)

# File: game_intro_events.py
import pygame as pg

pg.init()  # Pygame initialisieren (starten)
WINDOW = (800, 600)  # Fenstergrösse (als Tuple gespeichert)
screen = pg.display.set_mode(WINDOW)  # Fenster erstellen
pg.display.set_caption("Mein erstes Game")  # Fenstertitel setzen
icon = pg.image.load("Grundlagen_Info/00_Programmieren/Skript/Code/K08_Game/icon.png")
pg.display.set_icon(icon)

background_color = (50, 80, 120)

clock = pg.time.Clock()  # Clock für Zeitsteuerung erstellen

# Liste für Kreise
mouse_positions = []  # Liste für Maus-Klick-Positionen

running = True  # Hauptschleife
while running:
    # --- Events ---
    for event in pg.event.get():
        if event.type == pg.QUIT:
            running = False
        elif event.type == pg.KEYDOWN and event.key == pg.K_ESCAPE:
            running = False
        elif event.type == pg.MOUSEBUTTONDOWN:
            x, y = event.pos
            mouse_positions.append((x, y))

    # --- render (zeichnen) ---
    screen.fill(background_color)  # Hintergrundfarbe setzen

    # Kreise zeichnen
    # In der Game-Loop, im Render-Abschnitt:
    for pos in mouse_positions:
        pg.draw.circle(screen, (255, 100, 100), pos, 10)

    pg.display.flip()

    # --- Zeitsteuerung ---
    clock.tick(60)  # 60 FPS

pg.quit()
Note

💡 Hinweis: Dieses Skript verwendet grafische Desktop-Bibliotheken (turtle / pygame). Führen Sie diesen Code in Ihrer lokalen Entwicklungsumgebung (z.B. VS Code) aus.

↩︎ Zurück zu Aufgabe 8.10: Medien: Bild und Sound

# File: game_intro_media.py
import pygame as pg
from pathlib import Path

pg.init()  # Pygame initialisieren (starten)
WIDTH = 800
HEIGHT = 600
WINDOW = (WIDTH, HEIGHT)  # Fenstergrösse (als Tuple gespeichert)
screen = pg.display.set_mode(WINDOW)  # Fenster erstellen

base_dir = Path(__file__).parent
icon = pg.image.load(base_dir / "icon.png")
pg.display.set_caption("Mein erstes Game")  # Fenstertitel setzen
pg.display.set_icon(icon)

# Medien laden (Bild und Sound)
assets = base_dir / "assets"
img = pg.image.load(assets / "player.jpeg")
# Skalieren auf bestimmte Höhe, Seitenverhältnis beibehalten
desired_height = 100
aspect_ratio = img.get_width() / img.get_height()
new_width = int(desired_height * aspect_ratio)
img = pg.transform.scale(img, (new_width, desired_height))

# Spieler-Rect für Position/Kollision
player = img.get_rect(center=(WIDTH // 2, HEIGHT // 2))

# Einfaches Item als farbiges Quadrat
item_surf = pg.Surface((40, 40), pg.SRCALPHA)
item_surf.fill((255, 200, 0))
item = item_surf.get_rect(center=(WIDTH // 3, HEIGHT // 2))

# Sound laden
try:
    pg.mixer.init()
    motor = pg.mixer.Sound(assets / "motor.mp3")
except Exception:
    motor = None  # falls kein Audio verfügbar ist

background_color = (50, 80, 120)
clock = pg.time.Clock()  # Clock für Zeitsteuerung erstellen

running = True  #  Hauptschleife
collided = False
speed = 5

while running:
    # --- Events ---
    for event in pg.event.get():
        if event.type == pg.QUIT:
            running = False

    # --- Update ---
    keys = pg.key.get_pressed()
    if keys[pg.K_LEFT]:
        player.x -= speed
    if keys[pg.K_RIGHT]:
        player.x += speed
    if keys[pg.K_UP]:
        player.y -= speed
    if keys[pg.K_DOWN]:
        player.y += speed

    # Im Fenster halten
    player.clamp_ip(screen.get_rect())

    # Kollision + Sound
    if player.colliderect(item):
        if not collided and motor:
            motor.play()
        collided = True
    else:
        collided = False

    # --- render (zeichnen) ---
    screen.fill(background_color)  # Hintergrundfarbe setzen

    # Item und Spieler zeichnen
    screen.blit(item_surf, item)
    # optional: Rotation -> pg.transform.rotate(img, angle)
    screen.blit(img, player)

    pg.display.flip()

    # --- Zeitsteuerung ---
    clock.tick(60)  # 60 FPS

pg.quit()
Note

💡 Hinweis: Dieses Skript verwendet grafische Desktop-Bibliotheken (turtle / pygame). Führen Sie diesen Code in Ihrer lokalen Entwicklungsumgebung (z.B. VS Code) aus.

↩︎ Zurück zu Aufgabe 8.16: Sammelobjekte mit Sprites verwalten

# File: game_sprites.py
import random
import pygame as pg

pg.init()
WIDTH = 800
HEIGHT = 600
screen = pg.display.set_mode((WIDTH, HEIGHT))
pg.display.set_caption("Sprites und Sprite-Gruppen")
clock = pg.time.Clock()

class Spieler(pg.sprite.Sprite):
    def __init__(self):
        super().__init__()
        # Grafik fuer den Spieler (Rechteck/Surface)
        self.image = pg.Surface((40, 40))
        self.image.fill((0, 150, 255))
        self.rect = self.image.get_rect(center=(WIDTH // 2, HEIGHT // 2))
        self.speed = 5

    def update(self):
        # Steuerung ueber Pfeiltasten
        keys = pg.key.get_pressed()
        if keys[pg.K_LEFT]:
            self.rect.x -= self.speed
        if keys[pg.K_RIGHT]:
            self.rect.x += self.speed
        if keys[pg.K_UP]:
            self.rect.y -= self.speed
        if keys[pg.K_DOWN]:
            self.rect.y += self.speed
        # Im Fenster halten
        self.rect.clamp_ip(screen.get_rect())

class Muenze(pg.sprite.Sprite):
    def __init__(self, x, y):
        super().__init__()
        # Grafik fuer die Muenze
        self.image = pg.Surface((24, 24), pg.SRCALPHA)
        pg.draw.circle(self.image, (255, 215, 0), (12, 12), 12)
        self.rect = self.image.get_rect(center=(x, y))

# Sprite-Gruppen erstellen
spieler = Spieler()
spieler_gruppe = pg.sprite.GroupSingle(spieler)

muenzen_gruppe = pg.sprite.Group()
for _ in range(15):
    x = random.randint(30, WIDTH - 30)
    y = random.randint(30, HEIGHT - 30)
    muenzen_gruppe.add(Muenze(x, y))

font = pg.font.Font(None, 36)
score = 0
running = True

while running:
    for event in pg.event.get():
        if event.type == pg.QUIT:
            running = False

    # 1. Update: Alle Sprites in Gruppen aktualisieren
    spieler_gruppe.update()

    # 2. Kollisionspruefung:
    # dokill=True entfernt getroffene Muenzen automatisch aus muenzen_gruppe
    getroffene_muenzen = pg.sprite.spritecollide(spieler, muenzen_gruppe, True)
    if getroffene_muenzen:
        score += len(getroffene_muenzen)

    # 3. Render (Zeichnen)
    screen.fill((30, 30, 40))

    # Alle Sprites der Gruppen mit nur einem Befehl zeichnen
    muenzen_gruppe.draw(screen)
    spieler_gruppe.draw(screen)

    # Score anzeigen
    score_text = font.render(f"Score: {score}", True, (255, 255, 255))
    screen.blit(score_text, (10, 10))

    pg.display.flip()
    clock.tick(60)

pg.quit()
Note

💡 Hinweis: Dieses Skript verwendet grafische Desktop-Bibliotheken (turtle / pygame). Führen Sie diesen Code in Ihrer lokalen Entwicklungsumgebung (z.B. VS Code) aus.

↩︎ Zurück zu Aufgabe 8.12: Pong-Spiel mit dauerhaftem Highscore

# File: game_highscore_txt.py
import pygame as pg
import random

pg.init()
WIDTH, HEIGHT = 800, 600
screen = pg.display.set_mode((WIDTH, HEIGHT))
pg.display.set_caption("Pong mit Highscore")
clock = pg.time.Clock()
font = pg.font.Font(None, 36)

# Schläger und Ball
paddle_w, paddle_h = 15, 100
left_paddle = pg.Rect(30, HEIGHT // 2 - paddle_h // 2, paddle_w, paddle_h)
right_paddle = pg.Rect(
    WIDTH - 30 - paddle_w, HEIGHT // 2 - paddle_h // 2, paddle_w, paddle_h
)
ball = pg.Rect(WIDTH // 2 - 10, HEIGHT // 2 - 10, 20, 20)

paddle_speed = 6
ball_speed_x = 5
ball_speed_y = 5

left_score = 0
right_score = 0

# 1. Highscore beim Start aus Datei laden
try:
    with open("highscore.txt", "r") as file:
        highscore = int(file.read())
except FileNotFoundError:
    highscore = 0

running = True
while running:
    for event in pg.event.get():
        if event.type == pg.QUIT:
            running = False

    # Schläger steuern
    keys = pg.key.get_pressed()
    if keys[pg.K_w] and left_paddle.top > 0:
        left_paddle.y -= paddle_speed
    if keys[pg.K_s] and left_paddle.bottom < HEIGHT:
        left_paddle.y += paddle_speed
    if keys[pg.K_UP] and right_paddle.top > 0:
        right_paddle.y -= paddle_speed
    if keys[pg.K_DOWN] and right_paddle.bottom < HEIGHT:
        right_paddle.y += paddle_speed

    # Ball bewegen
    ball.x += ball_speed_x
    ball.y += ball_speed_y

    # Abprallen an oberem/unterem Rand
    if ball.top <= 0 or ball.bottom >= HEIGHT:
        ball_speed_y *= -1

    # Abprallen an Schlägern
    if ball.colliderect(left_paddle) or ball.colliderect(right_paddle):
        ball_speed_x *= -1

    # Punkte zählen und Ball zurücksetzen
    if ball.left <= 0:
        right_score += 1
        ball.center = (WIDTH // 2, HEIGHT // 2)
        ball_speed_x = 5
    elif ball.right >= WIDTH:
        left_score += 1
        ball.center = (WIDTH // 2, HEIGHT // 2)
        ball_speed_x = -5

    # 2. Highscore prüfen und bei neuem Rekord direkt in Datei speichern
    current_max = max(left_score, right_score)
    if current_max > highscore:
        highscore = current_max
        with open("highscore.txt", "w") as file:
            file.write(str(highscore))

    # Zeichnen
    screen.fill((30, 30, 40))
    pg.draw.rect(screen, (255, 255, 255), left_paddle)
    pg.draw.rect(screen, (255, 255, 255), right_paddle)
    pg.draw.ellipse(screen, (255, 255, 255), ball)
    pg.draw.aaline(
        screen, (100, 100, 100), (WIDTH // 2, 0), (WIDTH // 2, HEIGHT)
    )

    # 3. Spielstand und Highscore anzeigen
    score_surf = font.render(
        f"{left_score} : {right_score}", True, (255, 255, 255)
    )
    highscore_surf = font.render(
        f"Highscore: {highscore}", True, (200, 200, 100)
    )
    screen.blit(score_surf, (WIDTH // 2 - score_surf.get_width() // 2, 20))
    screen.blit(
        highscore_surf, (WIDTH // 2 - highscore_surf.get_width() // 2, 60)
    )

    pg.display.flip()
    clock.tick(60)

pg.quit()
Note

💡 Hinweis: Dieses Skript verwendet grafische Desktop-Bibliotheken (turtle / pygame). Führen Sie diesen Code in Ihrer lokalen Entwicklungsumgebung (z.B. VS Code) aus.

↩︎ Zurück zu Aufgabe 8.15: Leaderboard-Tabelle mit CSV

}