Calliope mini - Spaß durch Programmieren

Entwicklungsumgebungen

Regentropfen fangen

Ziel: Das Regentropfenspiel wird objekorientiert auf einer 8x8-Neopixel-Matrix realisiert.
Es wird eine Klasse erzeugt, die alle Attribute und Methoden beinhaltet, die notwendig sind, um einen leuchteneden Punkt auf der Matrix anzuzeigen und zu bewegen.
Attribut Bedeutung
self.__x x-Koordinate
self.__y y-Koordinate
self.__max_x Maximalwert in x-Richtung
self.__may_y Maximalwert in y-Richtung
self.__richtung Enthält die Richtung, in der sich der Punkt beim nächsten Bewege-Befehl bewegt
self.__h Die Farbe (hue) von 0° bis 360° (Farbkreis)
self.__s Die Farbsättigung (saturation). Bei Farben immer 100, bei Grautönen 0.
self.__l Die relative Helligkeit (lightness).
self.__pixel Die eigentliche Neopixel-Instanz.

 

Methode Bedeutung
__init__() Initialisierungsmethode. Die Methode wird autoamtisch aufgerufen, wenn von der Klasse eine neue Instantz erstellt wird. Der Methode müssen alle Startattribute übergeben.
zeige_pixel() Aus den h, s und l-Werten werden die RGB-Werte berechnet. Aus den x- und y-Werten wird die Nummer des Pixels berechnet. Das Pixel wird zum Leuchten gebracht.
loesche()

Diese Methode wird bei allen Bewegungen genutzt. Wenn sich ein Pixel bewegt, muss er an der alten Stelle gelöscht und an der neuen Stelle angezeigt werden. Die Farbwerte des Objektes dürfen dabei nicht geändert werden.
Der aktuelle Helligkeitswert wird in einer Hilfsvariablen (h) gespeichert, die Helligkeit wird auf 0 gesetzt. Damit wird die zeige_pixel()-Methode aufgerufen und der Wert von h wieder dem aktuellen Helligkeitswert zugeordnet.

setze_farbe(h,s,l) Die Attribute h, s und l werden auf die übergebenen Werte gesetzt.
setze_x(x), setze_y(y) Die Methoden überprüfen, ob der übergebene Wert zwischen 0 und dem jeweiligen Maximalwert liegt. Ist das der Fall, wird das aktuelle Pixel gelöscht und das x- oder y-Attribut neu gesetzt.
x, y Geben die aktuellen Werte der Attribute zurück.
setze_richtung(richtung) Setzt das Attribut für die Richtung der nächsten Bewegung. 1: rechts, 2: unten, 3: links, 4 oben
bewege() Es wird zuerst geprüft, ob eine Bewegung in die vorgegebene Richtung möglich ist oder ob der Rand überschritten wird. Ist eine Bewegung möglich, wird das aktuelle Pixel gelöscht, das x- oder y-Attribut geändert und das Pixel an der neuen Position angezeigt.

 

Tropfen erzeugen

Im Projekt wird eine Datei sprite.py erstellt, die alle Methoden und Attribute für das Objekt sprite enthält. Wird eine Instanz der Klasse erzeugt, müssen alle Parameter mit übergeben werden:

wasser=sprite.sprite(x_max,y_max,4,0,240,100,10,np)

x_max und y_max werden vorher auf den Wert 8 gesetzt. np ist eine Instanz der Neopixelklasse.

Durch den letzten Befehl wird der Tropfen sofort aufleuchten. Dazu mmuss aber die entsprechende Methode noch geschrieben werden.

Python_farben

Den Tropfen zeigen

Damit der Tropfen angezeigt werden kann, müssen die HSL-Farbe in RGB-Farben und die x- und y-Werte in die Nummer der LED in der Matrix aufgerufen werden.

Die Farbumwandlung wird in LED mit HSL-Farben ansteuern beschrieben.

Die LEDs werden auf der Matrix von oben links zeilenweise nach unten rechts durchnummeriert. Damit hat jede LED eine Nummer zwischen 0 und 63, in der ersten Zeile von 0 bis 7, in der darunter liegenden von 8 bis 15 usw. Solle die LED mit der Nummer 8 angesprochen werden, hat sie den x-Wert 0 und den y-Wert 1. Die Funktion umrechnen(x,y) gibt dann (1*8)+0=8 zurück.

