Spieleklassiker für Android nachprogrammieren – ganz einfach

Kistenschieber

11.02.2013
tetris_123RF_magenta10.png

© Magenta10, 123rf.com

Ihren ersten Ausflug in die Welt der Spieleprogrammierung erlebten Sie in Ausgabe 11/2012 an Hand der Kreation des Apple Shooters. Heute wagen wir uns noch ein Stück weiter: Mit Hilfe des App Game Kits erschaffen wir einen Clone des großen Spieleklassikers Sokoban.

Quellcode zum Download

Den Quellcode zum hier vorgestellten Spiel finden Sie unter [5] auf der Android-User-Homepage zum Download.

Das Original Sokoban erschien 1982 in Japan. Als Spektrum Holobyte 1984 eine europäische Variante veröffentlichte, wurde es prompt ein Hit (Abbildung 1 zeigt das Original von damals in einem Dos-Emulator). Noch heute erscheinen Freeware-Adaptionen und Fans tauschen sich im Internet über die Levels von damals aus. Auf den ersten Blick mutet die Idee dahinter erst einmal simpel an: In einem Lagerhaus, welches der Spieler aus der Vogelperspektive sieht, muss er einem kleinen Mann beim Aufräumen helfen. Dazu verschiebt die Figur herumliegende Kisten, so dass sie alle zum Schluss auf gekennzeichneten Ablagefeldern liegen. Doch es gibt ein paar Einschränkungen, wodurch besonders die schwierigeren Levels sehr knifflig werden können: Kisten können nur geschoben, nicht gezogen werden. Das Verschieben ist aber auch nur möglich, wenn sich direkt dahinter ein freies Feld befindet; Mauern oder weitere Holzkisten blockieren also möglicherweise den Weg. Wenn der Spieler einen Fehler macht, und zum Beispiel eine Kiste in einer Ecke einklemmt (von wo sie nur noch durch Ziehen wegzubekommen wäre, was ja nicht möglich ist), muss er auf Knopfdruck den Level neu starten. Diese Knobelei wurde derart beliebt, dass ihre Fans sie im Laufe der Zeit auf etliche Systeme portierten. Doch wie programmieren wir nun unseren eigenen Sokoban-Clone?

Abbildung 1: Das Sokoban-Original aus den 1980er Jahren.

Planungsphase

Zunächst gilt es, ein paar Vorüberlegungen zu treffen. Was für einen Aufbau soll der spätere Bildschirm haben? Im unteren Bereich sollte die App Pfeiltasten-Buttons einblenden, mit denen der Spieler die Figur bewegen kann. Andere Steuermöglichkeiten – wie beispielsweise der Geschwindigkeitssensor – passen zwar zu Renn- und Actiongames, wären aber für eine Knobelei wie diese, bei der es um gezielte, wohlüberlegte Züge geht, eher unpassend. Für den Fall, dass sich der Nutzer in eine verfahrene Situation hineinmanövriert hat, ist ferner ein Level neu starten-Button wichtig. In die obere Hälfte des Bildschirms platzieren wir das eigentliche Spielfeld. Dieses besteht bei Sokoban normalerweise aus einem Raster, innerhalb dessen die einzelnen Spielelemente zeilen- und spaltenweise angeordnet werden. Wieviele Elemente passen später in diesen Bereich hinein? Setzen wir die neueren Smartphone-Modelle als Norm, dann ist der Bildschirm im Portrait-Modus oft 720 Pixel breit und 1280 Pixel hoch (falls die App auf einem Modell mit anderer Auflösung ausgeführt wird, skaliert das App Game Kit [1] dank des Kommandos SetVirtualDisplay(720, 1280) das Bild später trotzdem so, dass es richtig angezeigt wird). Wenn wir für unsere Sprites Grafiken verwenden, die jeweils 60 Pixel breit und hoch sind, wäre dementsprechend eine Matrix von 11 Zeilen zu je 11 Spalten eine gute Wahl: Das Spielfeld benötigt dann 11*60 = 660 Pixel in der Breite (es bleibt also noch etwas Platz für einen seitlichen Rahmen) und Höhe (so dass die untere Hälfte des Schirms frei bleibt für die Steuerungsbuttons). Schauen Sie, um sich diese Aufteilung nocheinmal zu vergegenwärtigen, am besten einmal vorweg den Screenshot unseres späteren, fertigen Spiels an (Abbildung 2).

