Die LunarLander API im Überblick
Du steuerst kein Programm mehr Zeile für Zeile — du nutzt eine fertige Spielengine. Die API übernimmt Physik, Zeichnen und Kollision. Du lieferst nur noch die Logik.
API = Application Programming Interface.
Du rufst Funktionen auf, ohne zu wissen, wie sie intern funktionieren — wie ein Fernseher mit Fernbedienung.
Beispielsweise liefert getFuel() den aktuellen Treibstoffstand,
und setGravity(0.05) macht die Schwerkraft sanfter —
ohne dass du weißt, wie die Physik dahinter berechnet wird.
Das Spiel läuft in einer Schleife. Pro Frame passieren diese Dinge in dieser Reihenfolge. Klicke auf eine Box, um eine Erklärung mit Beispiel zu sehen.
Diese Funktionen kannst du anmelden. Du schreibst sie, die Engine ruft sie auf — zur richtigen Zeit, automatisch.
Physik und Rendering laufen automatisch. tick() und render() rufst du nie selbst auf.
init() und start()
Jedes LunarLander-Programm beginnt gleich: API importieren, Tastatur-Handler definieren,
init() + start() aufrufen.
| Funktion | Was sie tut |
|---|---|
| init(keyHandler) | Öffnet das Fenster, verbindet deine Tastatur-Funktion |
| start() | Startet die Spielschleife — kehrt erst nach Spielende zurück |
from lunarlander import *
def handleKey(keyCode):
if keyCode == 38: # Pfeil hoch → Schub
thrust()
elif keyCode == 37: # Pfeil links
moveLeft()
elif keyCode == 39: # Pfeil rechts
moveRight()
init(handleKey) # Fenster öffnen, Tastatur verbinden
start() # Spielschleife starten
# Hier läuft Code erst NACH dem Spielende:
if hasLanded():
print("Gelandet!")
elif hasCrashed():
print("Absturz!")
Deine eigene Datei darf nicht lunarlander.py heißen —
das ist der Name der API-Datei. Python würde sonst beim Import die falsche Datei laden
und init() schlägt mit einem Fehler fehl.
Wähle einen eigenen Namen, z.B. mein_lander.py.
Jede Taste hat einen eindeutigen Code. Die Pfeiltasten sind nur ein Beispiel.
| Taste | Keycode | Mögliche Aktion |
|---|---|---|
| Pfeil ← | 37 | moveLeft() |
| Pfeil ↑ | 38 | thrust() |
| Pfeil → | 39 | moveRight() |
| Leertaste | 32 | thrust() |
| ESC | 27 | Spiel beenden (eingebaut) |
| W, A, S, D | 87, 65, 83, 68 | Typische WASD-Steuerung |
TigerJython nutzt Javas getKeyCode() — das liefert immer den Code der
physischen Taste, unabhängig von Shift.
Buchstabentasten haben deshalb immer die Großbuchstaben-Codes (A–Z = 65–90),
auch wenn man ohne Shift tippt.
- Der Keycode für die
A-Taste ist 65, fürZist er 90. Erkennst du ein Muster? - TigerJython liefert immer den Code der physischen Taste — egal ob Shift gedrückt ist. Welche Codes braucht man also für eine WASD-Steuerung?
- Wie, wenn die Leertaste Schub geben soll statt Pfeil hoch?
Starte das Minimal-Beispiel in TigerJython. Klicke ins Spielfenster für den Tastaturfokus.
- Wie hoch ist deine V-Speed beim ersten Aufprall?
- Was passiert, wenn der Treibstoff auf 0 sinkt?
- Erweitere: Gib nach dem Spielende zusätzlich aus, wie viel Treibstoff verbraucht wurde (Startwert: 200).
Getter-Funktionen und HUD
Die API liefert jederzeit den aktuellen Zustand des Landers. Diese Werte kannst du nach dem Spielende auswerten — oder im HUD anzeigen lassen.
| Funktion | Wirkung |
|---|---|
| thrust() | Schub nach oben |
| moveLeft() | Schub nach links |
| moveRight() | Schub nach rechts |
| Funktion | Rückgabe |
|---|---|
| getHeight() | Höhe über Terrain |
| getVSpeed() | Vertikalgeschw. |
| getHSpeed() | Horizontalgeschw. |
| getFuel() | Treibstoff (0–200) |
| getX() | X-Position |
| isOverLandingZone() | True / False |
| hasLanded() | True nach Landung |
| hasCrashed() | True nach Absturz |
| Funktion | Parameter / Beispiel |
|---|---|
| setHudItems(itemListe) | itemListe = ["fuel", "vSpeed", "height"] |
| setHudFontSize(groesse) | setHudFontSize(18) |
| setHudPosition(abstandLinks, abstandOben) | setHudPosition(10, 30) |
Erlaubte Werte für itemListe:
from lunarlander import *
itemListe = ["fuel", "vSpeed", "height"] # was im HUD erscheint
setHudItems(itemListe)
setHudFontSize(16) # Schriftgröße in Pixeln
setHudPosition(10, 30) # Abstand vom linken / oberen Rand
def handleKey(keyCode):
if keyCode == 37:
moveLeft()
elif keyCode == 38:
thrust()
elif keyCode == 39:
moveRight()
init(handleKey)
start()
# Ab hier: Spiel ist beendet
if hasLanded():
print("Gelandet! Treibstoff übrig:", getFuel())
elif hasCrashed():
print("Absturz! V-Speed:", round(getVSpeed(), 2))
Experimentiere mit dem Programm:
- Ergänze
itemListeum"hSpeed"und"x". - Füge vor
init()ein:setGravity(0.05)— was ändert sich? - Berechne und gib nach dem Spiel aus: Verbrauch = 200 − getFuel().
setCustomDrawer() — Hook 1
Dein erster Hook: statt des gelben Standard-Quadrats zeichnest du deinen Lander selbst. Die Engine ruft deine Funktion automatisch jeden Frame auf. Alle Koordinaten sind relativ zur Lander-Mitte (0, 0).
Du schreibst eine Funktion, die den Lander zeichnet. Du meldest sie einmalig vor init() an.
Die Engine kümmert sich darum, dass sie jeden Frame zur richtigen Zeit aufgerufen wird.
Verfügbare Zeichenbefehle:
Wichtig:
setPos() teleportiert immer ohne Linie — auch bei penDown().
Für Linien: penDown() + moveTo().
Absolut vs. relativ:
Es gibt zwei Wege zu zeichnen. Empfohlen: absolut — du gibst Koordinaten direkt an
(setPos(x,y) + moveTo(x,y)). Du siehst sofort welcher Punkt wo landet.
Alternativ: relativ mit forward(), left(), right() —
funktioniert, aber Achtung: die Winkelkonvention ist
0° = oben, 90° = rechts (Uhrzeigersinn) — nicht wie im Matheunterricht.
Das führt leicht zu gespiegelten Formen.
from lunarlander import *
def meinLander():
# Rumpf: Rechteck — absolut mit moveTo(), (0,0) = Lander-Mitte
# +Y = oben, –Y = unten, +X = rechts, –X = links
setPenColor("cyan")
setPenWidth(2)
penUp()
setPos(-12, 14) # oben-links
penDown()
moveTo(12, 14) # → oben-rechts
moveTo(12, -14) # → unten-rechts
moveTo(-12, -14) # → unten-links
moveTo(-12, 14) # → schließt Rechteck
penUp()
# Linkes Landegestell: schräg nach links-unten
setPenColor("gray")
setPos(-12, -14)
penDown()
moveTo(-22, -26)
penUp()
# Rechtes Landegestell: schräg nach rechts-unten
setPos(12, -14)
penDown()
moveTo(22, -26)
penUp()
# Cockpit-Fenster
setPenColor("white")
setPos(0, 2)
dot(8)
setCustomDrawer(meinLander) # Hook anmelden — vor init()
def handleKey(keyCode):
if keyCode == 37: moveLeft()
elif keyCode == 38: thrust()
elif keyCode == 39: moveRight()
setHudItems(["fuel", "vSpeed", "height"])
init(handleKey)
start()
Entwirf deinen eigenen Lander. Mindestanforderungen:
- Mindestens zwei Farben
- Eine erkennbare Landebasis (Beine, Stützen oder Kufen)
- Ein sichtbares Triebwerk oder eine Düse
Tipp: Teste deinen Lander ohne den Rest des Programms — zeichne ihn zuerst einfach auf Papier mit dem Koordinatensystem (0,0 = Mitte).
setUpdateFunction() — Hook 2
Dein zweiter Hook: eine Funktion, die die Engine jeden Frame aufruft — bevor die Physik berechnet wird. Hier baust du Spiellogik, Warnungen und Zähler ein.
| keyHandler (Hook 1) | updateFunction (Hook 2) | |
|---|---|---|
| Wann | Bei Tastendruck | Jeden Frame (~70 ms) |
| Wofür | Steuerung des Landers | Logik, Warnungen, Zähler |
| Getter nutzbar | Ja | Ja |
from lunarlander import *
frameZaehler = 0 # zählt die Frames seit Spielstart
warnungAusgegeben = False # Merker: Warnung nur einmal ausgeben
def update():
global frameZaehler, warnungAusgegeben # Variablen von außen verändern
frameZaehler = frameZaehler + 1
# alle 30 Frames einen Status ausgeben
if frameZaehler % 30 == 0:
print("Höhe:", round(getHeight(), 1), "| Treibstoff:", getFuel())
# einmalige Warnung bei niedrigem Treibstoff
if getFuel() < 40 and not warnungAusgegeben:
print("*** TREIBSTOFF KRITISCH ***")
warnungAusgegeben = True
setUpdateFunction(update) # Hook anmelden — vor init()
def handleKey(keyCode):
if keyCode == 37: moveLeft()
elif keyCode == 38: thrust()
elif keyCode == 39: moveRight()
setHudItems(["fuel", "vSpeed", "hSpeed", "height"])
init(handleKey)
start()
Regel:
Variablen, die du in einer Funktion nur liest, brauchen kein global.
Variablen, die du veränderst, müssen mit global deklariert werden —
sonst erstellt Python stillschweigend eine lokale Kopie und der ursprüngliche Wert bleibt unverändert.
Du kannst eigene Funktionen schreiben und diese aus Hooks heraus aufrufen.
Auch dort gilt die global-Regel:
punkte = 0
warnungGezeigt = False
def zeigeWarnung(): # eigene Hilfsfunktion
global warnungGezeigt # wird verändert → global nötig
if not warnungGezeigt:
print("Treibstoff niedrig!")
warnungGezeigt = True
def update():
global punkte # wird verändert → global nötig
punkte = punkte + 1
if getFuel() < 30:
zeigeWarnung() # Hilfsfunktion aus dem Hook aufrufen
setUpdateFunction(update)
Lesen: kein global nötig ·
Verändern: global erforderlich ·
Hilfsfunktion aufrufen: einfach den Namen schreiben
Mit togglePause() kannst du das Spiel in der Update-Funktion oder im keyHandler pausieren:
| Funktion | Wirkung |
|---|---|
| pauseGame() | Spielschleife anhalten |
| resumeGame() | Fortsetzen |
| togglePause() | Pause ein/aus — ideal für eine Taste |
| isPaused() | True wenn pausiert |
Ergänze die Update-Funktion:
- Gib eine Meldung aus, wenn der Lander über der Landezone ist
(
isOverLandingZone()) — aber nur alle 20 Frames, nicht jeden Frame. - Warne, wenn Höhe unter 60 und Vertikalgeschwindigkeit über 4.0 liegt.
- Bonus: Baue eine Pause-Taste (z.B. P = Keycode 80) in deinen
handleKeyein.
Drei Hooks — ein Programm
Du kennst jetzt alle Bausteine. Statt einem fertigen Programm zum Kopieren bekommst du drei kleine Startpunkte — Stubs mit Lücken, die du selbst vervollständigst.
from lunarlander import *
# ── Dein keyHandler ────────────────────────────────────────
def handleKey(keyCode):
# TODO: Pfeil hoch → thrust(), links → moveLeft(), rechts → moveRight()
pass
# ── Einstellungen ──────────────────────────────────────────
setHudItems(["fuel", "vSpeed", "height"])
# TODO: Probiere setGravity(...) und setFuel(...)
init(handleKey)
start()
# ── Auswertung ─────────────────────────────────────────────
# TODO: hasLanded() / hasCrashed() abfragen und Ergebnis ausgeben
from lunarlander import *
# ── Hook 2: Dein Lander ────────────────────────────────────
def meinLander():
# TODO: mindestens Rumpf + 1 Farbe + 1 weiteres Element
pass
setCustomDrawer(meinLander) # Hook anmelden — vor init()
# ── Hook 1: Steuerung ──────────────────────────────────────
def handleKey(keyCode):
if keyCode == 37: moveLeft()
elif keyCode == 38: thrust()
elif keyCode == 39: moveRight()
setHudItems(["fuel", "vSpeed", "height"])
init(handleKey)
start()
if hasLanded():
print("Gelandet! Treibstoff:", getFuel())
elif hasCrashed():
print("Absturz! V-Speed:", round(getVSpeed(), 2))
from lunarlander import *
# ── Hook 2: Lander ────────────────────────────────────────
def meinLander():
# TODO: dein Design von Stub B
pass
setCustomDrawer(meinLander)
# ── Hook 3: Update-Logik ──────────────────────────────────
zaehler = 0
def update():
global zaehler
zaehler = zaehler + 1
# TODO: Warnmeldung bei niedrigem Treibstoff (einmalig)
# TODO: Landehinweis wenn über Landezone und Höhe < 80
setUpdateFunction(update) # Hook anmelden — vor init()
# ── Hook 1: Steuerung ─────────────────────────────────────
def handleKey(keyCode):
if keyCode == 37: moveLeft()
elif keyCode == 38: thrust()
elif keyCode == 39: moveRight()
# TODO (Bonus): P-Taste = togglePause()
setHudItems(["fuel", "vSpeed", "height"])
init(handleKey)
start()
# ── Ergebnis ──────────────────────────────────────────────
# TODO: Auswertung ausgeben
AUSBLICK — DAS NÄCHSTE PROJEKT
- Eigenes Lander-Design (alle 3 Elemente)
- Eigenes Terrain + Landezone
- Mindestens 2 Warnmeldungen im Update
- Auswertung nach Spielende
- Punktesystem mit Treibstoffbonus
- Mehrere Landeplätze
setLandingZones() - Zeitlimit via
setGameEndFunction() - Animation:
setRotation()im Update - Schub-Flamme im Custom Drawer
- Eigene Idee — sprich mit mir
| Funktion | Wirkung |
|---|---|
| setTerrain([...]) | Eigenes Geländeprofil (mind. 8 Werte) |
| setGravity(wert) | Schwerkraft ändern (Standard: 0.15) |
| setFuel(menge) | Startmenge Treibstoff setzen |
| setLandingZone(start, ende) | Landeplatz-Indizes setzen |
| setLandingZones([[s,e],...]) | Mehrere Landeplätze setzen |
| setScale(faktor) | Lander-Größe skalieren (Standard: 1.0) |
| setRotation(winkel) | Lander rotieren in Grad (Standard: 0) |
| setGameEndFunction(fn) | Eigene Endbedingung (True = Spiel endet) |
| getFrameTime() | Dauer des letzten Frames in ms |
| restart() | Spiel neu starten |
| setWindowSize(b, h) | Fenstergröße ändern — Standard: 600 × 400 |
Alle Funktionen und ihre genaue Beschreibung findest du in der API-Dokumentation.