Die Methode zeige_pixel(self) greift auf die Attribute des Objektes zu (self.__h....), macht daraus die RGB-Farbwerte und lässt das Pixel aufleuchten. Wenn bis hier hin alles läuft, leuchtet nur eine LED in der oberen Zeile in einem Blauton auf.

Python_farben

Im Hauptprogramm (main.py) sieht das dann so aus:

Den Tropfen tropfen lassen

Abbildung des Tropfens auf der Matrix löschen.

Im nächsten Schritt soll der Tropfen nach unten wandern. Dazu ist die Abbildung des Tropfens auf der Matrix zu löschen. Es dürfen aber noch keine Attribute des Objektes verändert werden. Das passiert dann im folgenden Teil.

Dazu wird aus den Objektvariablen self.__x und self.__y die Nummer der LED auf der Matrix bestimmt und die drei Farben ausgeschaltet (0,0,0). Das Objekt selber mit allen Attributen bleibt aber vollständig erhalten.

Python_farben

Richtung ändern

Die Variable self.__richtung enthält die Richtung, in der sich der Tropfen bewegt. Es gelten folgende Festlegungen

  • 1: Links
  • 2: Runter
  • 3: Rechts
  • 4: Hoch

Damit der Tropfen nach unten fällt, schreibt man im Hauptprogramm

wasser.setze_richtung(2)

Python_farben

Einen Schritt tun

Die Methode bewege(self) entscheidet an Hand dem Inhalt der Variablen self.__richtung, was für einen Schritt zu tun ist. Dabei prüft sie, ob ein Rand überschritten wird. Falls das der Fall sein sollte, wird keine Bewegung ausgeführt.

Python_farben

Im Hauptprogramm wird der Wassertropfen erzeugt und leuchtet auf. Dann wird sofort die Richtung geändert. Damit man ihn sehen kann, wartet das Programm 300 ms und bewegt ihn eine Position weiter.

Python_farben

Der Tropfen fällt ganz runter

Setzt man die letzten beiden Befehle in eine unendliche Schleife, fällt der Tropfen bis nach unten und bleibt dort liegen. Da in der Methode bewege() geprüft wird, ob der Tropfen über den Rand läuft, muss man sich im Programm darum nicht mehr kümmern.

Python_farben

Es tropft unaufhörlich

Damit der Tropfen weiter tropft, muss der y-Wert geändert werden. Der y-Wert ist ein Attribut des Objektes und darf nur über eine entsprechende Methode geändert werden. In der Methode wird als erstes geprüft, ob die Ränder überschritten werden. Ist das nicht der Fall, wird die Ansicht des Punktes auf der Matix gelöscht und der y-Wert neu gesetzt.

Da eventuell mehrere Attibute geändert werden sollen (x-Wert, Richtung...), wird der Punkt nicht neu angezeigt. Das muss im Hauptprogramm nach dem Ändern aller Attribute geändert wurden.

Im Hauptprogramm wird die unendliche Schleife entsprechend erweitert.

Python_farben

bunter Regen

Aufgabe: Schreibe eine weitere Methode, die die Farbwerte neu setzt. Nenne die Methode setze_farbe... . Ändere das Hauptprogramm dann so, dass jeder fallende Tropfen einen um 20° verschobenen Farbwert hat. Achte darauf, dass man einen Farbkreis hat, nach Violett also wieder Rot kommen muss.

Die letzte Aufgabe konnte man lösen, wenn man eine neue Variable erzeugt hat, in der der aktuelle Farbwert gespeichert wird. In einer Entscheidung kann man kontollieren, ob der Inhalt der Variablen den Wert 360° nicht überschreitet. Das funktioniert, ist aber nicht nötig. Das Objekt wasser enthält ja selber ein Variable mit dem aktuellen Farbwert.

Man kann in der Klasse sprite eine weitere Methode schreiben, die alle Farbwerte zurückliefert (H, S und L). In return werden die drei Werte durch jeweils ein Komma getrennt angegeben. Damit gibt die Methode ein Tubel zurück!

Python_farben

Im Hauptprogramm wird der Tropfen bis nach unten bewegt. Ist er dort angekommen, wird der Variablen f das Tupel zugewiesen. Die Elemente werden wie in einer Liste angesprochen: f[0], f[1] und f[2].

Python_farben

Der Eimer

Der Eimer ist ein leuchtender Punkt, der mit den Tasten A und B auf der unteren Zeile bewegt wird. Trifft der Wassertropfen den Eimer, zählt es als Punkt, ansonsten wird, beginnend von 5 Leben an, ein Leben runtergezählt. Bei 0 ist Schluss.

