v0.9.9

Inhalt

1. Zeichnen und Gestalten
Wie erstelle ich einen eigenen LunarLander?

Mit setCustomDrawer(drawFunction). Deine Funktion zeichnet den Lander relativ zu seinem Mittelpunkt — setPos(0, 0) ist die Mitte des Landers.

from lunarlander import *

def meinLander():
    # Empfohlen: 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()
    setPenColor("white")
    setPos(0, 2)
    dot(8)              # Cockpit-Fenster

setCustomDrawer(meinLander)   # vor init() anmelden
init(handleKey)
start()
Wichtig: setPos() teleportiert immer ohne Linie — auch bei penDown(). Für Linien immer moveTo() verwenden.
Wie erstelle ich ein Terrain?

Das Terrain ist eine Liste mit Höhenwerten. Jeder Eintrag steht für einen Abschnitt von links nach rechts. Mindestens 8 Werte sind erforderlich.

setTerrain([30, 60, 90, 70, 20, 20, 20, 20, 55, 100, 120, 80, 40])
setLandingZone(4, 7)   # flacher Bereich (Index 4–7) = Landeplatz

Größere Werte = höheres Gelände. Die Werte sind relative Höhen, keine absoluten Koordinaten.

Wie füge ich mehrere Landeplätze hinzu?

Mit setLandingZones([...]) kannst du mehrere Landezonen auf einmal setzen. Der Lander reagiert dann automatisch auf jede dieser Zonen.

setLandingZones([(4, 7), (10, 12)])
# Landeplatz 1: Terrain-Index 4 bis 7
# Landeplatz 2: Terrain-Index 10 bis 12
Hinweis: Mindestens eine Zone ist immer erforderlich. Die Indizes müssen ins Terrain passen.
Wie funktionieren Skalierung und Rotation?

setScale(faktor) vergrößert oder verkleinert die Zeichnung und die Kollisionsprobe. setRotation(winkel) dreht sie.

setScale(1.5)     # 50 % größer als Standard (Standard: 1.0)
setRotation(30)   # 30° gedreht

Winkelkonvention: 0° = Ausgangsposition, positive Werte = gegen den Uhrzeigersinn.

Rotation und Scale lassen sich auch in der updateFunction animieren:

winkel = 0

def update():
    global winkel
    winkel = (winkel + 2) % 360
    setRotation(winkel)

setUpdateFunction(update)

HUD-Werte anzeigen: setHudItems(["rotation", "scale"])

Was ist die CollisionProbe?

Die CollisionProbe ist der Auftreffpunkt für die Terrain-Kollision. Sie wird relativ zur Lander-Mitte definiert:

setCollisionProbe(north, east, south, west)
# Beispiel: Probe am Fuß eines Landers mit y=-14
setCollisionProbe(0, 0, 14, 0)

Die Berechnung: collisionY = landerY + north − south

Zum Debuggen sichtbar machen:

setShowCollisionProbe(True)
setCollisionProbeStyle("red", 10)

Standardwert: south = landerSize / 2 (= 15 bei landerSize=30)

Wie kann ich Animationen machen?

Animationen brauchen immer drei Teile: eine Zustandsvariable, setUpdateFunction zum Ändern des Zustands, und setCustomDrawer zum Zeichnen.

schubAktiv = False

def update():
    global schubAktiv
    # Zustand pro Frame aktualisieren
    # (hier wird schubAktiv anderswo gesetzt — z.B. im keyHandler)

def meinLander():
    # Rumpf immer zeichnen — absolut mit moveTo()
    setPenColor("cyan"); setPenWidth(2)
    penUp(); setPos(-10, 10); penDown()
    moveTo(10, 10); moveTo(10, -10)
    moveTo(-10, -10); moveTo(-10, 10); penUp()
    # Flamme nur wenn Schub aktiv
    if schubAktiv:
        setPenColor("orange"); setPenWidth(3)
        penUp(); setPos(-4, -10); penDown()
        moveTo(-4, -22)   # Flamme nach unten
        penUp()