Abbildung 2: So sieht unser eigener Sokoban-Clone aus. Das Lagerhaus besteht aus 11x11 Spielelementen.
Abbildung 3: Der erste Level ist noch ein ganzes Stück leichter als der zweite.

Nun benötigen wir als letzte Vorarbeit noch nett anzuschauende Sprite-Grafiken für unser Spiel: einen Lagerarbeiter, die Holzkisten, die Mauern und den Boden der Lagerhalle. Beim Apple Shooter in Heft 11/2012 [2] griffen wir dazu noch auf die Seite Opengameart [3] zurück – doch dort finden sich für dieses Projekt keine passenden und gleichzeitig ansprechenden Bilder. Dafür springt diesmal Openclipart [4] ein. Die meisten der dort verwendeten Werke stehen unter der Public Domain / CC0 – Lizenz, Entwickler dürfen diese also ohne irgendwelche Auflagen auch in eigenen Spielen verwenden. Sie können als Vektorgrafiken heruntergeladen oder – in unserem Falle interessanter – direkt online in eine Bitmap-Grafik mit einer wählbaren Wunschgröße (wie etwa 60 Pixel) exportiert werden. Auf der besagten Homepage finden sich auch taugliche Bilder für Steuerungsbuttons; der Erstellung der Sprites für den Boden und die Zielfelder sowie ein paar wenige graphische Anpassungen lassen sich schnell mit einem Malprogramm wie Gimp durchführen. Die fertigen Grafiken (sowie auch den kompletten Quellcode des folgenden Abschnitts) finden Sie als Downloadlink mittels des QR-Codes am Ende von diesem Artikel.

Abbildung 4: Openclipart.org bietet nicht nur lizenzfreie Grafiken an, sondern konvertiert diese auch bei Bedarf.

Die konkrete Umsetzung

Das Listing des Spiels ist aus Platzgründen nicht im Heft abgedruckt, sondern stattdessen in dem oben erwähnten Downloadarchiv enthalten. Für ein gutes Verständnis des Quellcodes rufen Sie diesen optimalerweise an ihrem Computer auf, und legen das Android-User-Heft mit den Erklärungen daneben.

Der Anfang des Programms (Zeile 1-46) führt die üblichen Aktivitäten durch, welche nach dem Neustart einer App anfallen: Er initialisiert Variablen, setzt diese auf bestimmte Startwerte und lädt Grafiken in den Speicher. Doch hier fällt schon ein erster Unterschied gegenüber dem Apple Shooter aus dem letzten Workshop auf: Die Sprite-Grafiken des Spielfeldes werden über ein Array angesprochen (Zeile 30-35). Dabei handelt es sich um eine durchnummerierte "Ansammlung" an Variablen. Statt jeder Variable einen eigenen Namen zu geben, kann der Entwickler auf diese mit Zahlen (Indexwerten) zugreifen. Unser Initialisierungsteil macht jedoch zunächst einmal nichts anderes, als jedes Element dieses Arrays mit den Befehlen "For..Next" in einer Schleife zu durchlaufen. Hierbei wird erstmal jede dieser Variablen mit dem Datentyp Sprite assoziiert.

Ein Array muss vor seiner erstmaligen Verwendung immer in seiner Größe benannt und entsprechend viel Speicher reserviert werden (sogenanntes "Dimensionieren"). Die korrespondierende Anweisung aus Zeile Nr. 10 lautet: Dim Sprites[12,12], was bedeutet, dass ein Array mit dem Namen "Sprites" angelegt wird. Dieses soll zwei Dimensionen (für unsere Zeilen und Spalten) enthalten und jede davon 12 Elemente groß sein. Warum 12, wenn wir für unser Spielfeld nur 11 benötigen? Das App Game Kit fängt schon bei Element 0 zu zählen an. Wir werden aus Gründen der Quellcode-Übersichtlichkeit später das nullte Element nicht benutzen, sondern unser Array immer nur über die Zahlen 1-11 ansprechen. Element Nr. 0 ist trotzdem vorhanden, weswegen es bei der Größe unseres Datenfelds mitgezählt werden muss.