Eine einzige Zeile reicht aus, um den Eimer zu erstellen und anzuzeigen. Als Instanz der Klasse sprite hat das Objekt alle Attribute und Methoden erhalten.

Python_farben

Keine Schlafzeit mehr!

Der Eimer soll sofort bei jedem Drücken von A oder B bewegt werden. Da er von der Klasse sprite abgeleitet wurde, ist das kein Problem. Problematisch ist die Wartezeit zwischen der Tropfenbewegung. sleep() bedeudet wirklich Schlafen und dabei kann man nun wirklich nichts machen. Der sleep()-Befehl hat in einem Spiel, wo es auf Schnelligkeit ankommt, nichts zu suchen.

Die zeitgetaktete Abfolge der Bewegung lässt sich eleganter mit der Funktion running_time() realisieren. Der Rückgabewert dieser Funktion ist die Zeit in Millisekunden seit dem Start des Programms.

Vor der dauerhaft-Schleife werden zwei Variable wartezeit und zeit festgelegt. Der sleep()-Befehl zu Beginn der dauerhaft-Schleife wird durch zwei neue Zeilen erstetzt. Die Entscheidung prüft, ob die Systemzeit größer ist also der Wert in der Variable zeit. Wenn das der Fall ist, wird die Variable zeit um den Wert der wartezeit erhöht. Dann wird der Rest (Tropfen fällt eins nach unten...) ausgeführt. Das nächste Mal wird dieser Programmteil ausgeführt, wenn die Systemzeit um die Wartezeit vorangeschritten ist.

Python_farben


Python_farben

Jeder Tastendruck eine Eimerbewegung.

Wenn man einfach schreibt:

sieht das zwar vernünftig aus, funktioniert aber nicht. Der Eimer flitzt bei jedem Tastendruck nach rechts oder nach links, lässt sich aber nicht vernünftig steuern.

Bei jedem Tastendruck darf nur einmal die Methode bewege() aufgerufen werden. Da aber die dauerhaft-Schleife sehr schnell durchlaufen wird, ist die Taste beim nächsten Durchlauf immer noch gedrückt und die Methode bewege() wird sehr oft aufgerufen. Nach dem Drücken einer Taste muss sie also sofort gesperrt werden und die Sperre darf erst beim Loslassen der Taste aufgehoben werden.

Das realisiert man mit zwei Variablen taste_a und taste_b, die vom Type boolean sind und vor der dauerhaft-Schleife auf false gesetzt werden. In der dauerhaft-Schleife werden die Variablen immer zusammen mit dem Tastendruck abgefragt. Im Bild ist der Programmteil für die Taste A zu sehen.

Python_farben

Gefangen oder nicht?

Nun muss noch geprüft werden, ob das Wasser im Eimer gelandet ist. Dazu wird eine weitere Methode geschrieben. Diese muss prüfen, ob von zwei Objekten (Wasser und Eimer) sowohl die x- als auch die y-Koordinaten übereinstimmen.

Der Methode, die sowohl vom Eimer als auch vom Wasser aufgerufen werden kann, wird das andere Objekt übergeben: wasser.beruehrt(eimer) oder eimer.beruehrt(wasser). In beiden Fällen liefert die Methode ein True zurück, wenn der Tropfen in den Eimer gefallen ist.

Python_farben

Bevor man nun die Entscheidung für getroffen oder nicht getroffen einfügt, muss man fragen, was in den beiden Fällen passieren soll. Man kann z.B. im Hauptprogramm zwei globale Variablen definieren:

  • leben = 5
  • spielstand = 0

Dann kann man direkt nach dem Beginn der dauerhaft-Schleife fragen, ob die Anzahl der Leben größer 0 ist. Wenn das der Fall ist, läuft das Spiel weiter, ansonsten wird der Spielstand angezeigt. Der Spielstand sind die gefangenen Regentropfen.

Um das Spiel zunehmend aufregender zu machen, wird bei jedem gefangenen Tropfen die Wartezeit um 10 ms kleiner. Das sollte aber nur bis zu einer Wartezeit von 150 ms gehen, ansonsten hat man kaum noch eine Chance, den Tropfen zu fangen.

 

Python_farben

Auch den Spielstart kann man noch etwas besser gestalten. Das Spiel beginnt nicht sofort, sondern wartet, bis der Spieler die aste A gedrückt hat.

Python_farben

zurück