Wie man einen Shmup für die Apple Watch entwickelt

Anleitung für einen Shmup auf der Apple Watch: virtuelle Auflösung, Digital Crown, Kollisionen mit Rechtecken, Pooling, Level als Zeittabellen, Generator.

Pixelszene auf grünem Display: links ein Schiff, das auf Kometen, Raketen und einen Boss rechts schießt

Ein Shmup (Shoot 'em up, ein Shooter, in dem das eigene Schiff gegen Wellen von Gegnern fliegt) auf der Apple Watch hat drei Probleme: ein kleines Display, einen Finger, der das halbe Spiel verdeckt, und Uhren in verschiedenen Größen. Drei Entscheidungen lösen sie: Das ganze Spiel läuft auf einem festen Raster von 84 × 48 Punkten, Kollisionen werden von Hand mit Rechtecken geprüft, und Level sind Tabellen mit Zeitpunkten statt Code. Gesteuert wird mit der Digital Crown, geschossen wird automatisch.

Die Beispiele stammen aus Space Impact Watch, einem Remake des Shooters Space Impact, der im Jahr 2000 auf dem Nokia 3310 erschien. Das Spiel läuft auf der Uhr sowie auf iPhone und iPad. Technisch basiert es auf SpriteKit, dem 2D-Spieleframework von Apple, die Prinzipien gelten aber für jeden Shmup auf einem kleinen Bildschirm.

Feste virtuelle Auflösung 84 × 48

Legen Sie zuerst fest, in welchen Einheiten das Spiel rechnet. Mit Bildschirmpunkten ist die Welt auf jeder Uhrengröße anders groß. Besser ist eine kleine virtuelle Auflösung, in der alles berechnet wird. In unserem Spiel sind das 84 × 48, genau wie beim Display des Nokia 3310. Positionen, Geschwindigkeiten, Spritegrößen und Flugbahnen der Gegner sind in diesen Einheiten angegeben. Beim Start der Szene wird einmal berechnet, wie viele Bildschirmpunkte ein virtuelles Pixel hat (Szenenbreite geteilt durch 84), und alles wird nur damit multipliziert.

Spielszene mit sichtbarem Raster aus 84 × 48 virtuellen Pixeln
Ein Level auf dem Raster 84 × 48. Jedes Quadrat ist ein virtuelles Pixel, das die Uhr als Block aus mehreren echten Punkten zeichnet.

Was Sie damit gewinnen:

  • Gleiches Aussehen auf jedem Gerät. Das Spiel sieht auf jeder Uhrengröße und auf dem iPad gleich aus, nur die Blockgröße ändert sich.
  • Leveldesign mit kleinen ganzen Zahlen. Ein Gegner fliegt auf Höhe 13, eine Rakete biegt bei x = 20 ab.
  • Einfache Grafik. Die Sprites sind einfarbig und in zehnfacher Größe gezeichnet, ein virtuelles Pixel ist im Quellbild also ein Block von 10 × 10. Ihre Farbe bekommen sie erst im Spiel: in hellen Leveln schwarz auf grünem Hintergrund, in dunklen umgekehrt.

Steuerung mit der Krone und automatisches Feuer

Auf der Uhr verdeckt ein Finger das halbe Spiel, deshalb wird das Schiff nicht per Touch gesteuert. Die Bewegung übernimmt die Digital Crown: Eine Drehung bewegt das Schiff nach oben oder unten. Bieten Sie eine Einstellung für die Empfindlichkeit und eine Option zum Umkehren der Richtung an.

Geschossen wird automatisch, in unserem Spiel alle 0,4 Sekunden, ab Level vier alle 0,3. Das Display dient nur für eine Geste: Ein Wischen nach rechts löst eine von drei Spezialwaffen aus.

Drei Spezialwaffen: eine senkrechte Linie, eine Lenkrakete und ein Laser, der einen Boss trifft
Drei Spezialwaffen: eine Linie über die ganze Höhe des Spielfelds, eine Lenkrakete und ein Laser.

Auf der Uhr ist die Vibration bei einem Treffer genauso wichtig wie die Animation, eine kleine Explosion auf dem Display übersieht man leicht. Lösen Sie Haptik über eine einzige Schnittstelle aus (Treffer, Gegner zerstört, Boss besiegt), die jede Plattform selbst umsetzt: die Uhr mit einer Vibration, das iPhone auf seine eigene Art. Die Telefonversion hat keine Krone, sondern Tasten auf dem Display, die dieselben Einstellungen für Empfindlichkeit und Richtung verwenden.

Screenshot des Spiels im Apple-Watch-Simulator, das Schiff vor einem Boss
Das Spiel im Apple-Watch-Simulator: das Schiff vor einem Boss.

Der Game Loop in einer Szene

Das ganze Spiel kann eine Szene mit einer Schleife sein:

in jedem Frame:
    alle Objekte bewegen
    Kollisionen prüfen
    je nach Zeit im Level neue Gegner erzeugen

Bauen Sie die Teile in der Reihenfolge ein, in der sie sich am frühesten testen lassen:

  • Schießen und Bewegung des Spielers. Ohne sie lässt sich nichts testen.
  • Gegner und Kollisionen. Dem ersten Gegner reicht es, geradeaus nach links zu fliegen und nach einem Treffer zu sterben.
  • Vibration und Trefferanimationen. Ohne sie merkt der Spieler auf dem kleinen Display nicht, dass er getroffen hat.
  • Hintergrund und HUD. Leben, Punktestand, Anzahl der Spezialwaffen. Wenn das HUD in einem Level stört, verschieben Sie es nach unten.

Am Ende steht eine spielbare Schleife mit Leben, Punktestand, Explosionen, Hintergrund und Respawn des Spielers. Wenn Sie die Spiellogik von Darstellung und Eingabe trennen, können Uhren- und Telefonversion sie gemeinsam nutzen.

Kollisionen mit Rechtecken statt Physik-Engine

SpriteKit hat eine Physik-Engine, und es liegt nahe, sie auch für Kollisionen zu nutzen. Jedes Objekt bekommt einen Physikkörper aus dem Umriss seines Sprites, präzise Kollisionserkennung und eine Kategorie (Spieler, Gegner, Geschoss, Spezialwaffe), und die Engine meldet selbst die Paare, die sich berührt haben. Apple schreibt in der Dokumentation zu Physikkörpern, dass Körper aus dem Alphakanal einer Textur am genauesten und zugleich am rechenintensivsten sind. Wir haben das in unserem Spiel ausprobiert und sind zur Prüfung von Hand zurückgekehrt.

Die Prüfung von Hand ist eine Funktion, die nacheinander Paare von Gruppen durchgeht und Rechtecke vergleicht:

für jedes Paar von Gruppen:
    (Spieler und Gegner, Spieler und Geschosse, Geschosse und Gegner, Geschosse und Geschosse)
    für jedes Objekt A der ersten Gruppe und jedes Objekt B der zweiten:
        wenn Rechteck A sich mit Rechteck B überlappt:
            Treffer auflösen
Zwei Sprite-Paare mit markierten Kollisionsrechtecken, links überlappen sich nur die Ecken der Rechtecke
Links: Die Rechtecke überlappen sich, die Pixel nicht, das Spiel wertet es als Treffer. Rechts: ein echter Treffer.

Die Vorteile: Die Reihenfolge der Prüfungen liegt in Ihrer Hand, und die ganze Regel „was bei einem Treffer passiert“ steht an einer Stelle. Die Ungenauigkeit ist gering, denn auf dem Raster 84 × 48 sind die Sprites so klein, dass Rechteck und echter Umriss nur um wenige virtuelle Pixel abweichen.

Pooling nur dort, wo es sich lohnt

Pooling bedeutet, dass Sie ein Objekt nach seinem Ende nicht löschen, sondern ausblenden und beim nächsten Mal wiederverwenden. Es ist verlockend, alles zu poolen, auch Explosionen und Power-ups. Das hat aber seinen Preis: Jede Kollision und jedes Update muss zuerst prüfen, ob das Objekt überhaupt lebt. Eine vergessene Prüfung reicht, und ein ausgeblendeter Gegner tötet den Spieler oder ein ausgeblendetes Geschoss schießt eine Rakete ab. Als wir in unserem Spiel das Pooling überall entfernt haben, waren es vor allem diese Prüfungen, die wegfielen.

Eine einfache Regel: Poolen Sie, wovon es viel gibt und was immer gleich entsteht. Der Spieler erzeugt beim Start 21 Geschosse und recycelt sie danach nur noch. Gegner und ihre Geschosse werden ebenfalls wiederverwendet, Explosionen und Power-ups entstehen und verschwinden normal.

Level als Zeittabellen

Ein Level muss kein Code sein, eine Liste von Einträgen genügt. Jeder Eintrag sagt, was erscheint, in welcher Sekunde, auf welcher Höhe und wie es sich bewegt. Die Bewegung ist eine Liste von Punkten, die der Gegner mit gleichbleibender Geschwindigkeit abfliegt. Optional kommen Schusszeiten, ein Sturmangriff des Bosses oder ein Erzeuger kleinerer Gegner dazu.

in jedem Frame:
    Zeit = jetzt - Levelstart - Zeit in der Pause
    für jeden Eintrag in der Leveltabelle:
        wenn der Eintrag unbenutzt ist und Zeit > Eintrag.Zeit:
            Objekt laut Eintrag erzeugen
            Eintrag als benutzt markieren
Zeitachse des ersten Levels: von links nach rechts Sekunden, die Höhe der Symbole entspricht der Höhe, in der der Gegner erscheint
Level eins: 39 Einträge in 67 Sekunden. Kometen, dann Raketenwellen, Power-ups (rot) und am Ende der Boss.

Ein so geschriebenes Level lässt sich leicht abstimmen: Eine Welle um eine halbe Sekunde zu verschieben heißt, eine Zahl zu ändern. Auf der Zeitachse sieht man auch den Rhythmus, um den es geht: kurze Welle, Pause, anderer Gegnertyp, Power-up vor einem schwereren Abschnitt.

Eine Rakete fliegt im Zickzack über das Spielfeld, die Knickpunkte der Flugbahn sind markiert
Flugbahn einer Rakete aus Level eins: sechs Punkte, die sie mit gleichbleibender Geschwindigkeit abfliegt.

Der Nachteil zeigt sich bei größeren Änderungen. Das dichteste Level unseres Spiels hat 71 Zeilen, und so eine Tabelle liest sich ohne Editor schlecht. Günstiger ist es, Level als Wellen zu schreiben (eine Gruppe von Gegnern mit einem gemeinsamen Bewegungsmuster), nicht als einzelne Schiffe.

Endlosmodus aus einer Pseudozufallszahl

Für eine Rangliste (bei uns Game Center) brauchen Sie einen Modus ohne Ende. Dort ersetzt ein Generator die handgeschriebene Tabelle und erzeugt sie während des Spiels. Er erzeugt eine 20-stellige Pseudozufallszahl, und einzelne Ziffernpaare entscheiden über eine Welle:

Zahl = nächste 20-stellige Pseudozufallszahl
Typ = Power-up, wenn Ziffern 4 bis 5 > 90, sonst Gegner
Sprite = Ziffern 1 bis 2 modulo Anzahl der Gegnerarten
Pause vor der Welle = Ziffer 3 / 2 Sekunden (mindestens 1)
Höhe = Ziffer 7, umgerechnet auf die Höhe des Spielfelds
Bewegungsmuster = Ziffern 10 bis 11 modulo 10
Schiffe in der Welle = Ziffer 17 / 2 + 1
Geschwindigkeit = Ziffer 13 × (1 + Nummer der Welle / 10), begrenzt auf 20 bis 50
jede dreißigste Welle = Boss, die Erzeugung ruht, solange der Boss lebt

Lassen Sie den Seed (Startwert) des Generators im Spiel fest. Dann bekommt jeder Spieler dieselbe Wellenfolge, und die Punktzahlen in der Rangliste sind vergleichbar.

Zehn kleine Spielfelder, auf jedem ein Bewegungsmuster eines Gegners als gestrichelte Linie
Zehn Bewegungsmuster aus dem Generator. Die genaue Form hängt von der Höhe ab, in der die Welle erscheint.

Wie sich der Modus entwickelt, zeigt eine Simulation des Generators außerhalb des Spiels (300 Wellen, derselbe Seed wie im Spiel). Der erste Boss kommt nach 29 Wellen, also nach 74 Gegnern und 3 Power-ups, nach etwa 130 Sekunden. Die ersten 30 Wellen fliegen mit der Mindestgeschwindigkeit 20, weil eine einzelne Ziffer noch unter der Untergrenze liegt. Danach steigt die Durchschnittsgeschwindigkeit auf 28,5, 37,5 und 41, ab Welle 120 sind die meisten Gegner am Maximum. Etwa jede elfte Welle ist ein Power-up.

Zeitachse der ersten 135 Sekunden des Endlosmodus mit Gegnersymbolen und einem Boss am Ende
Die ersten 135 Sekunden des Endlosmodus laut Simulation des Generators. Die Wellen sind länger und unregelmäßiger als in einem handgeschriebenen Level.

Die Schwäche: Der Generator kennt keinen Rhythmus. Ein handgeschriebenes Level wechselt zwischen Druck und Erholung, der Generator nicht. Die Pause während des Bosses und die steigende Geschwindigkeit helfen etwas, aber die handgeschriebenen Level spielen sich besser.

Grenzen dieses Ansatzes

  • Keine automatischen Tests. Alles wird von Hand im Simulator und auf der Uhr geprüft.
  • Globaler Zustand. Der Großteil des Spielzustands sind globale Variablen einer einzigen Szene. Für ein reines Uhrenspiel ist das bequem, beim Hinzufügen einer iPhone-Version nicht mehr.
  • Kein Leveleditor. Eine Zeittabelle lässt sich in kleinen Schritten gut abstimmen, für größere Änderungen bräuchte sie einen Editor.

Wenn Sie ein Spiel für eine Uhr oder einen anderen kleinen Bildschirm entwickeln und Hilfe brauchen, schreiben Sie uns. Wie wir in einer anderen unserer Apps Rätsel erzeugen, steht im Beitrag über unseren Sudoku-Generator.

← Mehr in Space Impact Watch