setUpdateFunction(update)
setCustomDrawer(meinLander)
Tipp: setRotation() und setScale() lassen sich direkt in update() aufrufen — das reicht oft für einfache Animationen ohne eigenen Drawer.
2. Spielablauf und Logik
Wie greife ich in den laufenden Spielablauf ein?

Dafür ist setUpdateFunction(fn) gedacht. Deine Funktion wird jeden Frame aufgerufen — vor Physik, Zeichnen und Kollisionsprüfung.

def meinUpdate():
    if getFuel() < 30:
        print("Treibstoff fast leer!")
    if isOverLandingZone() and getHeight() < 60:
        print("Landeanflug!")

setUpdateFunction(meinUpdate)   # vor init() anmelden

Dort kannst du z.B. Punkte vergeben, Warnmeldungen ausgeben oder eigene Spielbedingungen prüfen.

Wie kann ich Punkte vergeben?

Die Bibliothek vergibt keine Punkte automatisch. Du verwaltest sie mit einer eigenen Variable in der updateFunction.

punkte = 1000   # Startpunkte

def update():
    global punkte
    verbrauch = 200 - getFuel()
    punkte = 1000 - verbrauch     # Abzug für Treibstoffverbrauch

setUpdateFunction(update)

# Nach start(): Endstand ausgeben
start()
print("Punkte:", punkte)
Wie kann ich das Spiel mit einer eigenen Bedingung beenden?

Mit setGameEndFunction(fn). Gibt deine Funktion True zurück, endet das Spiel sofort.

zeitLimit = 300   # 300 Frames ≈ 21 Sekunden

def pruefeEnde():
    global zeitLimit
    zeitLimit -= 1
    if zeitLimit <= 0:
        print("Zeit abgelaufen!")
        return True
    return False

setGameEndFunction(pruefeEnde)   # vor init() anmelden
Kann ich das Fenster auch schließen?

Die Spielschleife endet durch Landung, Absturz, ESC oder eine eigene Endbedingung. Das Fenster selbst bleibt normalerweise offen. In TigerJython wird es beim Programmende automatisch geschlossen.

Die ESC-Taste beendet das Spiel standardmäßig. Eine andere Taste kann mit setAbortKeyCode(code) gesetzt werden.

Kann ich Schwierigkeitslevel erstellen?

Ja — durch unterschiedliche Konfiguration vor init():

level = 2   # 1 = leicht, 2 = mittel, 3 = schwer

if level == 1:
    setGravity(0.08)
    setFuel(300)
    setMaxVerticalLandingSpeed(4.0)
elif level == 2:
    setGravity(0.15)
    setFuel(200)
    setMaxVerticalLandingSpeed(3.0)
elif level == 3:
    setGravity(0.25)
    setFuel(120)
    setMaxVerticalLandingSpeed(2.0)

init(handleKey)
start()
Kann ich mehrere Level / Terrains nacheinander laden?

Ja, mit restart() in Kombination mit einer eigenen Levelstruktur:

terrains = [
    [20, 20, 60, 80, 5, 5, 5, 70, 40, 20],
    [80, 60, 30, 10, 10, 10, 40, 70, 90, 50],
]
aktuellesLevel = 0

def ladeLevel(n):
    setTerrain(terrains[n])
    setLandingZone(3, 5)
    restart()

ladeLevel(0)
init(handleKey)
start()

if hasLanded() and aktuellesLevel < len(terrains) - 1:
    aktuellesLevel += 1
    ladeLevel(aktuellesLevel)
    start()
Kann ich ein 2-Personen-Spiel machen?

Mit der LunarLander-API direkt ist das schwierig, weil es nur einen Lander gibt. Es gibt aber kreative Ansätze:

  • Rundenbasiert: Spieler 1 landet, Ergebnis wird gespeichert, dann restart() für Spieler 2 mit anderem Startpunkt.
  • Völlig anderes Spiel: Die API als reine Render-Engine verwenden und beide Spielobjekte im setCustomDrawer selbst zeichnen und verwalten — wie bei Pong.