Zeile 49-51 ruft nun die Unterprogramme (Funktionen) "LevelNeuLaden()" und "LevelNeuZeichnen()" auf. Für die Umsetzung von diesen muss sich der Entwickler nun erstmal darüber im Klaren sein, wie er die Inhalte seiner Level (also welche Spielfigur wo steht) intern speichert (sogenanntes "Kodieren"). Wir werden dafür wie folgt vorgehen: Ein (diesmal) eindimensionales Array aus 11 Elementen speichert ebensoviele Zeichenketten, welche jeweils eine Zeile des Spielfeldes repräsentieren. Jede einzelne Zeile umfasst wiederrum 11 Zeichen, von denen jedes den Inhalt einer Spielfeld-Spalte speichert. Schauen Sie sich hierzu zum besseren Verständnis Abbildung 5 und (als Beispiel) Zeile 215 an: Leveldaten$[8] = "# $ ..#"Hier nutzen wir willkürliche Zeichen als Platzhalter für Spielinhalte. Dabei verwendet diese App zu internen Speicherung # als Kodierung für eine Mauer, $ für eine Kiste und .für das Zielfeld einer solchen Truhe. Die achte Zeile dieses Levels beschreibt also, dass in dieser Reihe links und rechts Mauern entlang laufen, relativ mittig eine Kiste herumliegt und sich vor der rechten Mauer zwei Zielfelder befinden. Die Funktion LevelNeuLaden() definiert auf diese Weise die Inhalte des Levels, während LevelNeuZeichnen() die Befehle enthält, um an Hand der gewählten Kodierung die richtigen Sprite-Grafiken für das Spielfeld zu setzen. Sie werden sich nun fragen, warum für die interne Speicherung der Levelinhalte nicht ebenfalls ein zweidimensionaler Array verwendet wird (wie vorher bei der Speicherung der Verweise auf die Bilder), sondern ein eindimensionaler, welcher Zeichenketten nutzt? Dies hat rein pragmatische Gründe: Auf die Weise kann der Entwickler leichter neue Levels entwerfen, indem er einfach neue Zeichenketten für weitere Levels in die Funktion LevelNeuLaden() hineinschreibt.

Abbildung 5: Die linke Seite zeigt, mit welchen ASCII-Zeichen unsere App intern die Level kodiert. Rechts ist zu sehen, wie dasselbe Level später im fertigen Spiel aussieht.

Zeile 53-79 lässt der Initialisierungsphase nun die Hauptschleife des Spiels folgen: So lange die App läuft, wird immer wieder erneut überprüft, ob der Nutzer den Bildschirm berührt hat. Falls ja: Ist einer der Pfeilbuttons davon betroffen? Dann rufe das Unterprogramm SpielerBewegungAngefordert(ZielPositionX,ZielPositionY) auf (Zeile 60-71). Ist stattdessen der "Level Neu starten"-Button gedrückt worden? Dann überschreibe mit Hilfe des Unterprogramms LevelNeuLaden() den aktuellen Inhalt von Leveldaten$[] mit der Ausgangssituation dieses Levels.

Was passiert nun, wenn der Nutzer eine Bewegung der Spielefigur "angefordert" hat? Die Funktion SpielerBewegungAngefordert(ZielPositionX,ZielPositionY) (Zeile 82-92) überprüft, ob es der Spielfigur überhaupt möglich ist, sich dort hin zu begeben. Dafür wird aus Leveldaten$ das Zeichen abgerufen, welches die Zielposition repräsentiert. Handelt es sich dabei um ein Leerzeichen, also dem, was in unserer Kodierung einem leeren Feld entspricht? Kein Problem, dann rufe das Unterprogramm BewegeSpieler(ZielPositionX,ZielPositionY) (Zeile 95-111) auf. Dieses verschiebt das Spielerzeichen in Leveldaten$ entsprechend, der anschließende Aufruf von LevelNeuZeichnen() bringt die zunächst nur in der internen Speicherung vorgenommenene Änderung auch auf den Bildschirm.

Nun kann es vorkommen, dass der Spieler eine Bewegung zu einem Zielfeld anfordert, auf dem eine Kiste steht – er will diese also offenbar verschieben. In dem Fall prüft nach einem entsprechenden Aufruf die Funktion KistenBewegungAngefordert(QuelleX,QuelleY,ZielX,ZielY) erst einmal, ob die Kiste in diese Richtung verschiebbar ist, oder ob diese durch eine in dieser Bewegungsrichtung dahinterstehende Wand oder zweite Kiste blockiert wird. Ob das erwünschte Verschieben von Erfolg gekrönt ist, erfährt das Unterprogramm SpielerBewegungAngefordert über den Rückgabeparameter von KistenBewegungAngefordert, und kann somit gegebenenfalls der verschobenen Holzkiste die Spielfigur hinterherfolgen lassen (siehe hierzu die Zeilen 88-89 und 113-121).

