Projects Case Study
Star Sprinkler: ein Pixel-Shooter, in dem nichts stirbt
Ein Top-Down-Shooter für Kinder von 6 bis 10 Jahren, in dem Gegner in Konfetti zerplatzen. Nativ in Kotlin, Canvas-Engine ohne Fremdbibliotheken, 209 Tests.
Der Shooter, den ich nicht gebaut habe#
Ich wollte ein Spiel bauen, das ein Sechsjähriger allein öffnen und verstehen kann – und das eine Zehnjährige nicht langweilig findet. Das klingt nach einer Balance-Frage. In der Praxis war es zuerst eine ganz andere Frage.
Ein Top-Down-Shooter ist eine strenge Form. Man schießt auf Dinge, die sich bewegen. Kinder lieben genau dieses Spiel. Aber dieselbe Mechanik hat eine Nebenwirkung, über die man bei diesem Genre selten spricht: Sie lehrt, dass Dinge zerstört werden.
Also stand am Anfang des Projekts keine Gameplay-Liste, sondern eine Designregel:
Nichts in diesem Spiel stirbt, blutet oder wird „getötet“.
Alles Weitere ist Konsequenz dieser einen Zeile.
Was daraus im Spiel wurde#
Man steuert ein lächelndes, sternförmiges Bewässerungs-Schiff und saugt kosmischen Staub, Weltraumplumen, Roboterkäfer und Sternscherben auf. Getroffen platzt Gegner in einem Konfettischauer auf, und die Tonhöhe steigt bei jedem Erfolgserlebnis.
Drei Zonen mit je zwölf Etappen, am Ende ein freundlicher Boss:
| Zone | Inhalt |
|---|---|
| Cosmic Cobalt | Staub und Blasen – die Rampe für Einsteiger |
| Bubble Bloom | Roboterkäfer und versteckte Sternscherben |
| Robot Garden | alles zusammen, weiterhin sanft |
Fünf Power-ups (Bubble Shield, Twin Twinkle, Zoom Boots, Star Magnet, Star Heart), ein Glitzer-Meter als Zielanzeige, und ein Spielstand, der ausschließlich aus Fortschritt und Lautstärke besteht.
Die Steuerung ist bewusst minimal: zwei Stick-Bereiche, deren Basen unter dem Daumen springen, plus eine Zielhilfe, die sanft den nächsten Gegner ansteuert. Auch mit einem Daumen spielbar – was für Sechsjährige der Unterschied zwischen „ich kann das" und „ich gebe auf" ist.
Kein Asset, keine Bibliothek, keine Game Engine#
Die zweite Designregel lautet: keine Dateien im Projekt außer Quellcode.
Das bedeutet:
- Grafik entsteht zur Laufzeit in einem kleinen Polygon-Rasterizer und wird auf der GPU eingefärbt. Kein PNG, keine fehlenden Assets, gestochen scharf auf jeder DPI.
- Audio wird aus Puls-, Dreieck- und Sägezahn-Oszillatoren plus Rauschpercussion synthetisiert: 14 Effekte und 4 Chiptune-Schleifen.
- Frame-Loop ohne Allokation, mit festen Entity-Pools – der Garbage Collector kann mitten im Spiel nichts ruckeln.
- Adaptive virtuelle Auflösung: feste virtuelle Höhe, damit Steuerung und Geschwindigkeit auf einem 16:9-Handy genauso funktionieren wie auf einem 4:3-Tablet.
Das Ergebnis ist eine Release-APK von rund 196 KB und ein AAB von etwa 232 KB. Keine AndroidX-Bibliothek, keine Runtime-Abhängigkeit – nur das Android-Framework und die Kotlin-Standardbibliothek.
Der Simulationskern ohne Android#
Die Entscheidung, die mir am meisten gebracht hat: Die gesamte Spiellogik kennt kein
android.*.
Der Kern rechnet gegen ein abstraktes RenderTarget. Dadurch lässt sich die vollständige
Simulation auf einem normalen JVM headless testen – inklusive deterministischer Wiederholung
und Zeitschritten, die nichts vertragen.
// Kernidee, verkürzt: keine Android-Typen, ein abstraktes Ziel
interface RenderTarget {
fun drawSprite(sprite: SpriteId, x: Float, y: Float, tint: Int)
}
fun step(world: World, dtSeconds: Float, target: RenderTarget): StepResultDas ist keine Abstraktion um der Abstraktion willen. Es ist der Grund, warum ich einen 10-Minuten-Dauerlauf automatisiert prüfen kann, warum ein Fehler reproduzierbar ist und warum die Physik ohne Emulator und ohne Gerät geprüft werden kann.
Regeln, die der Build überwacht#
Ein Versprechen in einem README ist ein Wunsch. Ein Test ist eine Zusage. Deshalb liegen die inhaltlichen Regeln als Tests im Projekt:
| Suite | Tests | Wofür |
|---|---|---|
WorldSimulationTest |
37 | Spielschleife, Dauerlauf, Determinismus, UI-Taps |
AudioSpecTest |
36 | Wellenformen, Hüllkurven, klickfreie Grenzen, nahtlose Schleifen |
LayoutResolutionTest |
31 | 11 Bildschirmformen von 16:9 bis 1:1 |
InputSystemTest |
26 | Totzonen, Daumenreichweite, Multitouch |
LevelTableTest |
26 | Schwierigkeitsgrenzen, Kurvenverlauf, Vokabular |
ChildSafetyAuditTest |
23 | Compliance-Gate über das echte Manifest |
SaveCodecTest |
19 | Round-Trip plus kaputte und abgeschnittene Spielstände |
PerfBudgetTest |
10 | Frame-Budget, Allokationsfreiheit, Pool-Grenzen |
Das interessanteste ist LevelTableTest. Er schlägt fehl, wenn in Bezeichnern für Gegner
oder Power-ups gruselige Vokabulatur auftaucht. „Getötet" darf in diesem Projekt nicht
vorkommen – auch nicht in einem Variablennamen für ein Feature, das ich später gebaut habe.
Ebenso hart gedeckelt sind Gegnergeschwindigkeit, Lebenspunkte und die Anzahl gleichzeitiger Gegner, und die Difficulty-Kurve darf zwischen zwei Etappen nie um mehr als 45 Prozent springen.
209 Tests, alle grün.
Offline als Beweis, nicht als Zusage#
Die App fordert keine einzige Berechtigung an – nicht mal VIBRATE. Damit kann das
Manifest keine Verbindung öffnen, selbst wenn der Code es versuchte. ChildSafetyAuditTest
liest dabei das echte Manifest, nicht eine Beschreibung davon.
Gespeichert wird eine einzige SharedPreferences-Datei im privaten Bereich: Spielstand und
Lautstärken. Keine Gerätekennung, keine Werbe-ID, kein Konto, keine Analyse.
Was ich dabei gelernt habe#
Constraints sind die beste Designhilfe, die es gibt. Die Regel „nichts stirbt" hat mir mehr kreative Entscheidungen abverlangt als jede Feature-Liste. Sie hat die Sprache geprägt, den Sounddesign-Ansatz, die Gegnernamen und die Sterne-Logik.
Eine Game Engine hätte die falsche Frage gestellt. Unity oder libGDX hätten Assets,
Berechtigungen und eine zwanzigfach größere APK gebracht. Die Frage, die sich hier stellte,
war stattdessen: Reicht ein Rasterizer auf Canvas? Ja.
Kompromisslose Vorsicht bei der Kindersicherung ist billig. Keine Berechtigung im Manifest ist eine Zeile. Ein Audit-Test dafür sind zwei. Die Kombination ist der stärkste Schutz, den man kaufen kann, ohne etwas zu kaufen.
Grenzen#
Der Ein-Daumen-Shooter bleibt Ein-Daumen-Shooter: mit zwei Fingern ist die Steuerung bewusst unterutilisiert. Das Spiel ist plattformgebunden an Android, und die 209 Tests laufen auf der JVM – echtes Geräteverhalten prüfen sie nicht ab.
Und es ist noch nicht veröffentlicht. Die App ist fertig gebaut und geprüft; was fehlt, sind die Dinge, die kein Code erledigen kann: Store-Texte, Screenshots auf echten Geräten, die Freigabeschritte in der Play Console.
Für ein ähnliches Android-Projekt – besonders, wenn es für Kinder gedacht ist und ohne Werbung, Konto oder Tracking auskommen soll – gibt es den Weg über haasarts.de.
Quelle
Dieses Projekt
Star Sprinkler ist Teil meiner Referenzen. Hier geht es zum Projekt auf haasarts.de.