3. Zeit, Frames & v0.9
Was ist getFrameTime() und wofür brauche ich das?

getFrameTime() gibt die Dauer des letzten kompletten Frames in Millisekunden zurück. Das ist nützlich um zu erkennen, ob das Spiel ruckelt oder ob die Zeichenfunktion zu aufwendig ist.

def update():
    if getFrameTime() > 100:
        print("Langsamer Frame:", round(getFrameTime(), 1), "ms")

setUpdateFunction(update)

Faustregel:

  • > 100 ms → deutliches Ruckeln wahrscheinlich
  • ≈ 40 ms → ungefähr 25 FPS
  • ≈ 20 ms → ungefähr 50 FPS

Auch als HUD-Wert anzeigbar: setHudItems(["fuel", "vSpeed", "frameTime"])

Wann ändere ich delay, wann smoothFactor?
  • delay ist die Wartezeit am Ende jedes Frames. Größeres delay → langsameres Spiel insgesamt.
  • smoothFactor bestimmt, wie stark sich etwas pro Frame ändert — nicht das Gesamttempo.

Beispiele:

  • Spiel wirkt insgesamt zu langsam → setDelay(30) statt setDelay(70)
  • Lander springt pro Frame zu weit → setSmoothFactor(0.3) statt 0.45
  • Lander reagiert zu träge auf Schub → setSmoothFactor(0.7)
Wichtig: smoothFactor = 0 ist keine gute Idee — dann ist fast keine Bewegung sichtbar.
Was sind die Schutzgrenzen für delay und smoothFactor?

In v0.9 werden zu kleine Werte automatisch auf ein sinnvolles Minimum angehoben:

  • delay mindestens 5 ms
  • smoothFactor mindestens 0.05
setDelay(0)
print(getDelay())          # 5

setSmoothFactor(0)
print(getSmoothFactor())   # 0.05
Wie kann ich das Spiel pausieren?
pauseGame()      # Spielschleife anhalten
resumeGame()     # Spielschleife fortsetzen
togglePause()    # Pause ein/aus wechseln
print(isPaused())

Typischer Einsatz: eine Taste schaltet Pause ein und aus.

def handleKey(keyCode):
    if keyCode == 80:    # P-Taste
        togglePause()
    elif keyCode == 38:
        thrust()
Pause ist besonders beim Debuggen hilfreich: Spiel anhalten, Konsolenausgaben in Ruhe lesen, dann fortsetzen. HUD-Wert: "paused"
Gibt es keyReleased in der API?

Nein — die Bibliothek bleibt bei keyPressed. Das OS sendet beim Halten einer Taste automatisch wiederholte Druckereignisse (Auto-Repeat).

Workaround für sanfte Bewegung (z.B. in einem Pong-Spiel): Velocity + Damping.

paddleY  = 0.0
paddleVY = 0.0
MAX_SPEED = 12
DAMPING   = 0.5    # klingt in ca. 3 Frames auf null ab

def update():
    global paddleY, paddleVY
    paddleY  = paddleY + paddleVY
    paddleVY = paddleVY * DAMPING   # ohne Tastendruck: Auslaufen

def handleKey(keyCode):
    global paddleVY
    if keyCode == 87:   # W-Taste
        paddleVY = MAX_SPEED
    elif keyCode == 83: # S-Taste
        paddleVY = -MAX_SPEED
Was ist setLandingZones() und wie unterscheidet es sich von setLandingZone()?

setLandingZone(start, ende) setzt genau einen Landeplatz.

setLandingZones([[start, ende], ...]) setzt mehrere Landeplätze auf einmal.

# Ein Landeplatz:
setLandingZone(3, 5)

# Mehrere Landeplätze:
setLandingZones([(2, 4), (8, 10)])   # zwei Landeplätze
Mindestens eine Zone ist immer erforderlich. Die Indizes müssen ins Terrain passen.
4. Steuerung und Bedienung
Wie kann ich eigene Tasten für die Steuerung definieren?