An welcher Stelle überprüft die App nun, ob schon alle Kisten auf Zielfeldern stehen – dass aktuelle Rätsel also gelöst ist? Dies wird innerhalb der Funktion "LevelNeuZeichnen" realisiert. Jedes Mal, wenn ein anderer Teil des Codes dieses Unterprogramm aufruft, wurde gerade etwas an den Inhalten des Spielfelds geändert – also hat die Spielfigur potentiell gerade eine weitere Kiste auf ein Zielfeld geschoben. Dies lässt sich daran prüfen, ob im Leveldaten$-Array nirgendwo mehr die Kodierungszeichen für unbelegte Zielfelder auftauchen (also das Kodierungszeichen "." für 'Leeres Zielfeld' oder "+" für 'Zielfeld, auf dem zwar der Spieler, aber keine Kiste steht'). Ist dem so, dann lädt die App nun das nächste Rätsel. Außer dies war schon das letzte Level – dann startet das Spiel wieder komplett von vorne bei Level Nr. 1 (siehe dazu die Zeilen 139, 154-156, 164-168 & 173-180).


Android User Shop

Einzelne Ausgabe
 
Express-Kauf als PDF
Preis € 0,99
(inkl. 19% MwSt.)

Ähnliche Artikel

  • Workshop: Android-Spiele selbst gemacht

    Wollten Sie schon immer einmal selbst ein kleines Android-Spiel erstellen? Mit dem "App Game Kit" gelingt das auch Anfängern, die über keine Vorkenntnisse verfügen, sehr schnell. Bereits am Ende dieses Workshops wird Ihnen Ihre erste eigene Kreation von Ihrem Smartphone entgegenleuchten.

  • Android-Spiele mit Monkey entwickeln

    In Ausgabe 11/2012 zeigten wir Ihnen, wie Sie mit dem App Game Kit unkompliziert ein Android-Spiel erstellen. Zu diesem Kit existiert jedoch auch ein Konkurrenzprodukt: Monkey Coder. Wir stellen den Monkey vor.

  • Android-User-Spielesammlung

    Manchmal sind es kleine unscheinbare Games, die zwischendurch wirklich Spaß machen. Android User hat 66 freie Android-Spiele in einer Collection zusammengestellt. Hier stellen wir die wichtigsten vor.

  • Neue Spiele aus der Tegrazone

    Der vergangene Sommer hat uns auch ein paar neue Spiele aus der Tegrazone von Nvidia beschert. Wir stellen die Titel Choplifter HD, Bounty Arms, Skiing Fred und Chuck's Challenge vor.

  • Neue Spiele bei Google Play

    In dieser Rubrik stellen wir Ihnen neue oder aktualisierte Spiele aus Google Play vor, die uns besonders gut gefallen.

comments powered by Disqus

Aktuelle Ausgabe

Neueste Artikel

Google Kamera: Neue Kamera-App von Google für alle Android-Geräte ab Version 4.4

Google hat gestern eine neue App in den Play Store eingestellt. Die Google-Kamera-App ist eine Weiterentwicklung der Standard-Kamera-App von Android und ersetzt auf Nexus-Geräten automatisch die bisherige Kamera. Die App lässt sich aber auch auf jedem beliebigen Android-Gerät mit Android 4.4 installieren.

Cal: Any.do Kalender: Schicker Kalender mit Facebook-Anbindung

Gehören Sie zu den 80 Prozent Android-Nutzern, die einfach den vorinstallierten Kalender nutzen oder sind Sie auf der Suche nach einer besseren, vielleicht einfach nur schöneren App? Falls letzteres auf Sie zutrifft, dann sollten Sie sich mal Cal Any.do Kalender anschauen. Dieser Kalender macht sogar Spaß!

Mit F-Secure Freedome VPN sicher per VPN im Internet unterwegs

Mangels ausreichender Datenflatrate benutzen viele Anwender auch im Inland freie WiFi-Hotspots in Cafes, öffentlichen Gebäuden oder vom Nachbarn. Im Ausland ist man durch die mageren Roaming-Pakete fast immer ans WLAN im Hotel gebunden. Doch kann man diesen Netzen vertrauen? Eine Anbindung ein virtuelles privates Netzwerk (VPN) sorgt für mehr Sicherheit. Wir haben die App von F-Secure getestet.