A Program for the Arduino Uno R3 and a 16x16 Neopixel Matrix to play Dino Run with a special 'easteregg'
The Team consists out of
- s86451
- s86423
- Arduino Uno R3
- LCD Display (2x16 HD44780) -> display for Score
- Buzzer
- WS2812E LED Panel (16x16) -> main display
- Taster mit RC Tiefpass
Der Code ist soweit wie möglich nicht blockierend gehalten und stattdessen darauf angewiesen, dass die Hauptschleife nicht zu lange für einen Durchlauf braucht, um Timings einzuhalten.
Die Hauptschleife lässt sich grob in zwei Teile einteilen:
- Renderer und Logik
- Sound
Sprites werden in den Framebuffer gerendered, sobald die entsprechende Drawfunktion aufgerufen wird. Der Framebuffer wird dann am Ende des Durchlaufs des Renderer- und Logikblocks mit der FastLED-Bibliothek an die LEDs gesendet.
Die Sprites werden aus Platzgründen mit einer Farbtiefe von 1bpp gespeichert, wobei ein Pixel entweder farbig oder transparent ist. Die Farbe wird bei jedem Drawcall angegeben, was theoretisch eine Wiederverwendung von Sprites für verschiedene Objekte erlauben würde, dies wird aber nicht genutzt.
Für die Endanimation wurde eine Farbtiefe von 2bpp ohne Transparenz genutzt, um etwas mehr Farben darstellen zu können.
Größere Ressourcen (Die Endanimation und die Musik dazu) werden aus Platzgründen nur im Programmspeicher gespeichert und nicht wie es standardmäßig der Fall ist beim Start in den SRAM geladen. Stattdessen müssen diese beim Zeichnen oder Abspielen immer Stückweise aus dem Programmspeicher gelesen werden. Die im Spiel genutzten Sprites werden normal in den SRAM geladen, da diese nur sehr wenig Speicher belegen (insgesamt 7 Bytes).
Kollisionen werden geprüft, indem geprüft wird, ob sich die bounding Boxes der Sprites überlappen, der Inhalt der Sprites ist dabei nicht relevant. Dies kann in manchen Fällen dazu führen, dass eine Kollision erkannt und damit das Spiel beendet wird, wenn es optisch keine geben sollte.
Eingaben vom Taster werden durch einen Interrupt entgegengenommen, und durch ein Flag gespeichert, aber nur behandelt, wenn ein Frame gezeichnet wird.
Die angestrebte Framerate ist einstellbar, damit im Spiel eine höhere, beim Abspielen der Animation eine niedrigere genutzt werden kann. Dies spart etwas weiteren Speicherplatz, da die Animation so weniger Frames beinhalten muss.
Die Animation wurde mit Hilfe von ffmpeg aus einem vorhandenen Video runterskaliert und auf vier Farben reduziert, und anschließend mit einem Pythonskript in eine CPP-Sourcedatei umgewandelt, welche die Daten enthält.
Die Hindernisse werden dynamisch generiert, der Seed für den Zufallszahlengenerator wird dabei beim Programmstart mit einem von einem schwebenden Analogiengang gelesenen Wert initialisiert.
Die X-Position und der Typ der Hindernisse werden in einem Ringpuffer abgelegt, welcher bei Bedarf aufgefüllt wird. Somit können mehrere Hindernisse gleichzeitig angezeigt werden.
Sound wird unabhängig vom der Spielelogik und dem Renderer behandelt. Die Kommunikation mit der Logik läuft dabei über ein paar Flags ab.
Wenn ein Sound gestartet wurde wird bei jedem Durchlauf der Hauptschleife geprüft, ob der aktuelle Ton fertig ist, und dann gegebenenfalls der nächste Abgespielt, dadurch kann nur ein Ton gleichzeitig abgespielt werden. Im Falle der Musik können auch Pausen nach den Noten eingefügt werden.