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.

Beitragsgrafik: dunkles Raster mit dem Titel des Spiels und dem Stern-Icon der App
Eigene Grafik – Haas Arts

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): StepResult

Das 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.