Zum Inhalt springen
BetaEN
Lektion 2 von 4Spiele programmieren

Spiele programmieren · Lektion 2 · 45 Minuten

Projekt: Fangspiel

Das lernst du

  • Mit random Dinge an zufälligen Stellen erscheinen lassen
  • Prüfen, ob sich zwei Rechtecke berühren (Kollision)
  • Punkte zählen, Leben abziehen und das Spiel mit spiel.ende() beenden

Fang die Sterne!

Von oben fallen Sterne. Unten fährt dein Korb hin und her. Jeder gefangene Stern bringt einen Punkt. Dafür brauchst du drei neue Dinge: Zufall, Kollision und einen Punktestand.

Zufall: Wo kommt der nächste Stern?

random.randint(0, 380) liefert eine zufällige ganze Zahl von 0 bis 380. Jeder Stern fällt so an einer anderen Stelle.

import random
from codelix import spiel

spiel.fenster(400, 300)
stern = {"x": 190, "y": 0}


def schritt():
    stern["y"] = stern["y"] + 5
    if stern["y"] > 300:
        stern["y"] = 0
        stern["x"] = random.randint(0, 380)
    spiel.rechteck(stern["x"], stern["y"], 20, 20, "gold")


spiel.starten(schritt)

Kollision: Berühren sich zwei Rechtecke?

Fängt der Korb den Stern? Das ist die wichtigste Frage in fast jedem Spiel. Zwei Rechtecke überlappen, wenn sie sich sowohl nach links-rechts als auch nach oben-unten überschneiden.

Nach links-rechts überschneiden sie sich, wenn beides stimmt:

  • a fängt links vom rechten Rand von b an: a["x"] < b["x"] + b["b"]
  • a hört rechts vom linken Rand von b auf: a["x"] + a["b"] > b["x"]

Für oben-unten gilt dasselbe mit y und der Höhe h. Probier es mit zwei Rechtecken aus:

a = {"x": 0, "y": 0, "b": 50, "h": 30}
b = {"x": 40, "y": 20, "b": 30, "h": 30}

links_rechts = a["x"] < b["x"] + b["b"] and a["x"] + a["b"] > b["x"]
oben_unten = a["y"] < b["y"] + b["h"] and a["y"] + a["h"] > b["y"]

print("überschneiden sich links-rechts:", links_rechts)
print("überschneiden sich oben-unten:", oben_unten)
print("Kollision:", links_rechts and oben_unten)

Ändere b["x"] auf 60. Dann ist links-rechts False, und es gibt keine Kollision mehr.

Leben und Spielende

Ein echtes Spiel ist irgendwann vorbei. Mit spiel.ende() hörst du auf. Das letzte Bild bleibt stehen. Hier fallen drei Sterne durch – dann ist Schluss:

from codelix import spiel

spiel.fenster(400, 300)
stern = {"y": 0}
stand = {"leben": 3}


def schritt():
    stern["y"] = stern["y"] + 15
    if stern["y"] > 300:
        stern["y"] = 0
        stand["leben"] = stand["leben"] - 1
    spiel.rechteck(190, stern["y"], 20, 20, "gold")
    spiel.text(10, 10, f"Leben: {stand['leben']}")
    if stand["leben"] == 0:
        spiel.text(120, 130, "Spiel vorbei!", "tomato", 28)
        spiel.ende()


spiel.starten(schritt)
print("Danke fürs Spielen!")

Nach spiel.ende() läuft dein Programm weiter. Deshalb erscheint am Ende noch der Text in der Ausgabe.

Weiterbauen: Wenn deine Aufgabe klappt, bau Leben ein: Fällt ein Stern unten durch, verlierst du eins. Bei 0 Leben ist das Spiel vorbei. Noch schwieriger: Mit jedem Punkt fallen die Sterne ein bisschen schneller.

Deine Aufgabe

Jetzt bist du dran

Schreib die Funktion trifft(a, b). Sie bekommt zwei Rechtecke als Dictionaries mit x, y, b (Breite) und h (Höhe). Sie gibt True zurück, wenn sich die beiden überlappen, sonst False. Berühren sich nur die Kanten, zählt das nicht. Nutze trifft dann in schritt(): Fängt der Korb den Stern, gibt es einen Punkt und der Stern startet wieder oben (y = 0).

import random
from codelix import spiel

spiel.fenster(400, 300)
korb = {"x": 170, "y": 270, "b": 60, "h": 20}
stern = {"x": 190, "y": 0, "b": 20, "h": 20}
stand = {"punkte": 0}


def trifft(a, b):
    return False


def schritt():
    if spiel.taste("links") and korb["x"] > 0:
        korb["x"] = korb["x"] - 6
    if spiel.taste("rechts") and korb["x"] < 340:
        korb["x"] = korb["x"] + 6

    stern["y"] = stern["y"] + 4
    if stern["y"] > 300:
        stern["y"] = 0
        stern["x"] = random.randint(0, 380)

    # Hier: Hat der Korb den Stern gefangen?

    spiel.rechteck(korb["x"], korb["y"], korb["b"], korb["h"], "sienna")
    spiel.rechteck(stern["x"], stern["y"], stern["b"], stern["h"], "gold")
    spiel.text(10, 10, f"Punkte: {stand['punkte']}")


spiel.starten(schritt)

Ohne Hinweise gelöst gibt es einen ⭐ auf deiner Fortschrittsseite.

Lösung anzeigen

Versuch es erst selbst – aus Fehlern lernt man am meisten. Hier ist eine mögliche Lösung:

import random
from codelix import spiel

spiel.fenster(400, 300)
korb = {"x": 170, "y": 270, "b": 60, "h": 20}
stern = {"x": 190, "y": 0, "b": 20, "h": 20}
stand = {"punkte": 0}


def trifft(a, b):
    return (a["x"] < b["x"] + b["b"] and a["x"] + a["b"] > b["x"]
            and a["y"] < b["y"] + b["h"] and a["y"] + a["h"] > b["y"])


def schritt():
    if spiel.taste("links") and korb["x"] > 0:
        korb["x"] = korb["x"] - 6
    if spiel.taste("rechts") and korb["x"] < 340:
        korb["x"] = korb["x"] + 6

    stern["y"] = stern["y"] + 4
    if stern["y"] > 300:
        stern["y"] = 0
        stern["x"] = random.randint(0, 380)

    if trifft(korb, stern):
        stand["punkte"] = stand["punkte"] + 1
        stern["y"] = 0
        stern["x"] = random.randint(0, 380)

    spiel.rechteck(korb["x"], korb["y"], korb["b"], korb["h"], "sienna")
    spiel.rechteck(stern["x"], stern["y"], stern["b"], stern["h"], "gold")
    spiel.text(10, 10, f"Punkte: {stand['punkte']}")


spiel.starten(schritt)