Im keyHandler kannst du beliebige Keycodes abfragen. Häufig verwendete Codes:

  • Pfeiltasten: ← 37, ↑ 38, → 39, ↓ 40
  • Leertaste: 32, ESC: 27, Enter: 10
  • Buchstabentasten: A=65, S=83, D=68, W=87  (A–Z = 65–90)
  • Ziffern: 0=48, 1=49, … 9=57
TigerJython-Besonderheit: getKeyCode() liefert immer den Code der physischen Taste — unabhängig davon ob Shift gedrückt ist. Buchstabentasten haben deshalb immer die Großbuchstaben-Codes (A=65, W=87 …), auch wenn man ohne Shift tippt.
def handleKey(keyCode):
    # print(keyCode)   # zum Herausfinden unbekannter Codes
    if keyCode == 87:  # W-Taste (Code 87 — unabhängig von Shift)
        thrust()
    elif keyCode == 65: # A-Taste
        moveLeft()
    elif keyCode == 68: # D-Taste
        moveRight()
    elif keyCode == 80: # P-Taste
        togglePause()

Die ESC-Taste (27) beendet das Spiel standardmäßig. Ändern mit setAbortKeyCode(code).

Wie verwende ich global — und wie rufe ich eigene Funktionen aus einem Hook auf?

Die Regel: In Python 2.7 (TigerJython) musst du eine Variable mit global deklarieren, wenn du sie innerhalb einer Funktion verändern willst. Nur lesen geht ohne global.

zaehler = 0
warnungGezeigt = False

def update():
    global zaehler, warnungGezeigt   # beide werden verändert
    zaehler = zaehler + 1
    if getFuel() < 30 and not warnungGezeigt:
        print("Treibstoff niedrig!")
        warnungGezeigt = True         # ohne global: lokale Kopie, Original bleibt 0

setUpdateFunction(update)

Eigene Hilfsfunktionen aus Hooks aufrufen: Du kannst beliebige eigene Funktionen definieren und sie aus update() oder handleKey() aufrufen — einfach den Namen schreiben. Die global-Regel gilt auch dort.

aktionAusgefuehrt = False

def zeigeWarnung():              # eigene Hilfsfunktion
    global aktionAusgefuehrt     # wird verändert → global nötig
    if not aktionAusgefuehrt:
        print("Achtung!")
        aktionAusgefuehrt = True

def update():
    if getFuel() < 40:
        zeigeWarnung()           # Hilfsfunktion aufrufen — kein global nötig hier

setUpdateFunction(update)
Kurzregel: Lesen → kein global  ·  Verändern → global erforderlich  ·  Hilfsfunktion aufrufen → einfach den Namen schreiben
Wie debugge ich mein Spiel am besten?
  • print() in der updateFunction — Werte jeden Frame ausgeben
  • getFrameTime() — prüfen ob das Spiel ruckelt
  • CollisionProbe sichtbar machen: setShowCollisionProbe(True)
  • Spielfeld-Rahmen einblenden: setShowInnerPlayfieldBorder(True)
  • HUD-Werte anzeigen: setHudItems(["height", "vSpeed", "collisionY", "frameTime"])
  • Pause nutzen: Spiel pausieren → Konsolenausgaben lesen → fortsetzen
def meinUpdate():
    print("h:", round(getHeight(),1),
          "vS:", round(getVSpeed(),2),
          "ft:", round(getFrameTime(),0))

setUpdateFunction(meinUpdate)
Wie ändere ich die Fenstergröße?

Mit setWindowSize(breite, hoehe) vor init(). Das schwarze Fenster ist die gesamte Spielfläche — es gibt keine feste Standardgröße, die du berücksichtigen musst.

setWindowSize(500, 700)   # Hochformat — vor init()
init(handleKey)
start()

Bei Änderung der Höhe wird groundLevel automatisch neu berechnet (relativ-Modus) — oder proportional skaliert wenn es vorher manuell gesetzt wurde. Mehr dazu in der API-Dokumentation → setWindowSize().

Tipp: Ein schmaleres, höheres Fenster gibt dem Lander mehr Fallstrecke und macht das Spiel dynamischer.

Aktuelle Größe abfragen: getWindowSize() → gibt (breite, hoehe) zurück.

Wie schalte ich die Versionsmeldung beim Start aus?

Beim Aufruf von init() gibt die API automatisch eine Meldung in der Konsole aus — z.B. LunarLander API v0.9.9 ready. Das hilft SuS zu sehen, welche API-Version aktiv ist.

Abschalten mit setStartupMessage(False) vor init():

setStartupMessage(False)   # Meldung unterdrücken
init(handleKey)
start()
Tipp: Die Meldung ist standardmäßig aktiv — sie zeigt sofort ob die richtige Datei geladen wurde.
5. HUD und Anzeige
Wie kann ich das HUD verschieben?

Mit setHudPosition(abstandLinks, abstandOben).

setHudPosition(10, 30)    # Standard: 10 px von links, 30 px von oben
setHudPosition(200, 30)   # HUD in der Mitte des Fensters

Schriftgröße anpassen: setHudFontSize(18) — Standard ist 16.

Alle verfügbaren HUD-Werte: fuel, vSpeed, hSpeed, height, x, gravity, collisionX, collisionY, frameTime, rotation, scale, paused, abortKey

Kann ich HUD-Elemente horizontal anordnen?

Die Standard-HUD-Anzeige ist immer vertikal (eine Zeile pro Element). Eine echte horizontale Anordnung ist mit dem eingebauten HUD nicht möglich.

Workaround: HUD ausblenden und die Werte im setCustomDrawer selbst zeichnen — z.B. mit setPos() und dot() als Balkenanzeige, oder durch Aufrufe direkt im Drawer.

setHudItems([])   # eingebautes HUD ausblenden

def meinDrawer():
    # eigene Anzeige — z.B. Treibstoffbalken
    setPenColor("lime")
    setPenWidth(6)
    penUp()
    setPos(-80, -160)    # untere linke Ecke
    penDown()
    # Balken proportional zu getFuel() (0–200)
    moveTo(-80 + getFuel() * 0.8, -160)
    penUp()

setCustomDrawer(meinDrawer)
Warum gibt es am Rand des Fensters noch Abstand?

Ab v0.9.5 ist das schwarze Fenster die gesamte Spielfläche — es gibt keinen erzwungenen Randabstand mehr. Wenn du noch Abstand siehst, liegt das an deinen eigenen Koordinaten.

Das optionale Playfield hat standardmäßig Margin 0 und deckt sich damit exakt mit dem Fenster. Du kannst es zum Debuggen sichtbar machen:

setShowInnerPlayfieldBorder(True)
setPlayfieldMargin(10, 10, 10, 10)   # optionaler Abstand
Hinweis: HUD-Text und gturtle-Linien können nahe am Rand leicht abgeschnitten wirken, weil der Java-Swing-Zeichenbereich eine kleine innere Pufferzone hat. Das ist kein Fehler der API.
6. Typische Fehler
Warum wird mein Lander nicht gezeichnet?
  • setCustomDrawer(meinLander) vergessen — oder nach init() aufgerufen
  • Funktion heißt anders als beim Aufruf von setCustomDrawer()
  • penDown() vergessen — die Turtle zeichnet nur bei penDown()
  • Lander außerhalb des sichtbaren Bereichs gezeichnet (Koordinaten zu groß)
  • Fehler in der Zeichenfunktion: TigerJython zeigt ihn in der Konsole an
Tipp: setPos() teleportiert immer ohne Linie. Für Linien: penDown() + moveTo() oder forward().
Warum reagiert meine Taste nicht?
  • Spielfenster hat keinen Tastaturfokus — einmal ins schwarze Fenster klicken
  • Falscher Keycode: print(keyCode) in den Handler einfügen um den richtigen Code zu ermitteln
  • handleKey nicht an init() übergeben: init(handleKey)
  • Spiel läuft nicht — start() nicht aufgerufen
Warum endet das Spiel sofort?
  • Lander startet bereits auf dem Terrain — Startposition zu niedrig. Prüfe setStartPosition(x, y)
  • setGroundLevel zu hoch gesetzt
  • setGameEndFunction gibt sofort True zurück
  • Treibstoff = 0 von Anfang an (setFuel(0) versehentlich aufgerufen)
Warum stimmt meine CollisionProbe nicht?

Die Probe wird relativ zur Lander-Mitte berechnet: collisionY = landerY + north − south

  • Probe sichtbar machen: setShowCollisionProbe(True)
  • Liegt der Punkt zu hoch oder zu tief? → south-Wert anpassen
  • Nach setLanderSize() wird die Probe automatisch auf south = landerSize/2 zurückgesetzt
setCollisionProbe(0, 0, 14, 0)    # Probe 14 Einheiten unterhalb der Mitte
setShowCollisionProbe(True)        # zum Testen sichtbar machen
Warum erscheint mein Terrain nicht dort, wo ich es erwartet habe?
  • Terrain-Werte sind relative Höhen, keine absoluten y-Koordinaten
  • Die Grundlinie liegt bei groundLevel (Standard: relativ — 95% der halben Fensterhöhe, bei 400px = –190, bei 800px = –380). Terrain-Werte werden addiert. Manuell setzen mit setGroundLevel().
  • Zu hohe Terrain-Werte können den Lander-Startbereich überschreiten
  • setTerrain() muss vor init() aufgerufen werden
Warum wird mein HUD nicht angezeigt?
  • Liste leer: setHudItems([]) blendet das HUD aus
  • Ungültiger Schlüssel: erlaubt sind fuel, vSpeed, hSpeed, height, x, gravity, collisionX, collisionY, frameTime, rotation, scale, paused, abortKey
  • HUD-Position außerhalb des sichtbaren Bereichs: setHudPosition(10, 30) prüfen
Warum ist mein Lander plötzlich riesig oder winzig?
  • setScale() mit zu großem oder kleinem Wert aufgerufen — Standard ist 1.0
  • setLanderSize() zu groß gesetzt
  • Beide Einstellungen addieren sich: Scale 2.0 bei LanderSize 60 = sehr groß
setScale(1.0)      # zurücksetzen auf Standard
setLanderSize(30)  # Standard-Kantenlänge
Warum dreht sich mein Lander in die falsche Richtung?

Positive Winkel drehen gegen den Uhrzeigersinn, negative im Uhrzeigersinn. Die Winkelkonvention von gturtle weicht von mathematischen Standards ab.

Einfacher Test: setRotation(90) — dreht den Lander nach links. setRotation(-90) — nach rechts.

Warum ist mein Lander auf dem Kopf oder gespiegelt?

Das passiert meistens wenn forward(), left() und right() mit falschen Winkelwerten kombiniert werden. Die Winkelkonvention in gturtle ist:

  • setHeading(0) = nach oben (nicht rechts wie in der Schulmathematik!)
  • setHeading(90) = nach rechts · 180 = unten · 270 = links
  • Drehrichtung: right(90) dreht im Uhrzeigersinn

Einfachste Lösung: Verwende stattdessen setPos() und moveTo() mit direkten Koordinaten — das ist weniger fehleranfällig:

# Statt: setHeading(0); forward(20); right(90); ...
# Besser: direkte Koordinaten — (0,0) = Lander-Mitte, +Y = oben
penUp()
setPos(0, 15)       # Spitze oben
penDown()
moveTo(-18, -15)    # unten links
moveTo(18, -15)     # unten rechts
moveTo(0, 15)       # schließt Dreieck
penUp()
Merkhilfe: Im Lander-Koordinatensystem gilt wie in der Mathematik: positive Y-Werte zeigen nach oben, negative nach unten.
Warum funktioniert mein Levelwechsel nicht?
  • setTerrain() und setLandingZone() vor restart() aufrufen — nicht danach
  • restart() setzt Lander-Position, Treibstoff und Spielstatus zurück, aber nicht die Terrain-Konfiguration
  • Nach start() nochmal start() aufrufen für das nächste Level
Warum flackert meine Darstellung?

Das ist ein gturtle/TigerJython-internes Problem mit dem Repaint-Mechanismus. Die Bibliothek versucht es durch manuelles Repaint-Management zu verhindern.

  • Falls noch sichtbar: setDelay() leicht erhöhen
  • Prüfen ob getFrameTime() sehr niedrig ist — zu kurze Frames können Flackern verursachen
  • Keine externen Schleifen oder delay()-Aufrufe in der updateFunction
Warum sehe ich nur einen schwarzen Bildschirm?
  • start() nicht aufgerufen — das Fenster ist offen, aber die Schleife läuft nicht
  • Fehler in init() — Konsole prüfen
  • Terrain und Lander außerhalb des sichtbaren Bereichs (Koordinaten weit außerhalb)
  • setCustomDrawer mit leerer Funktion registriert
Warum stürzt mein Spiel direkt nach dem Start ab?
  • Fehler in der updateFunction oder dem setCustomDrawer — die genaue Fehlermeldung steht in der TigerJython-Konsole
  • Häufig: Variable ohne global verändert
  • Häufig: Eigene Datei heißt lunarlander.py — Python importiert dann sich selbst statt der API
  • Häufig: Division durch null oder Index außerhalb einer Liste
  • Häufig: Funktion an setCustomDrawer() übergeben, die Parameter erwartet
# Falsch — update erwartet keinen Parameter:
def update():
    ...
setUpdateFunction(update)   # korrekt — keine Klammern beim Übergeben
Warum wird meine Animation nicht sichtbar?
  • Zustandsvariable wird nicht mit global verändert
  • setUpdateFunction oder setCustomDrawer vergessen — oder nach init() aufgerufen
  • Zeichenbedingung immer True oder immer False — print() zum Prüfen
  • Zeichnung wird gezeichnet, aber sofort durch den schwarzen Hintergrund überschrieben — passiert nicht wenn die Zeichnung in setCustomDrawer ist
Warum bekomme ich bei from lunarlander import * einen Fehler?
  • Die Datei lunarlander.py liegt nicht im selben Ordner wie dein Programm
  • Der Dateiname ist falsch geschrieben (Groß-/Kleinschreibung beachten)
  • In TigerJython: aktuelles Verzeichnis prüfen — die py-Datei muss im selben Ordner liegen
Häufiger Anfängerfehler: Die eigene Datei heißt ebenfalls lunarlander.py.
Python sucht beim Import zuerst im aktuellen Ordner und findet dann die eigene Datei statt der API — init() und alle anderen Funktionen fehlen und es kommt sofort ein Fehler.
Lösung: Eigene Datei umbenennen, z.B. in mein_lander.py.
7. Projektideen
Was wäre ein gutes eigenes Projekt?

Die LunarLander-API lässt sich für sehr verschiedene Projekte nutzen:

Erweiterungen des LunarLanders:

  • Mehrere Levels mit steigender Schwierigkeit
  • Punktesystem mit Treibstoff-Bonus und Zeitbonus
  • Animierter Lander mit Schub-Flamme
  • Eigenes Terrain-Design

Völlig andere Spiele mit der Engine:

  • Pong: Gravity=0, kein Terrain, Custom Drawer für Schläger und Ball
  • Flappy Bird: seitliche Bewegung mit Hindernissen im Custom Drawer
  • Fangspiel: zwei Objekte, eines durch Update-Logik gesteuert

Kür-Ideen für Fortgeschrittene:

  • Zeitlimit via setGameEndFunction()
  • Mehrere Landeplätze mit unterschiedlichen Punktwerten
  • Rotation in der Update-Funktion als spielerisches Element
  • Eigene Highscore-Anzeige nach dem Spiel
Alle Funktionen: API-Dokumentation  ·  Tipp: Starte immer mit dem einfachsten funktionierenden Grundgerüst und erweitere schrittweise. Jeder Hook einzeln testen, bevor du alles kombinierst.