Einfach Genial DS, ein Spiel für den Nintendo DS - Online
Transcription
Einfach Genial DS, ein Spiel für den Nintendo DS - Online
Einfach Genial DS, ein Spiel für den Nintendo DS Wolfgang Schermann DIPLOMARBEIT 05/1/0305/027 eingereicht am Fachhochschul-Masterstudiengang Digitale Medien in Hagenberg im September 2007 c Copyright 2007 Wolfgang Schermann Alle Rechte vorbehalten ii Erklärung Hiermit erkläre ich an Eides statt, dass ich die vorliegende Arbeit selbstständig und ohne fremde Hilfe verfasst, andere als die angegebenen Quellen und Hilfsmittel nicht benutzt und die aus anderen Quellen entnommenen Stellen als solche gekennzeichnet habe. Hagenberg, am 6. September 2007 Wolfgang Schermann iii Inhaltsverzeichnis Erklärung iii Vorwort vi Kurzfassung vii Abstract viii 1 Einleitung 1.1 Geschichte der tragbaren Konsolen . . . . . . . . . . . . . . . 1.2 Unterschiede Konsole – PC . . . . . . . . . . . . . . . . . . . 1.3 Legale Aspekte von Open Source SDKs . . . . . . . . . . . . 1 1 2 3 2 Das System – Nintendo DS 2.1 Kurzer Überblick über den Nintendo DS . . . . . . . . . . . . 2.2 Gründe, warum der Nintendo DS für das Projekt Einfach Genial DS gewählt wurde . . . . . . . . . . . . . . . . . . . . 2.3 NdsLib – Die Hardware-Nahe Bibliothek . . . . . . . . . . . . 2.4 PaLib – Die Framework Bibliothek . . . . . . . . . . . . . . . 2.5 Installationsanleitung . . . . . . . . . . . . . . . . . . . . . . 2.5.1 Benötigte Hard- und Software . . . . . . . . . . . . . . 2.5.2 Einrichten der Entwicklungsumgebungen . . . . . . . . 2.5.3 Makefile . . . . . . . . . . . . . . . . . . . . . . . . . . 2.5.4 Debuggen . . . . . . . . . . . . . . . . . . . . . . . . . 2.5.5 Transfer des Programms auf den NDS . . . . . . . . . 2.5.6 Starten des Programms auf dem NDS . . . . . . . . . 5 5 8 8 9 9 9 10 15 16 21 21 3 Das Spiel – Einfach Genial 3.1 Das Spielprinzip . . . . . . . . . . . 3.1.1 Spielziel . . . . . . . . . . . . 3.1.2 Spielverlauf . . . . . . . . . . 3.2 Geplante Umsetzung . . . . . . . . . 3.2.1 Planung der Texteingabe . . 3.2.2 Planung der Spielfeldanzeige 23 23 24 25 26 29 31 iv . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . INHALTSVERZEICHNIS 3.3 v 3.2.3 Planung der GUI . . . . . . . . . . . . . . . . . . . . . Softwarearchitektur . . . . . . . . . . . . . . . . . . . . . . . . 4 Die Umsetzung 4.1 Dateisystem . . . . . . . . . . . . . . . 4.1.1 Nintendo DS Dateisystem . . . 4.1.2 Gameboy Advance Dateisystem 4.1.3 PaLib Dateisystem . . . . . . . 4.1.4 FAT Dateisystem . . . . . . . . 4.1.5 Verwendung im Spiel . . . . . . 4.2 2D-Grafik . . . . . . . . . . . . . . . . 4.2.1 Logos . . . . . . . . . . . . . . 4.2.2 Hintergründe . . . . . . . . . . 4.2.3 Sprites . . . . . . . . . . . . . . 4.3 Sound . . . . . . . . . . . . . . . . . . 4.3.1 Soundeffekte . . . . . . . . . . 4.3.2 Musik . . . . . . . . . . . . . . 4.4 Zeichenerkennung . . . . . . . . . . . . 4.5 Spielfeld . . . . . . . . . . . . . . . . . 4.6 Computergegner . . . . . . . . . . . . 4.7 Multiplayer . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 32 36 37 37 38 38 38 39 40 42 42 43 53 57 59 60 61 67 76 84 5 Evaluierung 92 5.1 PaLib . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 92 5.2 NdsLib . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 95 5.3 Allgemeine Nintendo DS Programmierung . . . . . . . . . . . 96 6 Schlussbemerkungen 97 6.1 Erreichte Ziele . . . . . . . . . . . . . . . . . . . . . . . . . . 97 6.2 Ausblick . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 97 A Inhalt der CD-ROM A.1 Diplomarbeit . . . A.2 Projekt . . . . . . A.3 Literatur . . . . . . A.4 Sonstiges . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 100 100 100 100 100 B Listings 101 Literaturverzeichnis 107 Vorwort Wenn von Spieleprogrammierung gesprochen wird, meint man normalerweise die Programmierung von Spielen für den Computer. Ein genau so wichtiger Bereich der Spieleprogrammierung ist jedoch auch die Programmierung von Spielen für Spielekonsolen. Während es relativ leicht ist, Informationen über Spieleprogrammierung für den Computer zu bekommen, da es hierfür gute Fachbücher gibt, kann man außerhalb einer etablierten Firma kaum Informationen über Programmierung für Spielekonsolen erhalten. Jedoch gibt es seit einigen Jahren die Homebrew Szene“, die es sich zur ” Aufgabe gemacht hat, die Konsolen zu analysieren und eigene ProgrammierBibliotheken zusammenzustellen. Da die großen Konsolen, wie zum Beispiel die Playstation 3 und die XBox 360, jedoch sehr komplex sind, wurden bisher vor allem Programmier-Bibliotheken für tragbare Konsolen, wie den Nintendo DS und die Playstation Portable, entwickelt. Wer jedoch annimmt, nun mit diesen Bibliotheken leicht von der Spieleprogrammierung am Computer auf die Spieleprogrammierung für Konsolen wechseln zu können, wird schnell feststellen müssen, dass der Wechsel trotzdem nicht so einfach ist. Da alle Konsolen viel Hardwareoptimierung verwenden, ist die Programmierung eine komplett andere als für den Computer. Auch sind Informationen über diese Bibliotheken und technische Informationen über die Konsole, die man zum Programmieren ebenfalls braucht, nicht gesammelt an einem Punkt verfügbar. Jedes Unterprojekt der Home” brew Szene“ hat seine eigene Website und es gibt nur sehr wenige Verweise auf weitere Projekte. So kann man leicht schon bei der Realisierung seines ersten Projektes mit der NdsLib 1 sein, bevor man herausfindet, dass es auf dieser aufbauend noch die PaLib 2 gibt, die vieles bei der Programmierung erleichtert. In dieser Diplomarbeit wird nun die Entwicklung eines Spieles für den Nintendo DS dokumentiert und dabei aufgezeigt, in wie vielen Punkten sich die Konsolen-Programmierung von der Computer-Programmierung unterscheidet. 1 2 Hardware-nahe Bibliothek für den Nintendo DS Framework-Bibliothek für den Nintendo DS (baut auf der NdsLib auf) vi Kurzfassung Die vorliegende Diplomarbeit dokumentiert die Entwicklung des Spieles Ein” fach Genial DS“. Das Spiel wurde mit den Open Source Bibliotheken NdsLib und PaLib für die tragbare Spielekonsole Nintendo DS umgesetzt. Zu Beginn der Arbeit wird kurz auf die Entwicklung der Spielekonsolen und die Unterschiede zwischen der Programmierung für Spielekonsolen und der Programmierung für den Computer eingegangen. Danach wird die tragbare Spielekonsole Nintendo DS vorgestellt und begründet, warum gerade diese Spielekonsole für das Spiel Einfach Genial DS“ ausgewählt wurde. ” Die Vorbereitungen, um ein Projekt für den Nintendo DS kompilieren zu können, sind der nächste größere Punkt. Dazu gehören die Installation der notwendigen Hard- und Software, das Einrichten der Entwicklungsumgebung und der Transfer des fertigen Programms auf die Konsole. Im Hauptteil der Arbeit werden zuerst die Spielregeln von Einfach Genial erklärt. Dann wird ausführlicher darauf eingegangen, wie bereits in der Planungsphase die Eigenschaften der Hardware berücksichtigt wurden, um Probleme bei der Umsetzung zu vermeiden. Dies ist einer der wichtigsten Punkte bei der Spieleprogrammierung für Konsolen, da es nichts Schlimmeres gibt, als knapp vor der Fertigstellung eines Projektes festzustellen, dass die geplante Umsetzung nicht funktioniert. Im schlimmsten Fall muss dann mit dem Projekt wieder komplett neu begonnen werden. Die Umsetzung des Projektes wurde in die wichtigsten Bereiche unterteilt, die dann in der Reihenfolge, in der sie benötigt würden, wenn man das Projekt neu programmieren würde, vorgestellt werden. Dazu gehören die Verwendung des Dateisystems, die unterschiedlichen Arten der 2D-Grafik, die Sound-Ausgabe, die Benutzereingabe, die künstliche Intelligenz der Computergegner und die Netzwerkprogrammierung für den Mehr-Spieler-Modus. Abschließend werden noch kurz die verwendeten Programmier-Bibliotheken evaluiert und einige der Probleme, die bei der Umsetzung auftraten, vorgestellt. Hier werden zum Beispiel der Versuch, die Spielregeln mit einem Video zu präsentieren, vorgestellt und die Gründe aufgeführt, warum diese Funktion in der endgültigen Version des Projekts nicht mehr inkludiert ist. vii Abstract This master’s thesis is a documentation of the development of the game Einfach Genial DS. The two open source libraries NdsLib and PaLib were used to realise this game for the handheld console Nintendo DS. The first part contains a short overview of the history of the gaming consoles and highlights the differences between computer programming and console programming. After that the handheld console Nintendo DS is presented, followed by the explanation, why it was chosen for the game Einfach Genial DS. The necessary setup for developing software for the Nintendo DS is the next part. This includes the installation of any required hard- and software, the configuration of the development environment and the transfer of the finished program to the console. The main part starts with the rules of the game Einfach Genial. Then the planning of the project is presented, and how the restrictions of the hardware had to be considered to avoid problems. This is one of the most important things in console programming, because there is nothing worse than realizing near the end of the project that it will not work out. In the worst case, one has to start over from scratch. The implementation is divided into the major parts, which are presented in the order in which they would be needed if the project was programmed anew. Among them are the file system, the 2D graphic, the audio output, the user input, the artificial intelligence of the computer opponents and the network for the multiplayer game. At the end there is a short evaluation of the used libraries and the problems that surfaced during the project, for example, why the rules are not displayed as a video in the final version as planed. viii Kapitel 1 Einleitung 1.1 Geschichte der tragbaren Konsolen Entwickler von Computerspielen haben seit jeher mit begrenzten Ressourcen zu kämpfen. Das Ziel war daher immer, mit diesen begrenzten Ressourcen das bestmögliche Ergebnis zu erzielen. Auf dem PC gab es jedoch noch ein weiteres großes Problem, nämlich, dass diese Ressourcen nicht genau definiert sind. So konnte in den Anfängen der 2D-Grafik ein Spiel auf einem PC mit 2, 8, 16 oder 256 darstellbaren Farben gestartet werden. Außerdem gab es noch eine Reihe verschiedener Grafikstandards der unterschiedlichen Grafikkartenhersteller. So konnte etwa beim Klassiker Civilization [13], wie in Abbildung 1.1 ersichtlich, als erstes zwischen zwei Grafikstandards mit 256 Farben (VGA, MCGA) und zwei Grafikstandards mit 16 Farben (EGA, Tandy 1000) ausgewählt werden. Eine Hardwareoptimierung war hier kaum möglich, da es schon genug Zeit benötigte, überhaupt die verschiedenen Treiber zu unterstützen. Wo man zu dieser Zeit jedoch schon sehr gut optimieren konnte, waren die Bereiche, die bis heute bei jedem PC gleich sind und von dem man auch heute noch immer zu wenig hat, nämlich beim Speicherplatz. Spiele wurden zu dieser Zeit meist von Disketten gestartet, alle Grafiken und das Programm zusammen durften also nicht größer als die Kapazität einer 5 1/4 Zoll-Diskette beziehungsweise später dann einer 3 1/2 Zoll-Diskette sein. Aus diesem Grund wurden etwa indizierte Farben und gekachelte Hintergründe verwendet, um möglichst viel Platz zu sparen. Die indizierten Farben hatten noch den weiteren Vorteil, dass hier sehr einfach eine Anpassung an die Grafikkartentreiber vorgenommen werden konnte. Wenn z.B. ein Treiber die Ausgabe im Format Rot-Grün-Blau und ein anderer im Format Blau-Grün-Rot benötigte, so konnte einfach die Farbpalette ausgetauscht werden. Im Programmcode musste so nichts weiter berücksichtigt werden. Auch bei Civilization werden indizierte Farben verwendet. Wenn man sich die Ressourcen des Spiels ansieht, erkennt man, dass es zu jeder Bilddatei 1 KAPITEL 1. EINLEITUNG 2 Abbildung 1.1: Auswahl des Grafikmodus beim Spiel Civilization eine Datei mit der dazugehörigen Palette der indizierten Farben gibt. Als die ersten Konsolen, wie zum Beispiel der Nintendo NES, entwickelt wurden, waren die Techniken, Speicherplatz zu sparen, schon sehr gut entwickelt. Bei den Konsolen trat jedoch ein anderes Problem auf. Um diese für einen relativ geringen Preis im Verhältnis zum PC verkaufen zu können, mussten bei der Hardware weniger leistungsfähige Komponenten verwendet werden. So hatte der Nintendo NES [20] zum Beispiel nur einen Prozessor mit einer Geschwindigkeit von 1,79 Megahertz. Um mit einem solch langsamen Prozessor trotzdem noch flüssig Spiele darstellen zu können, mussten nun neue Techniken entwickelt werden. Da alle diese Systeme die gleichen Hardwarekomponenten verwendeten, konnten nun aber rechenintensive Operationen, wie etwa Sprites, Hintergründe, gekachelte Hintergründe, Animationen und Sounds, in die Hardware ausgelagert werden. Die CPU musste nur noch den logischen Anteil des Spieles abarbeiten. Seit diesen ersten Konsolen hat sich im Bereich der Hardwarebeschleunigung nicht mehr viel verändert. Es hat sich nur die Leistungsfähigkeit der Hardware verbessert. So ist zum Beispiel die Anzahl der Sprites von 64 beim Nintendo NES auf 128 beim Nintendo DS gestiegen. Auch die Größe der Sprites hat sich von 8 × 8 auf bis zu 64 × 64 gesteigert. Außerdem kamen noch neue Funktionen wie 3D-Grafik und Netzwerk hinzu. 1.2 Unterschiede Konsole – PC Neben den Beschränkungen durch die Hardwarebeschleunigung vieler Komponenten gibt es noch eine Reihe anderer Unterschiede zwischen einem Spiel für den Computer und einem für eine tragbare Konsole. Diese müssen von KAPITEL 1. EINLEITUNG 3 Anfang an berücksichtigt werden, um erfolgreich ein Spiel entwickeln zu können. Die Eingabemöglichkeiten sind bei Konsolen generell und bei tragbaren Konsolen im Besonderen sehr beschränkt. Bei der ersten Version des Gameboys bestanden die Eingabemöglichkeiten aus 8 Buttons (Steuerkreuz, A, B, Start, Select). Der Nintendo DS hat als erste Konsole durch den TouchScreen standardmäßig eine Maus-ähnliche Eingabemöglichkeit. Doch auch hier gibt es Einschränkungen, die man beim Spieledesign berücksichtigen muss. Der Touchscreen ist einerseits nicht so genau wie eine Maus (Auswahlobjekte dürfen also nicht zu klein sein), andererseits ist er auch unruhiger als eine Maus. Die Position des Stiftes auf dem Touch-Screen kann oft zwischen 2 Pixeln hin und her springen, was im Programm ausgeglichen werden muss. Eine weitere wichtige Einschränkung ist, dass der Spieler den Touch-Screen und die Buttons nicht gleichzeitig bedienen kann. Der Spieler hält in einer Hand das Gerät und in der anderen den Stift. Je nachdem, ob der Spieler nun Rechts- oder Linkshänder ist, kann er entweder das Steuerkreuz oder die Action-Buttons nicht mehr bedienen. 1.3 Legale Aspekte von Open Source SDKs Mit Open Source SDKs befindet man sich bei den Konsolen rechtlich in einer Grauzone. Einerseits besitzt man die Hardware und ist berechtigt, mit dieser zu machen was man will. Andererseits sind die Hardwaresysteme meistens geschützt, um das Abspielen von kopierten Spielen zu verhindern. In vielen Staaten, wie zum Beispiel in Deutschland, ist das Umgehen von Kopierschutzmechanismen verboten [17]. Wenn man sich den entsprechenden § 95a [3] des deutschen Urheberechtsgesetzes (UrhG) jedoch genauer ansieht, findet man den Satz, dass der Kopierschutz mit dem Ziel umgangen werden muss, Zugang zu einem nach dem Urheberechtsgesetz geschützten Werk zu erhalten. Außerdem ist Software nach § 69a Abs.5 UrhG vom Verbot ausgenommen. Ein Umgehen des hardwareseitigen Schutzmechanismus sollte demnach legal sein, da es ja in der Absicht geschieht, den eigenen Code ausführen zu können. Die Haltung der Hersteller von Konsolen in Bezug auf die Entwicklung eigener Programme ist gespalten. Einige Hersteller sehen nur, dass unautorisierte Software auf ihren Konsolen ausgeführt werden kann und wollen das mit allen Mitteln verhindern. Denn zu unautorisierter Software zählen auch kopierte Spiele, ob diese nun legale Privatkopien sind oder nicht. Andere Hersteller haben mittlerweile eingesehen, dass die Entwicklung von eigener Software weniger ein Schaden als ein Zusatz-nutzen ist und bieten offiziell Möglichkeiten dazu an. Dazu gehören unter anderem Sony mit der Playstation 3, die in einem Linux-Modus gestartet werden kann. Jedoch ist hierbei die Grafikbeschleunigung abgeschaltet, sodass es nicht wirklich möglich ist, KAPITEL 1. EINLEITUNG 4 Abbildung 1.2: GP2X Spiele darauf zu entwickeln. Microsoft geht mit seiner XBox 360 einen Schritt weiter und bietet die Software XNA Gamestudio Express an, mit der es möglich ist, Spiele zu entwickeln. Diese können dann online über das XBox Live Arcade System“ ” verbreitet werden. Die koreanische Firma Gamepark brachte im November 2005 den GP2X [7] (Abbildung 1.2) auf den Markt, der eine tragbare Konsole ist, die auf Linux basiert und komplett offen ist. Kapitel 2 Das System – Nintendo DS 2.1 Kurzer Überblick über den Nintendo DS Seit der Veröffentlichung im November 2004 wurden vom Nintendo DS [15] (Abbildung 2.1) laut Angaben der Firma Nintendo weltweit über 47 Millionen Geräte verkauft1 [16]. Die Firma kann damit für sich in Anspruch nehmen, das am schnellsten verkaufte Videospielsystem auf den Markt gebracht zu haben. Die Gründe für diesen überragenden Erfolg dürften in den vielfältigen Fähigkeiten des Geräts liegen. Im Jahr 2006 wurde eine leichtere und kleinere Version unter dem Namen Nintendo DS Lite in den Handel gebracht, die aber ansonsten die gleichen technischen Fähigkeiten wie das Original hat. Was dem Benutzer als Erstes ins Auge fallen wird, sind die zwei 3 Zoll TFT LCD Bildschirme, die beide eine Auflösung von 256 × 192 Pixel haben. Der untere der zwei Bildschirme ist ein Touch-Screen, der mit dem beigefügten Stift bedient werden kann. Links neben dem unteren Bildschirm befinden sich die Einschalttaste sowie ein Steuerkreuz. Rechts neben dem Touch-Screen befinden sich die schon von vielen tragbaren Konsolen bekannten sechs Tasten Start, Select, A, B, X und Y. Auf jeder Seite des oberen Bildschirms befindet sich ein Stereo Lautsprecher. Mit diesen kann sogar virtueller Surround-Sound ausgegeben werden. Auf der hinteren Seite der Konsole befinden sich noch zwei weitere Tasten L und R. Neben diesen sichtbaren Eingabemöglichkeiten hat der Nintendo DS noch ein Mikrophon. Die Spiele für den Nintendo DS kommen auf Flash Speicherkarten [19] (Abbildung 2.2), die bis zu ein Gigabyte Volumen haben können, in den Handel. Neben diesen neuen Speicherkarten können jedoch auch die Speicherkarten der Vorgängerkonsole Gameboy Advance verwendet werden. Wie schon bei der Vorgängerkonsole werden auch beim Nintendo DS alle Spiele der Vorgängerkonsolen unterstützt. Die Spiele des Game Boy Advance oder des Gameboy werden dabei auf einem der beiden Bildschirme dargestellt. 1 Stand 1. Juli 2007 5 KAPITEL 2. DAS SYSTEM – NINTENDO DS 6 Abbildung 2.1: Nintendo DS Abbildung 2.2: Nintendo DS Spiel Der Schacht für die Gameboy Advance Spiele befindet sich auf der Vorderseite der Konsole, während sich der Schacht für die Nintendo DS Spiele auf der Rückseite befindet, wie in Abbildung 2.3 dargestellt ist [14]. Um zusammen mit anderen Spielern spielen zu können, muss man nicht wie bisher die Konsolen mit einem Kabel verbinden, sondern kann die eingebaute Wireless LAN Verbindung verwenden. Über Wireless LAN AccessPoints kann man sogar weltweit gegeneinander spielen. Um zusammen ein Spiel spielen zu können, reicht es beim Nintendo DS aus, wenn einer der KAPITEL 2. DAS SYSTEM – NINTENDO DS 7 Abbildung 2.3: Nintendo DS mit Gameboy Advance und Nintendo DS Speicherkarte Spieler das Spiel besitzt. Die anderen können die zum Spielen notwendigen Daten über die Wireless LAN Verbindung herunterladen. Die Daten bleiben so lange erhalten, bis das Gerät abgeschaltet wird. Diese Methode ist eine Erweiterung des beim Game Boy Advance vorgestellten single cartridge ” multiplayer“ Systems. Die Rechenleistung des Nintendo DS [19] übernehmen zwei ARM CPUs. Der Hauptprozessor ist ein ARM946E-S, der mit 67 MHz läuft und von einem Coprozessor ARM7TDMI, der mit 33 MHz läuft, unterstützt wird. Jeder der beiden Prozessoren hat 4 MB RAM zur Verfügung. Das Gerät verfügt außerdem noch über zwei 2D-Grafikprozessoren, einen pro Bildschirm, und einen 3D-Grafikprozessor, der jedoch nur dafür ausgelegt ist, auf einem Bildschirm 3D-Grafik darzustellen. Es ist zwar theoretisch möglich, auf beiden Bildschirmen 3D-Grafik darzustellen, jedoch ist dies mit großen Performance-einbrüchen verbunden. Die 3D-Hardware unterstützt Texturierung, Beleuchtung, Alpha-Blending, Cel-Shading, Z-Buffering und Anti-Aliasing. Jedoch wird bei der Texturierung nur ein nächster Nachbar“ ” Textur-Filter unterstützt, wodurch die Texturen pixelig wirken können. Die Polygonanzahl ist auf 4096 beschränkt, die dann mit 60 fps dargestellt werden können. KAPITEL 2. DAS SYSTEM – NINTENDO DS 2.2 8 Gründe, warum der Nintendo DS für das Projekt Einfach Genial DS gewählt wurde Der Nintendo DS eignet sich aus mehreren Gründen gut als Testsystem. Er hat zwei Bildschirme, wodurch auf einem die Grafik ausgegeben werden kann, während auf dem anderen zum Beispiel Debug Informationen ausgegeben werden können. Diese Debug Informationen waren auf älteren Konsolen nur schwer auszugeben, da der Text für die Ausgabe eine komplette Schicht benötigte und diese dann für die 2D-Grafik fehlte. Ein weiterer Grund ist, dass die Entwicklungsbibliotheken der Homebrew-Szene für diese Konsole sehr umfassend sind. Bei älteren Konsolen wurden für die Hardware meistens Eigenentwicklungen verwendet, wodurch es sehr schwer war, an Informationen über die Hardware zu gelangen. Bei neueren Konsolen werden verstärkt Standardbauteile verwendet, und daher lassen sich zu diesen sehr viel leichter Informationen finden. Beim Nintendo DS zum Beispiel wurden zwei ARM CPUs eingesetzt, deren Informationen sich direkt von der Herstellerseite abrufen lassen (ARM72 und ARM93 ). Der nächste Grund, warum der Nintendo DS als Testsystem gewählt wurde, liegt in seiner großen Verbreitung. Mit über 35 Millionen verkauften Geräten ist er zurzeit die erfolgreichste tragbare Konsole. Der letzte Grund hat weniger mit den aktuellen Themen als mit einem Ausblick in die Zukunft zu tun. Die vielen Fähigkeiten des Nintendo DS neben der Ausgabe von 2D-Grafik werden es noch auf lange Zeit ermöglichen, Neues auszuprobieren und zu lernen. Vor allem Features, wie der TouchScreen und das Mikrophon, oder aber auch die WLAN-Anbindung sind ideal, um neue Ideen in der Spieleentwicklung umzusetzen. 2.3 NdsLib – Die Hardware-Nahe Bibliothek Die NdsLib ist laut eigener Aussage eine Sammlung von Funktionen, Werkzeugen und Include-Dateien für die Verwendung zur Entwicklung legaler, selbst gemachter Software für den Nintendo DS. Wenn man sich die IncludeDateien ansieht, erkennt man sehr schnell, dass die meisten Funktionen, die zur Verfügung gestellt werden, Makros sind, die direkte Speicherzugriffe in für den Programmierer leichter verständliche Formate umwandeln. Zusätzlich zu diesen Makros werden noch Funktionen zur Verfügung gestellt, die ähnlich auch bei der Windows-Programmierung verwendet werden. Dazu gehören zum Beispiel print() Funktionen zur Ausgabe von Text auf dem Bildschirm oder auch eine Implementierung des FAT-Dateisystems, um Daten speichern zu können. 2 3 http://www.arm.com/pdfs/DDI0210B 7TDMI R4.pdf http://www.arm.com/pdfs/ARM946.pdf KAPITEL 2. DAS SYSTEM – NINTENDO DS 2.4 9 PaLib – Die Framework Bibliothek Die PaLib – der Name steht für Programmers Arsenal Library – ist ein Framework, das die am häufigsten verwendeten Funktionen der NdsLib kapselt. Es ist mit dieser Library sehr einfach, grundlegende Elemente zu verwenden. Sollte man jedoch auf Probleme stoßen, ist es oft schwierig, diese zu lösen, da man keinen direkten Einfluss auf die Hardware mehr hat. Auch ist das Framework noch nicht komplett und viele Funktionen sind noch gar nicht, beziehungsweise nur teilweise, umgesetzt. 2.5 2.5.1 Installationsanleitung Benötigte Hard- und Software Das Erste, was installiert werden muss, ist die NdsLib, da in ihr die grundlegenden Funktionen gekapselt sind, auf denen die PaLib aufbaut. Die NdsLib ist Teil des devkitPro, einer Sammlung diverser Bibliotheken für KonsolenEntwicklung. Das devkitPro kann einfach über einen Updater heruntergeladen werden, den man dann später auch verwenden kann, um auf Updates zu überprüfen4. Neben der NdsLib für den Nintendo DS befinden sich noch Bibliotheken für den Gameboy Advance, GP32, Playstation Portable und GameCube in dieser Sammlung. Wichtig bei der Installation ist, dass im Installationspfad keine Leerstellen vorkommen dürfen. Der nächste Schritt ist die Installation der PaLib5 . Am einfachsten ist es, wenn die PaLib in dasselbe Verzeichnis wie die NdsLib installiert wird. Es gilt hier, wie bei der NdsLib, dass keine Leerstellen im Installationspfad vorkommen dürfen. Nachdem nun beide benötigten Bibliotheken installiert sind, ist der nächste Schritt die Auswahl einer Entwicklungsumgebung, in der man arbeiten will. Es gibt bei Entwicklungsumgebungen im Großen und Ganzen drei Möglichkeiten. Man kann die Software Programmer’s Notepad verwenden, die beim devkitPro mitinstalliert wird. Alle Beispiele der NdsLib liegen als Projekte dieser Software vor. Die zweite Möglichkeit ist die Software VisualHAM, die bei der PaLib mit installiert wird. Die Beispiele der PaLib liegen immer als VisualHAM Projekte, meistens jedoch auch als Programmer’s Notepad Projekte vor. Im Gegensatz zum Programmer’s Notepad wird VisualHAM jedoch nicht mehr weiterentwickelt. Auf der offiziellen Homepage dazu wird geraten, auf Microsoft Visual Studio zu wechseln, das die dritte Möglichkeit darstellt. Die Wahl der Entwicklungsumgebung bleibt im Prinzip den eigenen Vorlieben vorbehalten, denn bei allen drei Versionen befinden sich alle notwendigen Einstellungen ausgelagert in Make-Files, die zwischen den Entwick4 5 Zu finden ist der Updater auf http://www.devkitpro.org Homepage: http://www.palib.info KAPITEL 2. DAS SYSTEM – NINTENDO DS 10 lungsumgebungen ausgetauscht werden können. Auf die Entwicklungsumgebungen wird in Kapitel 2.5.2 näher eingegangen. Die letzte Software, die man benötigt, ist ein Emulator, um den erzeugten Nintendo DS Code am PC testen und debuggen zu können. Wenn man sich einmal in die Materie eingearbeitet hat, wird man auf mehrere Emulatoren stoßen, die im Internet verfügbar sind. Einer davon, Ensata, ist jedoch eine gecrackte Version des Nintendo-eigenen Emulators und sollte deshalb aus rechtlichen Gründen nicht verwendet werden. Aus diesem Grund wird hier auf diesen Emulator nicht weiter eingegangen. Für die Entwicklung des Spieles Einfach Genial DS wurde der Emulator DeSmuMe 6 verwendet. Wenn man einen Emulator auswählt, muss man beachten, dass noch kein Emulator den Nintendo DS vollständig und perfekt emulieren kann. Man sollte nicht nur einfach den ersten Emulator verwenden, den man findet, sondern mehrere testen, um festzustellen, welcher Emulator für das Projekt am besten geeignet ist. Auch sollte man, vor allem bei langfristigen Projekten, immer wieder die Homepage des Emulators besuchen, da diese meist schnell weiterentwickelt werden. Der hier verwendete Emulator DeSmuMe wurde zum Beispiel während der Entwicklungszeit des Spiels von der Version 0.3.0 zur Version 0.7.0 weiterentwickelt. Um die eigene Software nun auch auf dem Nintendo DS testen zu können, muss man seinen Nintendo DS so modifizieren, dass er Software startet, die nicht von Nintendo signiert wurde. Es gibt hier im Wesentlichen zwei Möglichkeiten. Beim FlashMe wird dem Nintendo DS eine modifizierte Firmware eingespielt, wodurch leider die Garantie verloren geht, jedoch muss danach keine zusätzliche Karte mehr verwendet werden. Beim PassMe, der zweiten Methode, wird eine spezielle Hardware in den Nintendo DS Kartenschacht gesteckt, die vorhanden sein muss, wenn ein eigenes Programm ausgeführt werden soll. Als nächstes muss man die eigene Software auf den Nintendo DS übertragen. Je nachdem, welche Modifizierung am Nintendo DS vorgenommen wurde, hat man hier mehrere Möglichkeiten. Üblich ist die Verwendung eines Nintendo DS Movie Players für den GBA Kartenslot. Für die Erstellung dieser Arbeit wurde ein Nintendo DS verwendet, der mit fertig aufgespieltem FlashMe gekauft wurde. Zusätzlich wurde ein M3Adapter [11] für Compact-Flash Karten, wie in Abbildung 2.4 zu sehen, eingesetzt. 2.5.2 Einrichten der Entwicklungsumgebungen Projekte bei der Programmierung für den Nintendo DS sind Makefile Projekte, das heißt, alle Einstellungen befinden sich gesammelt in einer Entwicklungsumgebungs-unabhängigen Datei und nicht in den Projekteinstellungen 6 Homepage: http://desmume.sourceforge.net/ KAPITEL 2. DAS SYSTEM – NINTENDO DS 11 Abbildung 2.4: Nintendo DS mit M3 Adapter der Entwicklungsumgebung. Die Wahl der Entwicklungsumgebung kann daher aufgrund persönlicher Vorlieben erfolgen. Im Laufe der Entwicklung des Spiels Einfach Genial DS wurden drei verschiedene Entwicklungsumgebungen getestet, die üblicherweise für die Entwicklung von Programmen für den Nintendo DS verwendet werden. Programmer’s Notepad 2 Die Beispiele der NdsLib liegen als Projekte für das Programmer’s Notepad 27 vor. Diese Entwicklungsumgebung wird bei der Installation der NdsLib mitinstalliert und deshalb vor allem zu Beginn viel verwendet, da die NdsLib meistens die erste SDK ist, mit der angehende Open-Source Nintendo DS Entwickler lernen. Abbildung 2.5 zeigt eines der Beispiele der NdsLib geöffnet im Programmer’s Notepad 2. Die Entwicklungsumgebung bietet nur die notwendigsten Funktionen, wie das Hervorheben von Schlüsselwörtern und das Kompilieren der Programme über Makefiles, das dafür aber für eine Vielzahl von Programmiersprachen. Über ein Pluginsystem kann die Entwicklungsumgebung noch erweitert werden. 7 Homepage: http://www.pnotepad.org KAPITEL 2. DAS SYSTEM – NINTENDO DS 12 Abbildung 2.5: Programmer’s Notepad 2 Projekt VisualHAM VisualHAM8 ist eine Entwicklungsumgebung, die speziell für die Verwendung mit der HAM-Bibliothek, einem Gameboy Advance SDK9 , entwickelt wurde. Sie unterstützt nur Syntax Hervorhebung für C/C++ und Assembler Code für die ARM Prozessoren, wie sie auch im Nintendo DS eingesetzt werden. Leider wurde die Entwicklung dieser Freeware eingestellt. Der Quellcode wurde aber als Open Source publiziert. VisualHAM verwendet für fast alle Einstellungen Konfigurations-Dateien und kann so relativ leicht für die Entwicklung mit einer anderen Bibliothek eingerichtet werden. Das wurde für die PaLib durchgeführt. Deshalb wird VisualHAM mit der PaLib mitinstalliert und alle Beispiele der PaLib liegen als VisualHAM Projekte vor. Da diese Entwicklungsumgebung für die Programmierung von Konsolen entwickelt wurde, sind einige Funktionen inkludiert, die auch bei der Entwicklung für den Nintendo DS sehr hilfreich sind. • Erstellung eigener Code-Block Templates, zum Beispiel für die Initialisierung der Konsole, das Laden eines Hintergrunds, das Laden einer 8 9 Homepage: http://www.console-dev.de/visualham.html System Development Kit KAPITEL 2. DAS SYSTEM – NINTENDO DS 13 Abbildung 2.6: VisualHAM-Projekt Palette,... • Ausführen des erstellten Programms in den verschiedenen Emulatoren, wie in Abbildung 2.6 zu sehen ist. • Ausgeben der Commandline Ausgabe der Emulatoren in der Entwicklungsumgebung. Microsoft Visual Studio Da Visual Studio auch Makefile Projekte unterstützt, kann es ebenfalls für die Entwicklung mit der NdsLib und der PaLib verwendet werden. Allerdings kann man hier nicht auf die Projekte der Beispiele zurückgreifen, sondern muss sein Projekt selbst konfigurieren. Da alle wichtigen Einstellungen im Makefile stehen, bleiben aber nur wenige solcher Einstellungen im Visual Studio Projekt übrig. Abbildung 2.7 zeigt das Visual Studio Projekt, das für die Erstellung des Spieles Einfach Genial DS verwendet wurde. Aus mehreren Gründen wurde Microsoft Visual Studio nach dem Austesten der anderen Entwicklungsumgebungen für das Spiel ausgewählt. Unter anderem, da Visual Studio als professionelle Entwicklungsumgebung den frei KAPITEL 2. DAS SYSTEM – NINTENDO DS 14 Abbildung 2.7: Visual Studio Projekt erhältlichen Editoren in der Vielzahl der Funktionen überlegen ist. Außerdem war hier Aufgrund der Verwendung an der Fachhochschule schon am meisten Vorwissen vorhanden. Da die Open Source SDKs die GNU Compiler Kollection zum Kompilieren verwenden, müssen im ersten Schritt die Pfade auf die Kompilierer in die Liste der ausführbaren Dateien eingefügt werden, wie in Abbildung 2.8(a) dargestellt. Es müssen zwei Verzeichnisse, die im Installationsordner des devkitPro liegen, eingefügt werden: msys\bin und devkitARM\bin. In den Projekteinstellungen müssen nun die Befehle angegeben werden, die benützt werden sollen, um das Projekt zu kompilieren bzw. komplett neu zu kompilieren. Im Wesentlichen genügen hier, wie in Abbildung 2.8(b), die beiden Befehle make −f Makefile und make −f Makefile clean. Jedoch kann Visual Studio dann die Kompilierwarnungen und -fehler nicht direkt den Programmcode-Zeilen zuordnen, da diese in der Linux Formatierung ausgegeben werden, die von Visual Studio nicht verarbeitet werden kann. Da die Zeilennummern in den Fehlermeldungen aufscheinen, wurde dies für das Projekt jedoch nicht als Hindernis betrachtet. Sollte man diese Zuordnung jedoch trotzdem benötigen, kann dies über den Cygwin Stream Editor erreicht werden. Nachdem man diesen installiert hat, muss man an die Befehle KAPITEL 2. DAS SYSTEM – NINTENDO DS 15 (a) Binary Pfade setzten (b) Kompilier-Einstellungen (c) Debug-Einstellungen Abbildung 2.8: Konfiguration des Visual Studio Makefile Projekts durch verweis auf den gewünschten Kompiler (a), Einstellung der Paramter für das Kompilieren (b) und die Angabe wie das Programm gestartet werden soll (c). sed −e ’s/\(\w\+\):\([0−9]\+\):/\1(\2):/’ anhängen, um die Ausgabe der Kompilierer in ein Visual Studio verständliches Format umzuwandeln. Um das erstellte Projekt debuggen zu können, muss nicht, wie bei PCProgrammen üblich, die .exe-Datei als Debug-Befehl angegeben werde, sondern, wie in Abbildung 2.8(c), die .exe-Datei des Emulators, der verwendet werden soll. Um die erstellte .nds-Datei automatisch in den Emulator zu laden, wird diese einfach als Befehlsargument mit übergeben. 2.5.3 Makefile In einem Makefile sind alle Einstellungen zusammengefasst, die zum Kompilieren notwendig sind. Die meisten Bereiche des Makefile sind immer gleich und können deswegen ohne Probleme von einem der Beispiele der PaLib kopiert werden. In Listing 2.1 werden nur die Bereiche angeführt, bei denen selbst Einstellungen vorgenommen werden können oder müssen. Als erstes werden die Pfade auf die Nintendo DS SDKs gesetzt. Bei der KAPITEL 2. DAS SYSTEM – NINTENDO DS 16 Installation des devkitPro wird der DEVKITARM Pfad gesetzt, der Pfad auf die PaLib muss jedoch normalerweise selbst angegeben werden. Wenn die PaLib in den Ordner des devkitPro installiert wurde, kann der PAPATH auf den DEVKITARM gesetzt werden. Mit TEXT1, TEXT2 und TEXT3 wird die Spielbeschreibung und mit ICON und LOGO werden die Logos des Spieles gesetzt. Auf die Logos wird in Kapitel 4.2.1 genauer eingegangen. Im nächsten Block wird angegeben, wie die Ausgabedatei benannt werden soll und welche Ordner verwendet werden sollen. Die folgenden beiden Variablen LIBS und LIBSPA geben an, welche Bibliotheken inkludiert werden sollen. Danach kommen noch die Ordner LIBDIRS und LIBDIRSPA, in denen sich die Bibliotheken und Include-Dateien befinden. Hier müssen Ordner angegeben werden, in denen sich dann zwei weitere Ordner (include und lib) befinden. Eine wichtige Einstellung, die oft für Fehler verantwortlich ist, befindet sich in der Mitte des Makefiles in Zeile 114. Hier wird angegeben, wie das Projekt gelinkt werden soll. CXX steht hier für C++ Projekte und CC gibt an, das das Projekt als C Projekt gelinkt werden soll. 5 6 PATH PAPATH Listing 2.1: Einfach Genial DS Makefile := $ ( D E V K I T A R M )/ bin : $ ( PATH ) := $ ( D E V K I T A R M) 7 8 9 10 11 12 13 32 33 34 35 84 85 92 93 114 ARM7BIN := -7 $ ( P A P A T H )/../ PAlib / lib / arm7 / arm7 . bin TEXT1 := E i n f a c h G e n i a l TEXT2 := S c h e r m a n n TEXT3 := W o l f g a n g ICON := -b $ ( C U R D I R )/../ logo . bmp LOGO := -o $ ( C U R D I R )/../ l o g o _ w i f i. bmp T A R G E T := $ ( shell b a s e n a m e $ ( C U R D I R )) BUILD := build S O U R C E S := gfx s o u r c e data I N C L U D E S := i n c l u d e build data LIBS := - lfat - lnds9 - l d s w i f i 9 L I B S P A := - lpa9 L I B D I R S := $ ( D E V K I T P R O )/ l i b n d s L I B D I R P A := $ ( P A P A T H) e x p o r t LD 2.5.4 := $ ( CXX ) Debuggen Das Debuggen der Programme für den Nintendo DS ist einer der kompliziertesten Bereiche, da ein eigenes System für das Debuggen, wie es bei der KAPITEL 2. DAS SYSTEM – NINTENDO DS 17 Abbildung 2.9: Nintendo DS Development Kit mit dem Namen Nitro“ ” offiziellen SDK mit dem Nitro“ System [4] (siehe Abbildung 2.9) vorhanden ” ist, nicht existiert. Es gibt im Wesentlichen zwei Möglichkeiten, Nintendo DS Programme, die mit Open-Source SDKs geschrieben wurden, zu debuggen: mit der Hilfe eines Emulators, oder direkt auf dem System, indem die Debug-Informationen als Text ausgegeben werden. Debuggen mit Emulatoren Da Emulatoren die Hardware des Nintendo DS simulieren, bieten sie oft die Möglichkeit, die einzelnen Bereiche der simulierten Hardware separat darzustellen. Damit lässt sich sehr gut überprüfen, ob man die Hardware im Programmcode richtig ansteuert und die Daten auch an den vorgesehenen Speicherplätzen gespeichert werden. Vor allem beim Arbeiten mit Paletten, Sprites und Hintergründen können Fehler so schnell erkannt werden. Abbildung 2.10 stellt die verschiedenen Debug-Ansichten des Emulators DeSmuME in der Version 0.7.0 dar, der für die Entwicklung des Spieles Einfach Genial DS hauptsächlich verwendet wurde. Das Debuggen mit dem Emulator hat den Vorteil, dass es direkt aus der Entwicklungsumgebung heraus gestartet werden kann. Außerdem kann die Ausführung jederzeit eingefroren werden, womit dann alle Werte in der Hardware überprüft werden können. Ein großes Problem bei den Emulatoren ist, dass zur Zeit noch nicht alle Funktionen implementiert sind. So kann es vorkommen, dass Fehler, die man im Emulator zu finden glaubt, gar nicht auf fehlerhaften Programmcode zurückzuführen sind, und dass manche Funktionen nicht im Emulator debuggt werden können. Im Vorprojekt wurde als Emulator noch die 0.3.0 Version des DeSmuME verwendet und als der Hintergrund getestet werden sollte, wurde am Emulator nichts angezeigt. Später hat sich dann herausgestellt, dass diese Version noch keine 512x256 Hintergründe darstellen konnte. Auf dem Nintendo DS wurde der Hintergrund dargestellt. Es empfiehlt sich deshalb, beim Debuggen im Emulator nicht nur auf einem Emulator zu debuggen. Im Zweifelsfall sollte der Code auf dem Gerät KAPITEL 2. DAS SYSTEM – NINTENDO DS (a) Jump&Run Demo (b) Ansicht Paletten (c) Ansicht Sprites (d) Ansicht Hintergründe Abbildung 2.10: Einfaches Jump & Run (a) im Emulator mit den DebugAnsichten der Palette (b), der Sprites (c) und der Hintergründe (d) 18 KAPITEL 2. DAS SYSTEM – NINTENDO DS 19 Abbildung 2.11: Ausgabe der Exception aus Listing 2.2 auf dem Nintendo DS selbst getestet werden. Sollte man Funktionen, wie etwa ein Dateisystem, einsetzten, die den Emulator zum Abstürzen bringen, sollte man neue Module des Programms – soweit möglich – zuerst separat entwickeln und erst, wenn sie fast abgeschlossen sind, in das Hauptprogramm einbauen. Diese Vorgangsweise wurde auch beim Spiel Einfach Genial DS ab dem Zeitpunkt, als das Dateisystem eingebaut wurde, eingesetzt. Debuggen auf dem Nintendo DS Beim Debuggen auf dem Nintendo DS selbst gibt es das Problem, dass man die Debug-Informationen auf einer Textschicht auf einem der beiden Bildschirme ausgeben muss, und somit diesen Layer nicht mehr für andere Grafik-Ausgaben benutzen kann. Je nachdem, was debuggt werden soll, gibt es mehrere Möglichkeiten, Informationen auszugeben. In der NdsLib ist ein Exception Handler inkludiert, der automatisch eine Fehlermeldung generiert. Um ihn zu verwenden, muss nur eine Codezeile inkludiert werden (siehe Listing 2.2). Eine solche Fehlermeldung wird in Abbildung 2.11 dargestellt. Listing 2.2: Exception Funktion der NdsLib 1 # i n c l u d e < nds .h > 2 3 4 5 6 int main ( void ) { // Die S t a n d a r d a u s g a b e der E x c e p t i o n s v e r w e n d e n d e f a u l t E x c e p t i o n H a n d l e r (); 7 8 9 10 i r q I n i t (); i r q E n a b l e( I R Q _ V B L A N K ); KAPITEL 2. DAS SYSTEM – NINTENDO DS 20 // E x c e p t i o n a u s l ö s e n *( u32 *)250 = 100; 11 12 13 while (1) s w i W a i t F o r V B l a n k (); r e t u r n 0; 14 15 16 } Die nächste Möglichkeit ist eine Klasse, die eine Konsolenausgabe (automatisch scrollender Text) ermöglicht, deren Verwendung in Listing 2.3 gezeigt wird. Abbildung 2.12 zeigt die Verwendung dieser Ausgabemöglichkeit bei dem Testbeispiel der Wireless LAN Funktionen. 1 Listing 2.3: Scrollender Text umgesetzt mit der NdsLib # i n c l u d e < nds .h > 2 3 4 5 // S p e i c h e r b e r e i c h des g e k a c h e l t e n H i n t e rgrunds , auf dem // der Text d a r g e s t e l l t wird (32 x 24 Z e i c h e n) # d e f i n e VRAM1 (( u16 *) 0 x 0 6 0 0 0 0 0 0) 6 7 8 9 10 11 12 13 14 15 16 17 18 void Debug :: S c r o l l U p T o p () { int i ; // 23 Z e i l e n nach oben v e r s c h i e b e n for ( i =0;i < 2 3 * 3 2 ;i ++) VRAM1 [0 x 0 7 C 0 0+ i ]= VRAM1 [0 x 0 7 C 0 0+ i +32]; // U n t e r s t e Zeile mit L e e r s t e l l e n f ü l l e n for ( i =0;i <32; i ++) { VRAM1 [0 x 0 7 C 0 0 + 2 3 * 3 2 +i ]= ’ ’; } } 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 void Debug :: print ( const char * str ) { int x ; // Nach oben s c r o l l e n S c r o l l U p T o p (); x =0; while (* str ) { if ( x ==32) { // Z e i l e n u m b r u c h x =0; S c r o l l U p T o p (); } // In die u n t e r s t e Zeile s c h r e i b e n VRAM1 [0 x 0 7 C 0 0 + 2 3 * 3 2 +x ++]=* str ; str ++; } } KAPITEL 2. DAS SYSTEM – NINTENDO DS 21 Abbildung 2.12: Ausgabe des scrollenden Textes aus Listing 2.3 auf dem Nintendo DS Die letzte Möglichkeit ist eine einfache Textausgabe, zum Beispiel über ein Makro. Diese hat den Vorteil, dass sie schnell und einfach zu implementieren und für die finale Version leicht deaktivierbar ist. 2.5.5 Transfer des Programms auf den NDS Die Methode, mit der das Programm auf den Nintendo DS transferiert wird, um es dort auszuführen, hängt von der verwendeten Hardware ab. Es gibt jedoch grundsätzlich drei Möglichkeiten: • Übertragung über ein Kabel (USB, Seriell) • Transfer über Wireless LAN • Verwendung eines Speichermediums Bei der Anschaffung der Hardware für die Entwicklung des Spiels Einfach Genial DS wurde entschieden, eine Lösung, die eine Compact Flash Karte als Speichermedium benützt, zu verwenden. Der Hauptgrund war, dass viele Transfers notwendig sein würden, um das Spiel zu debuggen, und diese Lösung robust und gut bedienbar war. Abbildung 2.4 zeigt die eingesetzte Hardware. Der eigentliche Transfer ist bei der verwendeten Lösung denkbar einfach. Das Programm kann mit Hilfe eines handelsüblichen Kartenlesers an jeden beliebigen Platz auf der Compact Flash Karte kopiert werden. 2.5.6 Starten des Programms auf dem NDS Das Starten eines selbst geschrieben Programms auf dem Nintendo DS ist, schon wie der Transfer, abhängig von der verwendeten Hardware. Die für das KAPITEL 2. DAS SYSTEM – NINTENDO DS 22 Abbildung 2.13: Dateibrowser des M3 Adapters mit den drei kompilierten Dateien Projekt gewählte Hardware hat ein eigenes Menü, dargestellt in Abbildung 2.13, über das das Programm gestartet werden kann. Bei der verwendeten Hardware gibt es, wie bei fast allen anderen auch, zwei Möglichkeiten, ein Programm zu starten, und zwar entweder als Spiel oder als Demo. Die Startweise als Spiel bezieht sich auf Sicherungskopien eigener Spiele, die Startweise als Demo ist ursprünglich für selbst geschriebene Programme vorgesehen. Welche von beiden man verwenden muss, hängt davon ab, wie man sein Programm kompiliert hat. Wenn man zum Kompilieren das Makefile eines PaLib Projektes nimmt, werden drei Dateien des erstellten Programms, die für unterschiedliche Hardware optimiert sind, erzeugt. • <Projektname>.ds.gba ist für den Gameboy Advance Movie Player vorgesehen. • <Projektname>.nds ist nicht für eine bestimmte Hardware optimiert und deshalb am besten für Emulatoren geeignet. • <Projektname>.sc.nds ist für Supercard und M3 Adapter optimiert. Da für das Projekt als Hardware ein M3 Adapter eingesetzt wurde, wurde immer die <Projektname>.sc.nds verwendet, um das Programm als Spiel starten zu können. So wird auch das Logo des Spiels richtig dargestellt. Abbildung 2.13 zeigt die Unterschiede in der Darstellung der drei verschiedenen Varianten am M3 Adapter. Kapitel 3 Das Spiel – Einfach Genial 3.1 Das Spielprinzip In diesem Kapitel wird auf das Spielprinzip und die Regeln des Spieles Einfach Genial [9] und darauf, wie diese auf dem Nintendo DS umgesetzt werden, eingegangen. Alle Abbildungen zeigen das Original-Brettspiel Einfach Genial (Abbildung 3.1), da die dementsprechenden Grafiken des Nintendo DS wegen seiner kleinen Bildschirme zu klein wären. Abbildung 3.1: Verpackung des Brettspiels Einfach Genial 23 KAPITEL 3. DAS SPIEL – EINFACH GENIAL (a) Einfärbiger Spielstein 24 (b) Zweifärbiger Spielstein Abbildung 3.2: Zwei Spielsteine des Brettspiels Einfach Genial. Einer mit zwei verschiedenen Farben (b) und einer mit nur einer Farbe (a). Abbildung 3.3: Der untere Spieler hat verloren, da sein blauer Stein weiter hinten ist 3.1.1 Spielziel Das Ziel des Spieles ist, durch Anlegen von Spielsteinen, die aus zwei zusammengefügten Sechsecken bestehen und in sechs verschiedenen Farben eingefärbt sein können (Abbildung 3.2), an Spielsteine mit übereinstimmenden Farben Punkte zu erzielen. Je mehr farblich übereinstimmende Spielsteine ausgehend vom neu angelegten Stein in gerader Linie liegen, desto mehr Punkte bekommt der Spieler dafür. Die erzielten Punkte werden auf einer Wertungstafel für jede Farbe gesondert gezählt. Der Spieler hat immer sechs Spielsteine vor sich, aus denen er einen zum Anlegen auswählen kann. Am Ende des Spieles bestimmt die Farbe, in der der Spieler am wenigsten Punkte hat, den Punktestand des Spielers. Es gewinnt der Spieler mit dem höchsten Punktestand. Sollten mehrere Spieler den gleichen Punktestand haben, liegt der Spieler vorne, der mehr Punkte in der nächst niedrigen Farbe hat. Abbildung 3.3 zeigt einen klaren Gewinner, Abbildung 3.4 zwei Spieler, die gleich viele Punkte in der niedrigsten Farbe haben. KAPITEL 3. DAS SPIEL – EINFACH GENIAL 25 Abbildung 3.4: Der obere Spieler hat verloren, da sein roter Stein weiter hinten ist 3.1.2 Spielverlauf Reihum kommt jeder Spieler an die Reihe. Ein Spielzug erfolgt immer nach dem gleichen Schema. Zuerst platziert der Spieler irgendeinen der sechs Spielsteine, die er zur Auswahl hat, auf dem Spielfeld auf zwei nebeneinander liegenden, freien Feldern. In der ersten Runde gibt es hierbei die Einschränkung, dass jeder Spieler an einem der schon auf dem Spielfeld vorhandenen Farbfelder, an das noch kein anderer Spieler angelegt hat, anlegen muss, wie es in Abbildung 3.5 gezeigt wird. Die Größe des Spielfeldes hängt von der Anzahl der Mitspieler ab. Der in Abbildung 3.5 dargestellte weiße Bereich ist das Spielfeld für zwei Spieler. Bei drei Mitspielern wird zusätzlich der hellgraue Bereich verwendet und bei vier Mitspielern kommt auch noch der dunkelgraue Bereich hinzu. Spielsteine können auch so platziert werden, dass einzelne Felder frei bleiben, die dann nicht mehr benützbar sind. Es muss nicht an bestehende Spielsteine angelegt werden, andere Spielsteine dürfen jedoch nie überdeckt werden. Wurde der Spielstein platziert, wird jede der beiden Farben auf dem Spielstein einzeln gewertet, auch wenn diese ident sind. Sternförmig werden von jeder Farbe aus alle Felder der selben Farbe, die in ununterbrochener, gerader Linie liegen, gezählt, also maximal in fünf Richtungen, da in Richtung der zweiten Hälfte des angelegten Steins nie gezählt wird. Das Werten eines neu angelegten Steins wird in Abbildung 3.7 für einen Stein mit zwei unterschiedlichen Farben und in Abbildung 3.6 für einen Stein mit zwei gleichen Farben dargestellt. Die erreichten Punkte für die beiden neu angelegten Felder werden dann KAPITEL 3. DAS SPIEL – EINFACH GENIAL 26 Abbildung 3.5: Spielfeld, nachdem alle 4 Spieler ihren ersten Spielstein angelegt haben auf der Punktetafel zu den bereits erzielten Punkten der jeweiligen Farbe addiert. Erreicht eine Farbe den Maximalwert 18 auf der Punktetafel (weitere Punkte in dieser Farbe verfallen), erhält der Spieler einen Bonuszug, den er spielen muss, bevor er neue Steine erhält. Da ein Stein zwei Farben haben kann, kann er auch zwei Bonuszüge auslösen, die dann ihrerseits wieder Bonuszüge auslösen können. Erreicht ein Spieler mit allen Farben den Wert 18, hat er automatisch gewonnen und das Spiel ist vorüber. Sobald ein Spieler keinen Spielstein mehr platzieren kann, weil keine zwei benachbarten Felder mehr frei sind, endet das Spiel. 3.2 Geplante Umsetzung Da das geplante Spiel durch das Vorbild des Brettspiels sehr genau definiert war, konnte die Umsetzung für den Nintendo DS bereits im Vorfeld gut geplant werden. Als erstes wurde eine grobe Liste der notwendigen Funktionen erstellt. • Anzeigen der Regeln • Startscreen • Hilfe KAPITEL 3. DAS SPIEL – EINFACH GENIAL (a) Zwei blaue Punkte (b) Vier blaue Punkte (c) Elf grüne Punkte Abbildung 3.6: Wertung eines Spielsteins (gelb umrandet) mit nur einer Farbe zu Beginn eines Spieles (a), in der Mitte (b) und gegen Ende des Spiels (c). • Eingabe der Benutzerdaten (Name, Server-Url) • Auswahl der Mitspieler • Spielfeldanzeige • GUI • Spielergebnis • Highscore • Sound • Netzwerk 27 KAPITEL 3. DAS SPIEL – EINFACH GENIAL (a) Ein blauer Punkt 28 (b) Ein oranger Punkt und zwei blaue Punkte (c) Zwei orange Punkte und vier blaue Punkte Abbildung 3.7: Wertung eines Spielsteins (gelb umrandet) mit zwei Farben zu Beginn eines Spieles (a), in der Mitte (b) und gegen Ende des Spiels (c). Mit diesen Funktionen wurde zunächst ein Ablaufplan für das Spiel erstellt. Abbildung 3.8 zeigt den Ablaufplan für das Netzwerkspiel und das Solospiel gegen einen oder mehrere Computergegner. Natürlich können auch beim Netzwerkspiel einige Spieler vom Computer gesteuert werden. Nachdem die Ablaufpläne erstellt waren, wurden als nächster Schritt alle aus der Sicht des Designs wichtigen Bereiche geplant. Da das Design am Nintendo DS sehr eingeschränkt ist, ist eine genaue Planung in diesem Bereich notwendig, um bei der Umsetzung Probleme zu vermeiden. Folgende Bereiche wurden als wichtig eingestuft und hinsichtlich Design durchgeplant: • Texteingabe • Spielfeldanzeige • GUI KAPITEL 3. DAS SPIEL – EINFACH GENIAL 29 Abbildung 3.8: Ablaufdiagramm des Spieles Einfach Genial DS 3.2.1 Planung der Texteingabe Da der Nintendo DS keine Tastatur hat, musste als erstes entschieden werden, wie die Texteingabe erfolgen sollte. Zur Auswahl standen dabei eine virtuelle Tastatur, die klassische Konsolentexteingabe, bei der mit dem Steuerkreuz der Buchstabe ausgewählt wird, und eine Zeichenerkennung, wie sie bei Handheld-Computern üblich ist. Als erstes wurde die Eingabe mittels des Steuerkreuzes evaluiert. Abbildung 3.9(a) zeigt die Texteingabe des Spieles Zelda [2] und Abbildung 3.9(b) die des Spieles Castlevania [10]. Diese Eingabemöglichkeit ist zwar langsam, dafür aber nicht anfällig für Falscheingaben und benötigt keine Erklärung, da normalerweise jeder Benutzer diese Form der Eingabe kennt. Die nächste Eingabeform, die getestet wurde, war das virtuelle Key- KAPITEL 3. DAS SPIEL – EINFACH GENIAL (a) Zelda 30 (b) Castlevania Abbildung 3.9: Konventionelle Texteingaben der Spiele Zelda (a) und Castlevania (b) die beide mit dem Steuerkreuz zu bedienen sind board. Hierbei wird ein Keyboard auf dem Touchscreen des Nintendo DS dargestellt. Der Benutzer wählt dann die jeweiligen Tasten mit dem Stift aus. Abbildung 3.10(a) (PicoChat System des Nintendo DS [15]) und Abbildung 3.10(b) (Texteingabe bei DSLinux [5]) zeigen zwei Beispiele dafür, wie konventionelle virtuelle Tastaturen bereits für den Nintendo DS umgesetzt wurden. Diese Form der Eingabe ist sehr intuitiv, da jeder Benutzer mit einer Tastatur umgehen kann. Allerdings ist der Touchscreen des Nintendo DS nur 256 × 192 Pixel groß und so bleiben bei einer konventionellen Darstellung für jede Taste nur 32 × 32 Pixel Platz. Da dies für eine Auswahl mittels eines Touchscreens relativ klein ist und die Tests gezeigt hatten, dass es ziemlich leicht ist, die falsche Taste auszuwählen, wurden noch alternative Tastaturlayouts getestet, wie zum Beispiel das Metropolis II [21] Tastaturlayout in Abbildung 3.11(c), das mit seinem hexagonalen Aufbau gut in das Design des Spieles Einfach Genial DS passen würde, und das DotNote [12] Tastaturlayout, bei dem die Tastenbelegung mehrfach umgeschalt werden kann. Abbildung 3.11(a) zeigt die primäre Tastenbelegung mit den am häufigsten verwendeten Buchstaben und Abbildung 3.11(b) die sekundäre Tastenbelegung mit den weniger häufig verwendeten Buchstaben. Die Ziffern und einige Sonderzeichen können über eine dritte Tastenbelegung eingegeben werden. Zeichenerkennung als Eingabemöglichkeit wurde als letztes getestet. Dabei zeichnet der Benutzer die Buchstaben nacheinander mit dem Stift auf den Touchscreen. Normalerweise werden Zeichen bei einer solchen Zeichenerkennung immer mit nur einem Strich gezeichnet. Es gibt mehrere unterschiedliche Schreibweisen. Genauer betrachtet wurden das Unistroke-Alphabet [6] (Abbildung 3.12(a)), das Graffiti-Alphabet [1] (Abbildung 3.12(b)) und das MDTIM-Alphabet [8] (Minimal Geräteabhängige Text-Eingabe-Methode) (Abbildung 3.12(c)). Die Tests haben gezeigt, dass diese Eingabemethode relativ schnell und je nach verwendetem Alphabet auch leicht zu erlernen ist. Es kann jedoch immer wieder vorkommen, dass Zeichen falsch erkannt werden. KAPITEL 3. DAS SPIEL – EINFACH GENIAL (a) PicoChat 31 (b) DSLinux Abbildung 3.10: Tastaturlayouts des DSLinux Projekts (b) und des im Nintendo DS inkludierten PicoChats (a) Nach dem Testen aller Eingabemöglichkeiten wurde die Entscheidung zugunsten einer Zeichenerkennung mit dem Graffiti-Alphabet getroffen. Die Eingabe mit dem Steuerkreuz wurde nicht gewählt, da das gesamte Spiel mit dem Touchscreen steuerbar sein sollte, ohne für die Texteingabe umgreifen zu müssen, die mittels einer virtuellen Tastatur nicht, weil sie nur schwierig ins Spieldesign implementierbar ist und auf dem Touchscreen in der konventionellen Form nur sehr klein dargestellt werden kann. Das Graffiti-Alphabet wurde gewählt, da es der Handschrift am ähnlichsten und deshalb leicht erlernbar ist. Aufgrund dieser Entscheidung wurde dann ein erstes Design der Texteingabe erstellt, das in Abbildung 3.13 zu sehen ist. Da bei der Zeichenerkennung oft Zeichen falsch erkannt werden, weil sie anderen Zeichen zu sehr ähneln (Zum Beispiel 0 und O), wurde ein Button eingeplant, mit dem das letzte eingegebene Zeichen durch ein ähnliches Zeichen ersetzt werden kann. 3.2.2 Planung der Spielfeldanzeige Während des Spiels sollte das Spielfeld auf dem oberen Bildschirm dargestellt werden und zwar in zwei Zoomstufen: einer Komplettansicht, um den Überblick behalten zu können, und einer Detailansicht, um Spielsteine zu platzieren. Das Hauptproblem war hier, dass Einfach Genial ein hexagonales Spielfeld hat, der Nintendo DS aber nur quadratische Kacheln unterstützt. Da bei einem Sechseck die Höhe nicht gleich der Breite ist, kann es nicht unverzerrt auf Quadrate aufgeteilt werden. Nach Tests mit mehreren Ansätzen wurde das Sechseck in der endgültigen Version so verzerrt, dass es sich auf 4 bzw. 16 Quadrate aufteilen lässt, wie in Abbildung 3.14 gezeigt wird. Für beide Zoomstufen wurde nun ein Set Kacheln (Abbildung 3.15) und damit dann das Design für die Spielfeldanzeige, das in Abbildung 3.16 zu sehen ist, erstellt. KAPITEL 3. DAS SPIEL – EINFACH GENIAL (a) DotNote Tastatur mit der primären Tastenbelegung 32 (b) DotNote Tastatur mit der sekundären Tastenbelegung (c) Metropolis II Tastaturlayout Abbildung 3.11: Die für die Verwendung auf dem Nintendo DS getestete alternative Tastaturlayouts. Die DotNote Tastatur (a–b) für den Palm und eine theoretische Tastatur (c) 3.2.3 Planung der GUI Über die GUI, die während des Spiels auf dem Touchscreen dargestellt wird, sollten alle Funktionen des Spiels gesteuert werden können. Außerdem sollten alle für das Spiel wichtigen Informationen dargestellt werden. Dazu gehörten eine verkleinerte Abbildung des Spielplans für das Positionieren der Spielsteine, die momentan zur Verfügung stehenden Spielsteine, die eigene Punktetafel und die Punktetafeln der Mitspieler, die für den aktuellen Zug verbleibende Zeit und die Steuerelemente für Sound, Zoom, usw. Aufgrund dieser Anforderungen wurde ein erster Entwurf erstellt, der in Abbildung 3.17 zu sehen ist. KAPITEL 3. DAS SPIEL – EINFACH GENIAL (a) Unistroke (b) Graffiti (c) MDTIM Abbildung 3.12: Die für das Spiel Einfach Genial DS getesteten Alphabete. Mit dem Unistroke Alphabet (a) eines der Ältesten, das sehr populäre Graffiti Alphabet (b) und ein theoretishes Alphabet (c) 33 KAPITEL 3. DAS SPIEL – EINFACH GENIAL Abbildung 3.13: Erstes Design der Texteingabe (a) Sechseck unterteilt in 4 bzw. 6 Quadrate (b) Sechseck unterteilt in 16 bzw. 24 Quadrate Abbildung 3.14: Sechsecke angepasst und in Quadrate unterteilt. Das unverzerrtes Sechseck ist hellgrau, das angepasste Sechseck grau und die 8 × 8 Pixel großen Quadrate sind schwarz dargestellt. Die kleinen Spielsteine (a) sind 16 Pixel hoch und die großen Spielsteine (b) sind 32 Pixel hoch. 34 KAPITEL 3. DAS SPIEL – EINFACH GENIAL Abbildung 3.15: Das für das Spiel Einfach Genial DS erstellte Set an Kacheln. Das schwarze Quadrat zeigt die Größe einer 8 × 8 Pixel großen Kachel. (a) Spielfeld-Design in kleiner Zoomstufe (b) Spielfeld-Design in großer Zoomstufe Abbildung 3.16: Designentwürfe für das Spielfeld des Spieles Einfach Genial DS in der kleinen Zoomstufe (a) und hineingezoomt (b). Abbildung 3.17: Erstes Design der GUI 35 KAPITEL 3. DAS SPIEL – EINFACH GENIAL 36 Abbildung 3.18: Geplante Klassenstruktur des Spiels Einfach Genial DS 3.3 Softwarearchitektur Bei der Softwarearchitektur gibt es aufgrund der Eigenheiten der Programmierung für den Nintendo DS einige Einschränkungen, vor allem in Bezug auf die Verwendung von Ressourcen. Deswegen wurde die Spielelogik vom Nintendo DS spezifischen Programmcode getrennt. Abbildung 3.18 zeigt die geplante Architektur, wobei der Nintendo DS spezifische Programmcode in der Game-Klasse gekapselt ist. Kapitel 4 Die Umsetzung Die Umsetzung eines Spiels für ein relativ unbekanntes System ist keine lineare Arbeit. Oft werden bei der Implementierung von neuen Funktionen ältere Funktionen überarbeitet oder ersetzt. Um das Verständnis zu erleichtern, wurde deswegen die Umsetzung nicht in chronologischer Reihenfolge geordnet, sondern so, wie sie erfolgen würde, wenn man das ganze Spiel noch einmal komplett neu programmieren würde. 4.1 Dateisystem Wenn man mit der Programmierung für den Nintendo DS und die beiden SDKs NdsLib und PaLib beginnt, wird man Ressourcen, wie Hintergründe, Sprites usw., immer über das Inkludieren der jeweiligen .c- und .h-Dateien in das Projekt verfügbar machen. Das wird bei größeren Projekten jedoch schnell zu einigen Problemen führen. • Ressourcen werden mit dem Programm geladen. Der Nintendo DS verfügt jedoch nur über 4 MB RAM für jede CPU. • Ressourcen können nur verändert werden, indem neu kompiliert wird. • Ressourcen sind nur in dem Programmbereich verfügbar, in den sie inkludiert wurden. • Ressourcen können im Programm nicht geändert und dann wieder gespeichert werden (Highscore, Speicherungen,...). Die Vorgehensweise, alle Ressourcen mitzukompilieren, entspricht auf dem PC jener, alle Ressourcen in die exe-Datei miteinzubinden. Genau wie am PC ist das nicht die optimale Lösung. In beiden Fällen sollten Ressourcen über das Dateisystem1 nachträglich geladen werden. Auf dem Nintendo 1 Englisch: Filesystem (FS) 37 KAPITEL 4. DIE UMSETZUNG 38 DS gibt es, wie auf dem PC, mehrere mögliche Dateisysteme. Anders als auf dem PC gibt es jedoch kein Betriebssystem, das eine einheitliche Schnittstelle zum Dateizugriff bietet. Durch das Betriebssystem wird es für den Entwickler am PC uninteressant, ob die Daten nun auf einer Festplatte, die mit FAT32 formatiert wurde, oder auf einer, die mit NTFS formatiert ist, liegen. 4.1.1 Nintendo DS Dateisystem Das Nintendo DS Filesystem ist das von der offiziellen Nintendo SDK verwendete Dateisystem. Es gibt bereits einige Open Source Tools, die Dateien zu diesem Dateisystem hinzufügen und wieder daraus entfernen können. Jedoch gibt es noch keine Open Source Bibliotheken, die es ermöglichen, auf das Dateisystem vom Programmcode aus zuzugreifen. Das Dateisystem wird gemeinsam mit dem Programmcode zu einem Image zusammengefasst, d.h.: Auf der Speicherkarte befindet sich nur eine einzige Datei, in der sich das eigentliche Spiel und alle Dateien befinden. Da sich dieses Image auf dem ROM-Teil der Speicherkarte befindet, kann das Dateisystem nur gelesen werden und bietet keine Möglichkeit, Daten abzuspeichern. 4.1.2 Gameboy Advance Dateisystem Das Gameboy Advance Dateisystem ist jenes, das als erstes von den Open Source SDKs für den Nintendo DS verwendet wurde. Da vieles in der Hardware ähnlich zu der des Gameboy Advance ist, konnte das Dateisystem - wie einiges andere auch - mit relativ wenigen Änderungen übernommen werden. Das Dateisystem befindet sich, wie das des Nintendo DS, gemeinsam mit dem Programmcode in einem Image und es gelten die gleichen Einschränkungen. 4.1.3 PaLib Dateisystem Das PaLib Dateisystem ist eine Erweiterung des Gameboy Advance Dateisystems für die Verwendung mit dem PaLib SDK. Dieses Dateisystem wurde für das Spiel Einfach Genial DS verwendet, da es für dieses Dateisystem für viele der benötigten Ressourcen Funktionen zum Laden gibt. Wie bei den Nintendo DS und Gameboy Advance Dateisystemen, wird das Dateisystem an die NDS-Datei angefügt. Der Beginn des Dateisystems wird über einen Identifizierungs-String definiert, danach kommt ein Header, dann der Directory Table und danach die Binärdaten. Der Aufbau der fertigen NDS-Datei mit inkludiertem Dateisystem wird in der Abbildung 4.1 dargestellt. Nach dem Kompilieren des Spiels muss an die erstellte .nds-Datei das Dateisystem angefügt werden. Das erfolgt über das Werkzeug PAFS.exe, KAPITEL 4. DIE UMSETZUNG 39 Abbildung 4.1: Datenstruktur einer NDS-Datei mit einem PAFS das über die Befehlseingabe angesteuert wird. Beim Spiel Einfach Genial DS wurde zum Beispiel mit dem Befehl: PAFS.exe EinfachGenial.sc.nds ” ressources“, der Ordner ressources“, in dem sich alle benötigten Dateien ” befanden, an die .nds-Datei des Spiels angefügt. 4.1.4 FAT Dateisystem Die Bibliothek libfat ermöglicht, auf Speicherkarten in beiden Steckplätzen (NDS- und GBA-Karteneinschub) zuzugreifen und Daten von ihnen zu lesen sowie auf sie zu schreiben, jedoch nur, wenn für das verwendete Gerät FAT-Treiber vorhanden sind. Nachdem es eine Vielzahl von verschiedenen Geräten gibt, wurden diese Treiber seit 2006 nicht mehr in die libfat inkludiert. Der Benutzer muss das Programm für sein Gerät mit den richtigen Treiberdaten patchen. Dieses Patchsystem nennt sich Dynamically Linked ” KAPITEL 4. DIE UMSETZUNG 40 Device Interface for libfat“ 2 und es stehen bereits Patches für mehr als 20 Geräte zur Verfügung. Dieses Dateisystem wurde im Spiel Einfach Genial DS aus dem Grund nicht verwendet, da es bei der Entwicklung eines Nintendo DS Spiels mit der offiziellen SDK nicht möglich ist, in dieser Weise auf die Speicherkarte des Spiels zuzugreifen (Speicherkarten der Spiele sind größtenteils ROM), und das Spiel möglichst nahe am Vorbild der offiziellen Spiele bleiben sollte. 4.1.5 Verwendung im Spiel Für das Spiel Einfach Genial DS wurde das PaLib Dateisystem verwendet, da alle benutzten Ressource-Dateien in der PaLib verwendet werden und es für dieses Dateisystem Funktionen zum Laden aller Ressourcen gibt. Der erste Schritt, um das PaLib Dateisystem verwenden zu können, ist, es zu initialisieren, wie in Listing 4.1 Zeile 1. Anders als die meisten anderen Initialisierungen in der SDK, die gerne mehrfach ausgeführt werden, um die Einstellungen zu resetten, darf diese jedoch nur einmal ausgeführt werden. Wird die Init() Funktion ein zweites Mal aufgerufen, bleibt das Programm an dieser Stelle hängen. Nach der Initialisierung kann das Dateisystem an jeder beliebigen Stelle im Code verwendet werden, um Ressourcen zu laden. Die Identifizierung der Dateien, die man laden möchte, erfolgt über eine Integer-ID, die der Position der Datei im Dateien-Array entspricht. Zugriff auf Informationen zu den Dateien und Ordnern, wie Name, Erweiterung und Größe, erhält man über drei globale Strukturen: • PA FSSys – Enthält die Anzahl der Dateien (NFiles) und Ordner (NFolder) • PA FSFile – Array mit den Informationen zu jeder Datei, wie Name (Name), Erweiterung (Ext), Größe (Length) und Position der Daten der Datei (FilePos) • PA FSFolder – Array mit den Informationen zu jedem Ordner, wie Name (Name), Anzahl der Unterordner (NFolder), Anzahl der Dateien (NFiles), Index des ersten Unterordners (FirstFolder) und Index der ersten Datei (FirstFile) Innerhalb des Dateisystems kann nach Dateien und Ordnern gesucht werden (siehe Listing 4.1 Zeile 4 und Zeile 15). Beim Suchen nach Dateien ist der erste Parameter der Startindex im Dateien-Array. Es kann nach Name und/oder Erweiterung gesucht werden. Wenn man alle Dateien mit der gleichen Endung benötigt, kann man sich diese wie in Listing 4.1 Zeile 35 in ein Array speichern lassen. 2 Homepage: http://dldi.drunkencoders.com KAPITEL 4. DIE UMSETZUNG 1 41 Listing 4.1: Codebeispiel: Verwendung des PaLib Dateisystems P A _ F S I n i t (); 2 3 4 5 // S u c h e n nach einer s p e z i e l l e n Datei int image = P A _ F S G e t F i l e (0 , " Titel " , " jpg" ); P A _ L o a d F S I m a g e (0 , image ); 6 7 8 9 10 int int int int n A u d i o = 0; c A u d i o = 0; a F o l d e r = -1; audio [ 1 0 0 ] ; 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 if ( u s e A u d i o F o l d e r ) { // Nur Musik - D a t e i e n im Audio - O r d n e r v e r w e n d e n a F o l d e r = P A _ F S G e t F o l d e r ( " audio " ); // Alle D a t e i e n mit der r i c h t i g e n E n d u n g ins Array // s p e i c h e r n if ( a F o l d e r != -1) { for ( int i = 0; i < P A _ F S F o l d e r[ a F o l d e r ]. N F i l e s; i ++) { if ( s t r c m p( P A _ F S F i l e[ P A _ F S F o l d e r[ a F o l d e r ]. F i r s t F i l e + i ]. Ext , " mod" ) == 0) { audio [ n A u d i o ]= i ; n A u d i o ++; } } } } else { // Alle Music - D a t e i e n v e r w e n d e n n A u d i o = P A _ F S S e a r c h E x t ( audio , " mod" ); } 37 38 39 40 41 42 43 if ( n A u d i o) { P A _ P l a y F S M o d ( audio [ c A u d i o ]); P A _ O u t p u t T e x t (0 , 0 , 0 , " Now P l a y i n g % s " , P A _ F S F i l e[ audio [ c A u d i o ]]. Name ); } KAPITEL 4. DIE UMSETZUNG (a) Logo Einfach Genial DS 42 (b) DS-Download-Spiel Logo Abbildung 4.2: Die beiden Logos des Spieles Einfach Genial DS. Das Logo das bei der Spielauswahl (a) und das Logo das beim Spielen über WLAN angezeigt würde (b). 4.2 2D-Grafik Vor dem Nintendo DS konnten tragbare Konsolen nur 2D-Grafik darstellen. Auch wenn der Nintendo DS bereits 3D-Grafik darstellen kann, ist die 2D-Grafik noch immer ein sehr wichtiger Bestandteil. Da Einfach Genial DS eine Umsetzung eines Brettspiels ist und sich deswegen sehr gut in 2D-Grafik darstellen lässt, wurden für die Spiele-Grafiken nur 2D verwendet. Die 2DGrafik, die im Spiel verwendet wurde, kommt in einigen unterschiedlichen Verwendungen vor. In den nächsten Unterkapiteln wird nur auf diese Verwendungsformen in der Reihenfolge ihres Auftretens im Spiel eingegangen. 4.2.1 Logos Die ersten Grafiken, die im Spiel Verwendung finden, sind die beiden Logos, die das Spiel im Menü des Nintendo DS darstellen. Eines der Logos dient für die normale Auswahl des Spiels, das zweite, falls das Spiel über die DSDownload-Spiel-Funktion ausgewählt wird. Die Logos benötigen deswegen besondere Beachtung, weil sie noch stärkeren Beschränkungen unterworfen sind als die restlichen Grafiken. Das Logo für die normale Auswahl ist ein 32 × 32 Pixel großes Bitmap. Da von der Palette im Aufbau der Nintendo DS Datei nur 32 Bit für die Palette des Logos reserviert sind, dürfen für das Logo maximal 16 verschiedene Farben verwendet werden. Abbildung 4.2(a) zeigt das Logo, das für Einfach Genial DS verwendet wurde. Das Logo für das DS-Download-Spiel ist ein 104 × 16 Pixel großes Bitmap, für das nur zwei verschiedene Farben verwendet werden können. Auch beim Spiel Einfach Genial DS wurde ein DS-Download-Spiel-Logo inkludiert, das in Abbildung 4.2(b) gezeigt wird, obwohl dieser Spielmodus zur Zeit nicht für das Spiel vorgesehen ist. KAPITEL 4. DIE UMSETZUNG 4.2.2 43 Hintergründe Alle Grafiken, die sich nicht oder nur als Gesamtes bewegen, werden beim Nintendo DS als Hintergründe3 dargestellt. Auf jedem Bildschirm4 können bis zu vier Hintergründe gleichzeitig darstellt werden. Der Speicherplatz für die Hintergründe ist jedoch begrenzt, weswegen manche Arten von Hintergründen nicht auf allen vier Schichten gleichzeitig vorhanden sein können. Die Schichten sind durchnummeriert mit 0 bis 3, wobei Schicht 0 die höchste Schicht ist (d.h. ein Hintergrund auf Schicht 0 verdeckt einen Hintergrund auf Schicht 1). Es gibt vier verschiedene Arten von Hintergründen: • 16 Bit Hintergründe • 8 Bit Hintergründe • Gekachelte Hintergründe • Drehbare Hintergründe 16 Bit Hintergründe 16 Bit Hintergründe sind am einfachsten zu verwenden, brauchen jedoch die meisten Ressourcen. Es kann pro Bildschirm immer nur maximal ein 16 Bit Hintergrund auf Schicht 3 dargestellt werden, da dieser 75% des für die Hintergründe zur Verfügung stehenden Speichers verbraucht. Dafür kann auf 16 Bit Hintergründen gezeichnet werden. Der große Vorteil von 16 Bit Hintergründen ist, dass sie einfach über das Dateisystem geladen werden können. Geladen werden können Bitmap-, Jpeg-, Raw- und Gif-Dateien, die 256 × 192 Pixel groß sein müssen. Dadurch sind 16 Bit Hintergründe ideal für die meisten einfachen Bereiche in Spielen, wie etwa Spielauswahl, Menü, etc., da hier nicht mehrere Schichten benötigt werden. Außerdem wird durch das Auslagern der Bildinformationen in das Dateisystem die Größe des Programms klein gehalten. Das ist auch deswegen wichtig, da das Programm in den RAM des Nintendo DS geladen werden muss, und hierbei für jede der beiden CPU’s nur 4 MB RAM zur Verfügung stehen. Im Spiel Einfach Genial DS werden zum Beispiel für den Startbildschirm, die Spielauswahl und als GUI Hintergrund 16 Bit Hintergründe verwendet, wie in den Abbildungen 4.3 zu sehen ist. Geladen werden die Hintergründe über das PAFS aus Jpeg-Dateien, wie im Listing 4.2 gezeigt wird. 3 4 Englisch: Background (BG) Englisch: Screen KAPITEL 4. DIE UMSETZUNG (a) Startbildschirm 44 (b) Spielauswahl Abbildung 4.3: Zwei der Hintergründe, die aus dem Dateisystem geladen werden. Der Hintergrund der gleich anch dem Starten gezeigt wird (a) und der Hintergrund bei der Auswahl des Spielmodus (b). 1 2 3 4 5 6 7 8 Listing 4.2: Laden eines 16 Bit Hintergrunds aus dem Dateisystem // H i n t e r g r u n d i n i t i a l i s i e r e n P A _ I n i t 1 6 b i t B g (0 , 3); // D a t e i s y s t e m i n i t i a l i s i e r e n P A _ F S I n i t (); // Datei s u c h e n int image = P A _ F S G e t F i l e (0 , " Titel " , " jpg" ); // H i n t e r g r u n d laden P A _ L o a d F S I m a g e (0 , image ); Die Möglichkeit, auf 16 Bit Hintergründen zu zeichnen, wird beim Spiel Einfach Genial DS bei der Punkteanzeige im Spiel verwendet. Da die Punkte durch Rechtecke dargestellt werden, können sie über eine einfache Funktion, die in Listing 4.3 dargestellt wird, gezeichnet werden. Da 16 Bit Hintergründe keine Palette verwenden, können alle Farben zum Zeichnen verwendet werden. Bei allen anderen Hintergründen könnten nur die Farben der Palette zum Zeichnen verwendet werden, was sehr einschränkt, da dazu beim Zeichnen die Palette bekannt sein muss. Die Punkte werden gezeichnet, indem die Farbwerte aus einem Bitmap (Abbildung 4.4(b)), in dem die Punktgrafik gespeichert ist, in den Hintergrund (Abbildung 4.4(a)) kopiert werden. Um den gezeichneten Bereich wieder zu löschen, wird einfach ein graues Rechteck darüber gezeichnet. Im Spiel wird also die Funktion drawBlock immer zweimal gleichzeitig aufgerufen, einmal, um den Punkt an der alten Position zu löschen, und ein zweites Mal, um den Punkt an der neuen Position zu zeichnen. 1 2 3 4 Listing 4.3: Zeichnen der Punkte auf dem GUI Hintergrund void d r a w B l o c k( int x , int y , bool large , bool full ) { // O f f s e t in der Punkte - G r a f i k b e r e c h n e n int count = 16 - 16 * large ; KAPITEL 4. DIE UMSETZUNG (a) GUI Hintergrund (b) Punkte-Grafik (Maßstab 2:1) Abbildung 4.4: GUI Hintergrund (a) und die Punkte-Grafik (b), die zum Zeichnen der Punkte verwendet wird if ( full ) { // R e c h t e c k aus der Punkte - G r a f i k in den // H i n t e r g r u n d k o p i e r e n for ( int i = 0; i < 4; i ++) for ( int j = 0; j < 2 + 2 * large ; j ++) { P A _ P u t 1 6 b i t P i x e l (0 , x + i , y + j , P u n k t e _ B i t m a p[ count ]); count ++; } } else { // G r a u e s R e c h t e c k z e i c h n e n // F a r b w e r t 49680 ist im F o r m a t // A 1 R 5 G 5 B 5 -> 1 16 16 16 P A _ D r a w 1 6 b i t R e c t (0 , x , y , x + 4 , y + 2 + 2 * large , 4 9 6 8 0 ) ; } 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 } 45 KAPITEL 4. DIE UMSETZUNG 46 8 Bit Hintergründe 8 Bit Hintergründe sind ähnlich wie 16 Bit Hintergründe, sie verwenden jedoch im Gegensatz zu diesen eine Palette. Der Speicherverbrauch reduziert sich genau so wie die Bit-Anzahl um die Hälfte auf 37,5% des verfügbaren Speichers. Aus der Datei geladen können 8 Bit Hintergründe nur aus Gifoder Raw-Dateien werden, wobei hier einige Änderungen im Code notwendig sind, wie Listing 4.4 zeigt. 1 2 3 4 5 6 7 8 9 Listing 4.4: Laden eines 8 Bit Hintergrunds aus dem Dateisystem // H i n t e r g r u n d i n i t i a l i s i e r e n P A _ I n i t 8 b i t B g (1 , 3); // D a t e i s y s t e m i n i t i a l i s i e r e n P A _ F S I n i t (); // Datei s u c h e n int image = P A _ F S G e t F i l e (0 , " Titel " , " gif" ); // Z e i g e r auf die Datei e r z e u g e n und // damit den H i n t e r g r u n d laden P A _ L o a d G i f (1 , P A _ P A F S F i l e ( image )); Im Spiel Einfach Genial DS werden jedoch keine Bilder auf einen 8 Bit Hintergrund geladen, da der Speicher immer ausreicht, um einen 16 Bit Hintergrund zu verwenden. Ein 8 Bit Hintergrund wird jedoch für die Schicht, auf der der Benutzer bei der Texterkennung zeichnet, verwendet, da die Texterkennung am besten mit einem 8 Bit Hintergrund funktioniert. In Listing 4.5 wird die Initialisierung des 8 Bit Hintergrunds gezeigt, die unterschiedlich zu der des 16 Bit Hintergrunds ist, da hier noch die Zeichenfarbe in der Palette gesetzt werden muss. 1 2 3 Listing 4.5: Zeichnen auf einem 8 Bit Hintergrund P A _ I n i t 8 b i t B g (0 , 3); // Auf P o s i t i o n 1 der P a l e t t e die Farbe Weiß P A _ S e t B g P a l C o l (0 , 1 , P A _ R G B (31 , 31 , 31)); 4 5 6 7 // Beim Z e i c h n e n nur noch die // Paletten - P o s i t i o n der Farbe a n g e b e n P A _ 8 b i t D r a w (0 , 1); Gekachelte Hintergründe Bei gekachelten Hintergründen5 werden die Bilder aus einzelnen Kacheln zusammengesetzt. Die Funktionsweise ist gleich wie bei einem Bild mit Farbpalette, wo im Bild nur noch die Indizes auf die Palette gespeichert werden müssen und nicht mehr die komplette Farbinformation. Der Hintergrund speichert nur noch die Indizes auf die Kacheln6 , wobei die Kacheln selbst 5 6 Englisch: Tilemap Englisch: Tiles KAPITEL 4. DIE UMSETZUNG Abmessungen 256 512 1024 2048 4096 × × × × × 256 512 1024 2048 4096 Kacheln 32 64 128 256 512 × × × × × 32 64 128 256 512 Speicherverbrauch 16 Bit Hintergrund 128 512 2048 8192 32768 47 Speicherverbrauch gekachelter Hintergrund gesamt 66,5 72,5 96,5 192,5 576,5 Tabelle 4.1: Vergleich des Speicherverbrauchs eines 16 Bit Hintergrunds mit einem gekachelten Hintergrund, der alle 1024 Kacheln und eine volle Palette mit 256 Farben verwendet (Alle Speicherangaben in kB) wieder eine Farbpalette verwenden. Beim Nintendo DS sind die einzelnen Kacheln immer 8 × 8 Pixel groß. Die Summe der Kacheln, die ein Hintergrund verwenden kann, sind in einem Set7 zusammengefasst. Beim Nintendo DS können bis zu 1024 Kacheln in einem Set sein, wodurch im Hintergrund die Indizes der Kacheln theoretisch mit 10 Bit gespeichert werden könnten. Da jedoch 16 Bit einfacher zu verarbeiten sind und dadurch noch Zusatzinformationen - wie z. B., welche Palette verwendet, oder, ob die Kachel gespiegelt dargestellt werden soll - mit abgespeichert werden können, werden die Indizes in 16 Bit gespeichert. Wenn kleine Bilder als Hintergrund verwendet werden, fällt der Unterschied im Speicherverbrauch noch nicht so stark aus, da bei einem 256 × 256 großen Bild noch keine Kachel mehr als einmal verwendet werden muss. Dieses Bild würde gekachelt 32 × 32 Kacheln groß sein, was genau 1024 Kacheln in Summe entspricht. Da in einem Set 1024 Kacheln sein können, kann das gesamte Set aus uniquen Kacheln bestehen. Doch selbst dann wäre es kleiner als ein 16 Bit Hintergrund derselben Größe. Ein 256 × 256 Pixel großer 16 Bit Hintergrund benötigt immer 128 kB (256 × 256 × 2 Byte) im Speicher. Ein gekachelter Hintergrund benötigt 512 Byte für die Palette, maximal 64 kB (8 × 8 × 1024) für das Set der verfügbaren Kacheln und 2 kB (32 × 32 × 2 Byte) für den eigentlichen Hintergrund, was in Summe maximal 66,5 kB sind. Werden Bilder verwendet, die größer als 256 × 256 Pixel sind, steigt die Vorteilhaftigkeit von gekachelten Hintergründen sehr schnell. Denn auch bei sehr großen gekachelten Hintergründen, wie etwa in Abbildung 4.5 gezeigt, bleibt die Größe des Kachel-Sets mit maximal 64 kB konstant, wodurch der Speicherverbrauch sehr viel langsamer ansteigt, wie Tabelle 4.1 zeigt. Im Emulator kann man die verschiedenen Sets an Kacheln, die zur Verfügung stehen, betrachten. Abbildung 4.6 zeigt die Kacheln, die für die Darstel7 Englisch: Tileset KAPITEL 4. DIE UMSETZUNG 48 Abbildung 4.5: Sehr großer gekachelter Hintergrund mit 2048 × 2048 Pixel aus einem der Beispiele der PaLib lung des Bildes aus Abbildung 4.5 benötigt werden. Da der Bildschirm des Nintendo DS nur 256 × 192 Pixel misst, wird nicht immer das gesamte Bild aus den Kacheln zusammengesetzt, sondern nur ein 512 × 256 Pixel großer Bereich, der ausreicht, um den Hintergrund bewegen zu können, ohne dass Lücken entstehen. Bei der Bewegung werden jedoch immer nur die Bereiche aktualisiert, die um den Bildschirmbereich herum liegen. Die Abbildungen 4.7(a) und 4.7(b) zeigen, wie sich das Bild im Speicher verändert, wenn der Bildschirm bewegt wird. Beim Spiel Einfach Genial DS werden gekachelte Hintergründe an mehreren Stellen verwendet. Bei der Texteingabe wird aus Platzgründen das Hintergrundbild als gekachelter Hintergrund auf Schicht 2 geladen, da auf Schicht 3 bereits ein 8 Bit Hintergrund ist, der zum Zeichnen der Eingabe verwendet wird. Da dieser gekachelte Hintergrund nicht verschoben wird, ist das Laden relativ einfach. Nachdem die Grafik erstellt wurde, müssen Bereiche, die transparent sein sollen, in einer der Farben Grün, Magenta, Weiß oder Schwarz markiert werden, je nachdem, welche der Farben im Bild nicht vorkommt. Abbildung 4.8 zeigt das Bild für die Texteingabe, bei dem der transparente Bereich grün markiert wurde. KAPITEL 4. DIE UMSETZUNG 49 Abbildung 4.6: Kachel-Set der Abbildung 4.5 Das Bild muss als indiziertes Bild mit maximal 256 verschiedenen Farben gespeichert werden, um für einen gekachelten Hintergrund verwendbar zu sein. Beim Spiel Einfach Genial DS wurde als Dateiformat Png verwendet, Bitmap und Gif wären jedoch ebenfalls möglich. Der nächste Schritt ist, das Bild in das bei der PaLib mitgelieferte Werkzeug PaGfx zu laden. Hier müssen die transparente Farbe und die gewünschte Art der Ausgabe, wie in diesem Fall als EasyBG, der für die meisten gekachelten Hintergründe funktionieren sollte, gewählt werden. Es können mehrere Bilder gleichzeitig zum Umwandeln geladen werden, was vor allem dann sinnvoll ist, wenn die Bilder zum Beispiel die gleiche Palette verwenden sollen. Abbildung 4.9 zeigt das Werkzeug PaGfx mit den Bildern für die Texteingabe und allen Einstellungen. Das Werkzeug PaGfx erzeugt beim Umwandeln eine Header- und eine Source-Datei, die dann ins Programm eingebunden werden müssen, wie zum Beispiel in den Listings 4.6 und 4.7. Der Hintergrund kann danach mit der Funktion void PA_EasyBgLoad(u8 screen, u8 bg_number, PAGfx bg_name) auf eine der Schichten geladen werden. 1 2 Listing 4.6: Hintergründe Include Datei // H i n t e r g ru ndart , Breite , Höhe e x t e r n const int T e x t _ I n p u t _ I n f o [3]; 3 4 5 6 // H i n t e r g r u n d d a t e n e x t e r n const u n s i g n e d short T e x t _ I n p u t _ M a p [768] _ _ a t t r i b u t e _ _ (( a l i g n e d (4))) ; KAPITEL 4. DIE UMSETZUNG (a) Gekachelter Hintergrund im Speicher vor dem Bewegen (b) Gekachelter Hintergrund im Speicher nach dem Bewegen Abbildung 4.7: Veränderungen eines gekachelten Hintergrunds im Speicher während des Bewegens des Hintergrunds. Die Ausgangssituation (a) wird der Endsituation (b) gegenübergestellt. Abbildung 4.8: Grafik für einen gekachelten Hintergrund mit grün hinterlegtem transparenten Bereich 50 KAPITEL 4. DIE UMSETZUNG 51 Abbildung 4.9: Gekachelte Hintergründe für die Umwandlung im Werkzeug PaGfx geladen 7 8 9 10 // Kachel - Set e x t e r n const u n s i g n e d char T e x t _ I n p u t _ T i l e s [ 3 6 9 2 8 ] _ _ a t t r i b u t e _ _ (( a l i g n e d ( 4 ) ) ) ; 11 12 13 14 1 2 // F a r b p a l e t t e e x t e r n const u n s i g n e d short T e x t _ I n p u t _ P a l [102] _ _ a t t r i b u t e _ _ (( a l i g n e d (4))) ; Listing 4.7: Hintergründe Source Datei // C Datei mit H i n t e r g r u n d d a t e n # i n c l u d e " T e x t _ I n p u t. c " 3 4 5 // C Datei mit P a l e t t e d a t e n # i n c l u d e " T e x t _ I n p u t. pal . c " Neben der Verwendung als Speicherplatz sparender Hintergrund werden gekachelte Hintergründe im Spiel Einfach Genial DS noch für die Darstellung des Spielfeldes verwendet, da hier das Spielfeld im Programm aus den Kacheln zusammengesetzt werden kann, was sehr schnell geht, und dabei keine langsamen Zeichenfunktionen verwendet werden müssen. Für die Darstellung des Spielfelds wurde als erstes das in Kapitel 3.2.2 erstellte Set an Kacheln mit dem PaGfx Werkzeug und einem Beispielspielfeld umgewandelt und ins Programm eingebunden, wie Listing 4.8 und 4.9 zeigen, wobei hier die Hintergrunddaten von einem Array auf einen Pointer geändert wurden, da ja der Beispielhintergrund nicht benötigt wird, weil die Hintergrund-Daten vom Programm aus zusammengestellt werden. Die Erstellung der Hintergrund-Daten aus den Spielfeldinformationen wird im Kapitel 4.5 genauer behandelt. Geladen wird der gekachelte Hintergrund KAPITEL 4. DIE UMSETZUNG 52 des Spielfelds dann mit der normalen Funktion zum Laden eines gekachelten Hintergrunds. 1 2 Listing 4.8: Spielfeld Include Datei // H i n t e r g ru ndart , Breite , Höhe e x t e r n int G a m e M a p _ I n f o [3]; 3 4 5 6 // H i n t e r g r u n d d a t e n ( noch keine Daten d e f i n i e r t) e x t e r n u n s i g n e d short * G a m e M a p _ M a p _ _ a t t r i b u t e _ _ (( a l i g n e d ( 4 ) ) ) ; 7 8 9 10 // Kachel - Set e x t e r n const u n s i g n e d char G a m e M a p _ T i l e s [ 1 9 7 7 6 ] _ _ a t t r i b u t e _ _ (( a l i g n e d ( 4 ) ) ) ; 11 12 13 14 // F a r b p a l e t t e e x t e r n const u n s i g n e d short G a m e M a p _ P a l [24] _ _ a t t r i b u t e _ _ (( a l i g n e d ( 4 ) ) ) ; Listing 4.9: Spielfeld Source Datei 1 2 // C Datei mit Kachel - Daten # i n c l u d e " gfx / G a m e M a p. c " 3 4 5 6 // P o i n t e r für die s p ä t e r e n S p i e l f e l d d a t e n u n s i g n e d short * G a m e M a p _ M a p _ _ a t t r i b u t e _ _ (( a l i g n e d ( 4 ) ) ) ; 7 8 9 // C Datei mit P a l e t t e d a t e n # i n c l u d e " gfx / G a m e M a p. pal . c " Drehbare Hintergründe Drehbare Hintergründe8 sind eine Sonderform, da sie gesondert initialisiert werden müssen. Vom Aufbau her unterscheiden sie sich nicht von normalen gekachelten Hintergründen, jedoch haben sie mehr Funktionen, wie neben dem im Titel steckenden Drehen“ auch noch die Möglichkeit zu skalieren ” und den Drehmittelpunkt festzulegen. Um auf einem Bildschirm einen drehbaren Hintergrund verwenden zu können, muss der Videomodus des Bildschirms mit der Funktion PA_SetVideoMode auf 1 (Drehbarer Hintergrund auf Schicht 3) oder 2 (Drehbare Hintergründe auf Schicht 2 und 3) gesetzt werden. Ein kurzes Beispiel für die Verwendung von drehbaren Hintergründen zeigt Listing 4.10. Im Spiel Einfach Genial DS werden drehbare Hintergründe zurzeit noch nicht verwendet, jedoch ist es geplant, die Ansicht des Spielfeldes auf der GUI durch einen drehbaren Hintergrund, der skaliert ist, zu ersetzten und dann das wirkliche Spielfeld dort darzustellen. 8 Englisch: Rotating Backgrounds (RotBG) KAPITEL 4. DIE UMSETZUNG 1 2 53 Listing 4.10: Beispiel eines rotierbaren Hintergrunds // D r e h b a r e n H i n t e r g r u n d auf S c h i c h t 3 a k t i v i e r e n P A _ S e t V i d e o M o d e (0 , 1); 3 4 5 6 7 8 9 PA_LoadPAGfxRotBg( 0 , // B i l d s c h i r m 3 , // Hintergrund - S c h i c h t Tiles_1 , // Name des mit PaGfx // u m g e w a n d e l t e n H i n t e r g r u n d s 1); // Neu b e g i n n e n wenn Ende e r r e i c h t 10 11 12 13 14 15 16 17 // Options - V a r i a b l e n s32 s c r o l l x = 0; s32 s c r o l l y = 0; s32 r o t c e n t e r x = 0; s32 r o t c e n t e r y = 0; s16 angle = 0; s32 zoom = 256; 18 19 20 21 22 23 24 25 26 27 while ( true ) { // B e w e g e n mit dem S t e u e r k r e u z zoom += Pad . Held . Start - Pad . Held . S e l e c t; s c r o l l x += Pad . Held . Right - Pad . Held . Left ; s c r o l l y += Pad . Held . Down - Pad . Held . Up ; r o t c e n t e r x += Pad . Held . A - Pad . Held . Y ; r o t c e n t e r y += Pad . Held . B - Pad . Held . X ; angle += Pad . Held . R - Pad . Held . L ; 28 // H i n t e r g r u n d v e r ä n d e r n P A _ S e t B g R o t (0 , 3 , scrollx , scrolly , rotcenterx , rotcentery , angle , zoom ); 29 30 31 32 // W a r t e n auf neu Z e i c h n e n P A _ W a i t F o r V B L (); 33 34 35 } 4.2.3 Sprites Sprites sind rechteckige Grafiken, die sich vor den Hintergründen bewegen. Der große Vorteil der Sprites ist, dass sie bewegt werden können, ohne dass der Hintergrund dahinter komplett neu gezeichnet werden muss. Wie Abbildung 4.10 zeigt, wird einfach nur der Hintergrund an der Stelle, wo das Sprite war, wieder hergestellt und das Sprite an der neuen Position gezeichnet. Sprites können in allen Farbmodi des Nintendo DS dargestellt werden, jedoch ist der übliche die Darstellung als indizierte Farben mit einer 256 Farben großen Palette, wobei eine davon transparent sein kann. Die Abmessungen sind auf einige Kombinationen von Breiten und Höhen eingeschränkt, KAPITEL 4. DIE UMSETZUNG Höhe / Breite 8 16 32 64 8 8×8 8×16 8×32 54 16 16×8 16×16 16×32 32 32×8 32×16 32×32 32×64 64 64×32 64×64 Tabelle 4.2: Sprite Abmessungen welche Tabelle 4.2 entnommen werden können. Die Sprite-Daten sind in der Hardware auf vier verschiedene Orte pro Bildschirm verteilt, was ein paar Einschränkungen hinsichtlich der Verwendung mit sich bringt. Die Farbpaletten der Sprites sind zusammen in einem separaten Speicher, der Platz für 16 Paletten bietet. Der nächste Speicherort beinhaltet die Bilddaten des Sprites und der dritte bietet Platz für 32 Parametersets, die Rotations- und Zoomdaten beinhalten können. Der letzte Speicher fügt die anderen drei Speicherorte zu einem kompletten Sprite zusammen. Er wird im Englischen oft als OAM (Object Attribute Memory) bezeichnet, da er 128 Parametersets für die Sprites beinhalten kann. In diesen Parametersets wird festgelegt, welche Palette, welcher Farbmodus, welche Bilddaten und welches Rotations- und Zoomset verwendet werden. Außerdem sind hier noch die Position auf dem Bildschirm, die Größe, die Spiegelung, die Priorität (Tiefenebene), der Mosaik-Effekt und die Transparenz festgelegt. Die Bilddaten für die Sprites müssen, wie bei den gekachelten Hintergründen, mit dem PaGfx Werkzeug umgewandelt und die dabei erzeugten Dateien inkludiert werden. Abbildung 4.11 zeigt die Einstellungen, die bei dem PaGfx Werkzeug für das Spiel Einfach Genial DS verwendet wurden und Listing 4.11 und Listing 4.12 zeigen, wie die erzeugten Dateien inkludiert werden. Werden mehrere Sprites benötigt können sie gemeinsam umgewandelt und inkludiert werden. Dazu müssen sie auf der Grafik untereinander angeordnet werden. Alle Sprites, die im Spiel Einfach Genial DS verwendet wurden, werden in Abbildung 4.12 gezeigt. Listing 4.11: Sprites Include Datei 1 2 3 // Sprite - Daten e x t e r n const u n s i g n e d char S p r i t e s _ L a r g e _ S p r i t e [ 6 1 4 4 ] _ _ a t t r i b u t e _ _ (( a l i g n e d ( 4 ) ) ) ; 4 5 6 7 // F a r b p a l e t t e e x t e r n const u n s i g n e d short S p r i t e s _ L a r g e _ P a l [24] _ _ a t t r i b u t e _ _ (( a l i g n e d ( 4 ) ) ) ; Listing 4.12: Sprites Source Datei KAPITEL 4. DIE UMSETZUNG (a) Originalzustand (b) Veränderung (c) Endzustand Abbildung 4.10: Vorgänge beim Verschieben eines Sprites. Im Ausgangszustand (a) wird die alte Spriteposition überzeichnet und das Sprite neu gezeichnet (b) wodurch das gewünschte Endergebnis (c) erreicht wird. 55 KAPITEL 4. DIE UMSETZUNG 56 Abbildung 4.11: PaGfx Werkzeug mit geladenen Grafiken (a) (b) (c) (d) (e) Abbildung 4.12: Die für das Spiel Einfach Genial DS erstellten SpriteGrafiken. Die Grafiken für die Spielerauswahl (a), die kleine Spielsteine (b), die großen Spielsteine (c), der Anzeiger der Computerzüge (d) und die Medaillien für die Gewinner (e). 1 2 // C Datei mit S p r i t e d a t e n # include " Sprites_Large.c" 3 4 5 // C Datei mit P a l e t t e d a t e n # i n c l u d e " S p r i t e s _ L a r g e . pal . c " Beim Laden der Sprites muss zuerst die jeweilige Palette geladen werden, (PA_LoadSpritePal), danach kann mit der Funktion PA_CreateSprite das Sprite erzeugt werden. Die Sprite-Nummer und die Paletten-Nummer müssen selbst verwaltet werden. Auch die Positionen der Sprites müssen in KAPITEL 4. DIE UMSETZUNG 57 eigenen Variablen gespeichert werden, falls man auf diese zugreifen möchte, da der Speicher der Sprites nur geschrieben werden kann. Die Positionen der Sprites können in X von 0 bis 511 und in Y von 0 bis 255 reichen und werden mit der Funktion PA_SetSpriteXY gesetzt. Größere Positionen werden mit Modulo in diesen Bereich gebracht. Neue Sprites können zwar zu jeder Zeit erzeugt werden, jedoch hat sich beim Spiel Einfach Genial DS herausgestellt, dass es dabei nach einer größeren Anzahl erzeugter Sprites zu Darstellungsfehlern kommen kann. Deswegen wurden alle möglicherweise benötigten Sprites bereits im Vorfeld erzeugt. Während des Spiels selbst wurde dann auf das Erzeugen neuer Sprites verzichtet. Für den oberen Bildschirm wurden von jeder Spielstein-Farbe und -Größe zwei Sprites erzeugt, da hier immer nur ein Spielstein sichtbar ist. Für den unteren Bildschirm wurden für jede Farbe zwölf Sprites erzeugt, wobei alle zwölf nur benötigt würden, wenn alle Spielsteine des Spielers einfärbig sind und die gleiche Farbe haben, was sehr unwahrscheinlich ist. Die nicht benötigten Sprites werden außerhalb des Bildschirmbereiches bewegt. Abbildung 4.13 zeigt die auf dem unteren Bildschirm während des Spiels sichtbaren Sprites. 4.3 Sound Der Nintendo DS kann Stereo-Sound auf 16 Kanälen ausgeben, wobei man zwischen der Ausgabe von Musik und der Ausgabe von Soundeffekten unterscheiden muss. Beim Spiel Einfach Genial DS wurde für die Soundausgabe eine eigene statische Klasse erstellt, damit sie von überall aus erreichbar ist. Die für den Spieler notwendigen Steuerungsoptionen sind das Ändern der Lautstärke und das Ausschalten der Audio-Ausgabe. Diese Funktionen sind in Listing 4.13 zu sehen. Listing 4.13: Utility Funktionen der Audio-Klasse 1 2 3 4 5 6 7 8 9 10 11 void Audio :: c h a n g e V o l u m e( int c h a n g e) { v o l u m e += c h a n g e; // 0 = 0% - 127 = 100% if ( v o l u m e < 0) v o l u m e = 0; if ( v o l u m e > 127) v o l u m e = 127; // M u s i k l a u t s t ä r k e P A _ S e t M o d V o l u m e ( v o l u m e ); // S o u n d e f f e k t l a u t s t ä r k e P A _ S e t S o u n d V o l( v o l u m e ); } 12 13 14 15 void Audio :: t o g g l e M u t e () { mute = ! mute ; KAPITEL 4. DIE UMSETZUNG (a) Sprites im Spiel (b) Sprites in der Debug-Ansicht Abbildung 4.13: Die im Spiel Einfach Genial DS verwendeten Sprites in der Debug-Ansicht (b) und im Spiel (a). 58 KAPITEL 4. DIE UMSETZUNG if ( mute ) { // Musik s t o p p e n P A _ S t o p M o d (); } else { // Z u l e t z t g e w ä h l t e Musik a b s p i e l e n P A _ P l a y F S M o d( audio [ c A u d i o ]); } 16 17 18 19 20 21 22 23 24 25 26 59 } 4.3.1 Soundeffekte Um Soundeffekte ausgeben zu können, müssen diese in ein spezielles Format umgewandelt werden, und zwar müssen sie als 8 Bit Signed PCM als Datendatei (.raw) abgespeichert werden. Die Samplerate kann beliebig gewählt werden, jedoch ist 11025 üblich. Die Dateien müssen in den data-Ordner kopiert werden, wo dann beim Kompilieren automatisch eine Include-Datei für die Soundeffekt-Dateien erzeugt wird. Abgespielt werden können die Soundeffekte – wenn die Standard-Samplerate verwendet wurde – mit der Funktion PA_PlaySimpleSound. Bei anderen Sampleraten muss zuerst mit der Funktion PA_SetDefaultSound die Samplerate gesetzt werden. Der Channel der zum Abspielen des Soundeffekts benötigt wird, kann mit der Funktion PA_GetFreeSoundChannel ermittelt werden. Soundeffekte können über die Funktion playSound der Audio-Klasse abgespielt werden, die in Listing 4.14 dargestellt ist, wobei für die Identifizierung des gewünschten Soundeffekts Defines verwendet werden. 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 Listing 4.14: Abspielen von Soundeffekten void Audio :: p l a y S o u n d( int sound ) { int c h a n n e l = P A _ G e t F r e e S o u n d C h a n n e l (); s w i t c h( sound ) { // S p i e l s t e i n a u f n e h m e n case P I C K _ S o u n d: P A _ P l a y S i m p l e S o u n d ( channel , a u f n e h m e n ); break ; // S p i e l s t e i n a n l e g e n nicht e r f o l g r e i c h case M I S S _ S o u n d: P A _ P l a y S i m p l e S o u n d ( channel , d a n e b e n ); break ; // S p i e l s t e i n a n l e g e n e r f o l g r e i c h case P L A C E _ S o u n d: P A _ P l a y S i m p l e S o u n d ( channel , p l a t z i e r e n ); break ; KAPITEL 4. DIE UMSETZUNG } 18 19 60 } 4.3.2 Musik Als Musik können mit der PaLib Module-Dateien (.mod) abgespielt werden. Dieses Format ist dem Midi-Format ähnlich, beinhaltet jedoch eigene Sound-Samples und benötigt deswegen im Gegensatz zum Midi-Format zum Abspielen keine Sound-Bank. Die Erstellung von Module-Dateien ist nicht einfach, jedoch gibt es sehr viele frei verfügbare im Internet. Die für das Spiel Einfach Genial DS verwendete Musik wurde von der Website The ” Mod Archive“ 9 heruntergeladen. Geladen werden kann die Musik sehr einfach über das PaLib Dateisystem, wie in Listing 4.15 gezeigt wird. Es werden einfach alle Module Dateien aus dem Dateisystem in ein Array gespeichert, wodurch man neue Musikstücke hinzufügen kann, ohne den Programmcode ändern zu müssen. 1 2 3 4 5 6 Listing 4.15: Abspielen von Musik void Audio :: init () { // Alle M o d u l e D a t e i e n in das // audio Array e i n f ü g e n n A u d i o = P A _ F S S e a r c h E x t ( audio , " mod" ); } 7 8 9 10 11 12 13 14 15 16 17 18 void Audio :: n e x t M u s i c () { // A b b r e c h e n wenn keine D a t e i e n v o r h a n d e n if (! n A u d i o) r e t u r n; c A u d i o ++; if ( c A u d i o >= n A u d i o) c A u d i o = 0; // A k t u e l l e Datei g e g e b e n e n f a l l s s t o p p e n P A _ S t o p M o d (); // Neue Datei a b s p i e l e n P A _ P l a y F S M o d ( audio [ c A u d i o ]); } 19 20 21 22 23 24 25 26 27 void Audio :: p r e v M u s i c () { if (! n A u d i o) r e t u r n; cAudio - -; if ( c A u d i o < 0) c A u d i o = n A u d i o - 1; P A _ S t o p M o d (); P A _ P l a y F S M o d ( audio [ c A u d i o ]); } 9 Homepage: http://www.modarchive.org KAPITEL 4. DIE UMSETZUNG 61 Abbildung 4.14: Vorgang der Zeichenerkennung bei der PaLib Bei der Entwicklung des Spieles Einfach Genial DS hat sich ein Problem mit der Musik herausgestellt, und zwar folgendes: Wenn die Module Datei alle 16 Kanäle benötigt, können keine Soundeffekte mehr abgespielt werden, da kein Kanal dafür frei ist. Falls bei der Module Datei doch kurz ein Kanal frei wäre und ein Soundeffekt in dieser Zeit abgespielt würde, kommt es oft zu lauten Knack-Geräuschen, wenn der Kanal wieder von der Module Datei verwendet wird. Aus diesem Grund gibt es beim Spiel Einfach Genial DS die Möglichkeit, zwischen der Musik und den Soundeffekten zu wählen. 4.4 Zeichenerkennung Die Zeichenerkennung für den Nintendo DS ist keine hardwareunterstützte Funktion, sondern eine Funktion der PaLib. Gezeichnet werden die zu erkennenden Zeichen auf einem 8-bit Hintergrund, der leer sein muss. Die Erkennung der Zeichen erfolgt, sobald der Stylus abgehoben wird, also, wenn Stylus.Released auf true wechselt. Die Zeichen müssen daher in einem Zug ohne Unterbrechung gezeichnet werden. Würde man ein A in der klassischen Blockschrift schreiben, würde es zwar als A, der Querstrich jedoch als eigener Buchstabe (als Leerstelle) erkannt. Bei der Erkennung wird aus der kontinuierlichen Linie eine Linie, die aus 15 Teilen besteht, gebildet (siehe Abbildung 4.14). Der Winkel jedes der 15 Abschnitte bildet dann die Vergleichsgrundlage für die Zeichenerkennung. Standardmäßig sind bei der PaLib nur die Buchstaben A – Z, der Punkt, der Zeilenumbruch, die Leerstelle und der Backslash mit einer Schreibweise wie in Abbildung 4.15 definiert. Weitere Zeichen können über die Funktion PA_RecoAddShape der PaLib definiert werden und werden dann ebenfalls er- KAPITEL 4. DIE UMSETZUNG 62 Abbildung 4.15: In der PaLib vordefinierte Zeichen kannt. Die Zeichen werden über einen String von 15 ASCII Zeichen definiert. Leider werden in der Dokumentation der PaLib nur 4 der möglichen ASCII Zeichen erklärt. • "AAAAAAAAAAAAAAA"- Gerade Linie nach rechts • "111111111111111"- Gerade Linie nach links • "IIIIIIIIIIIIIII"- Gerade Linie nach oben • "999999999999999"- Gerade Linie nach unten Wenn man sich in den Quellcode der PaLib einliest, kann man jedoch über die vordefinierten Zeichen recht schnell alle möglichen ASCII Zeichen, die verwendet werden können, herausfinden. Welchen Winkeln diese Zeichen zuzuordnen sind, wurde durch ein einfaches Testprogramm ermittelt. Das Ergebnis zeigt Abbildung 4.16. Nachdem alle Winkel zugeordnet werden konnten, konnten nun alle noch benötigten Zeichen definiert werden. Es wurden alle Ziffern außer der Null definiert, um sowohl eine Eingabe einer IP Adresse als auch einer Url zu ermöglichen. Bei der Null gibt es das Problem, dass ihre Definition mit der des O fast gleich wäre. Darauf wird jedoch etwas später genauer eingegangen. • "EEEEEE999999999"- 1 • "EDA?=<97548?@AA"- 2 • "@?>;631@?>;6310"- 3 KAPITEL 4. DIE UMSETZUNG 63 Abbildung 4.16: Zuordnung Winkel – ASCII Zeichen • "999AAAHHH999999"- 4 • "000999A?=;97530"- 5 • "23469;<>ACDEGJM"- 6 • "AAAAA6666666666"- 7 • "149<<9400MHDDHM"- 8 • "952NKHECA<;9964"- 9 Nachdem nun alle Zeichen definiert waren, wurde die Eingabe einem ausführlichen Test unterzogen, bei dem sich die Annahme aus dem ersten Entwurf des Spiels bestätigte. Viele Zeichen werden bei unsauberer Schreibweise falsch erkannt. Manche Zeichen sind einander so ähnlich, dass sie fast nicht zu unterscheiden sind. Es wurde deshalb ein Button eingeführt, mit dem man das zuletzt erkannte Zeichen auf das nächste ähnliche Zeichen wechseln kann. Mit diesem Button kann man zum Beispiel zuerst aus einem U ein V machen, dann aus dem V ein W und aus dem W wieder ein U. So kann in circa 90% der Fälle das gewünschte Zeichen ohne eine neue Eingabe erreicht werden. KAPITEL 4. DIE UMSETZUNG 64 Verwendung im Spiel Im Spiel Einfach Genial DS wurde die Speicherung und Veränderung der Eingabe in eine eigene Input-Klasse ausgelagert. Da in dieser Klasse nur Standard-String-Funktionen verwendet werden, wird hier auf diese Klasse nicht näher eingegangen. Da auf dem Hintergrund, auf dem die Zeichen gezeichnet werden, sonst nichts dargestellt werden darf, wurde die graphische GUI Oberfläche auf einen eigenen Layer ausgelagert. Der Zeichenhintergrund muss als 8 Bit Hintergrund in Layer 3 initialisiert werden (siehe Listing 4.16 Zeile 2). Danach muss die Zeichenfarbe, in diesem Fall Weiß, gesetzt werden. Würde nur der Zeichenhintergrund dargestellt werden, würde das Ergebnis Abbildung 4.17(c) entsprechen. Der Layer mit der GUI Grafik hat einen transparenten Bereich in der Mitte, erkennbar in Abbildung 4.17(b), in der der Zeichenhintergrund sichtbar bleibt. Da die Ressourcen begrenzt sind – es kann immer nur ein 8 oder 16 Bit Hintergrund dargestellt werden – kann die GUI-Grafik nicht aus einer Datei geladen werden. Aus diesem Grund wird die GUI-Grafik als Tile-Hintergrund in Layer 2 geladen (Listing 4.16 Zeile 6). Auf Layer 0 wird der eingegebene Text schwarz ausgegeben. Layer 1 wird nicht verwendet. Der Benutzer sieht dann alle Layer zusammengesetzt wie in Abbildung 4.17(d). Nachdem alle Hintergründe initialisiert und geladen wurden, werden die selbst definierten Zeichen initialisiert (Listing 4.16 Zeile 20 – 28). Wird der Stylus neu auf dem Touchscreen aufgesetzt, wird zuerst überprüft, ob der Aufsatzpunkt in einem der aktiven Bereiche der Buttons (wie in Abbildung 4.17(a)) liegt (Listing 4.16 Zeile 38 – 69). Wenn dies nicht der Fall ist, wird abgefragt, ob die Zeichenfläche ausgewählt wurde (Listing 4.16 Zeile 71). Die eigentliche Zeichenerkennung folgt dann ab Zeile 76. Zuerst wird der neu eingegebene Bereich gezeichnet und dann wird abgefragt, ob das Zeichen abgeschlossen ist (Stylus wieder abgehoben wird). Wird hier ein gültiges Zeichen erkannt, wird es an die Input-Klasse zur Verarbeitung übergeben. 1 2 3 4 Listing 4.16: Codebeispiel: Zeichenerkennung // 8 Bit H i n t e r g r u n d zum Z e i c h n e n P A _ I n i t 8 b i t B g (0 , 3); // Z e i c h e n f a r b e s e t z t e n ( weiß ) P A _ S e t B g P a l C o l (0 , 1 , P A _ R G B (31 , 31 , 31)); 5 6 7 // GUI H i n t e r g r u n d laden P A _ E a s y B g L o a d (0 , 2 , T e x t _ I n p u t ); 8 9 10 11 // T e x t a u s g a b e i n i t i a l i s i e r e n P A _ I n i t T e x t (0 , 0); P A _ S e t T e x t C o l (0 , 0 , 0 , 0); KAPITEL 4. DIE UMSETZUNG 65 (a) Aktive Bereiche der Buttons (b) GUI Hintergrund (c) Zeichenhintergrund 8 Bit (d) Zusammengesetzte Layer Abbildung 4.17: Aufbau der Layer bei der Zeichenerkennung. Der teilweise transparente Hintergrund (b) verdeckt die nicht benötigten Bereiche der Zeichenfläche (c), wodurch das gewünschte Ergebnis (d) erzielt wird. Die Benutzereingaben werden auf den aktiven Bereichen (a) erkannt. 12 13 bool h i t B u t t o n = true ; 14 15 16 17 18 // Input K l a s s e i n i t i a l i s i e r e n Input input (2); input . s e t D i s p l a y P o s i t i o n (0 , 1 , 4 , 14); input . s e t D i s p l a y P o s i t i o n (1 , 17 , 4 , 15); 19 20 21 22 23 24 25 26 27 // D e f i n i t i o n e i g e n e r P A _ R e c o A d d S h a p e ( ’1 ’ , P A _ R e c o A d d S h a p e ( ’2 ’ , P A _ R e c o A d d S h a p e ( ’3 ’ , P A _ R e c o A d d S h a p e ( ’4 ’ , P A _ R e c o A d d S h a p e ( ’5 ’ , P A _ R e c o A d d S h a p e ( ’6 ’ , P A _ R e c o A d d S h a p e ( ’7 ’ , Zeichen " E E E E E E 9 9 9 9 9 9 9 9 9 " ); " EDA ?= <97548? @AA" ); " @ ? >;631@ ? >;6310" ); " 999 A A A H H H 9 9 9 9 9 9" ); " 0 0 0 9 9 9A ? = ; 9 7 5 3 0" ); " 23469; < > A C D E G J M" ); " A A A A A 6 6 6 6 6 6 6 6 6 6 " ); KAPITEL 4. DIE UMSETZUNG 28 29 P A _ R e c o A d d S h a p e ( ’8 ’ , " 149 < <9400 M H D D H M" ); P A _ R e c o A d d S h a p e ( ’9 ’ , " 952 NKHECA < ; 9 9 6 4" ); 30 31 32 33 34 35 36 while (1) { if ( S t y l u s. N e w p r e s s) { // Z e i c h e n H i n t e r g r u n d l ö s c h e n P A _ C l e a r 8 b i t B g (0); 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 // B u t t o n s a b f r a g e n if ( P A _ D i s t a n c e( S t y l u s.X , S t y l u s.Y , 233 , 170) < 300) { break ; } else if ( P A _ D i s t a n c e( S t y l u s.X , S t y l u s.Y , 220 , 80) < 900) { h i t B u t t o n = true ; // L e t z t e s Z e i c h e n gegen ä h n l i c h e s w e c h s e l n input . s w i t c h L a s t C h a r (); } else if ( P A _ D i s t a n c e( S t y l u s.X , S t y l u s.Y , 25 , 170) < 300) { h i t B u t t o n = true ; // Groß - und K l e i n s c h r e i b u n g w e c h s e l n input . shift (); } else if ( P A _ D i s t a n c e( S t y l u s.X , S t y l u s.Y , 28 , 36) < 400 || P A _ D i s t a n c e( S t y l u s.X , S t y l u s.Y , 64 , 36) < 400 || P A _ D i s t a n c e( S t y l u s.X , S t y l u s.Y , 100 , 36) < 400) { h i t B u t t o n = true ; // Zum l i n k e n Text w e c h s e l n input . s w i t c h I n p u t (0); } else if ( P A _ D i s t a n c e( S t y l u s.X , S t y l u s.Y , 156 , 36) < 400 || P A _ D i s t a n c e( S t y l u s.X , S t y l u s.Y , 192 , 36) < 400 || P A _ D i s t a n c e( S t y l u s.X , S t y l u s.Y , 228 , 36) < 400) { h i t B u t t o n = true ; // Zum r e c h t e n Text w e c h s e l n input . s w i t c h I n p u t (1); } else if ( P A _ D i s t a n c e( S t y l u s.X , S t y l u s.Y , 128 , 144) < 4900) 66 KAPITEL 4. DIE UMSETZUNG { 77 // Z e i c h e n f l ä c h e b e r ü h r t h i t B u t t o n = false ; 78 79 } } if (! h i t B u t t o n) { // Z e i c h n e n P A _ 8 b i t D r a w (0 , 1); // A b f r a g e n ob neues Z e i c h e n e r k a n n t wurde char l e t t e r = P A _ C h e c k L e t t e r (); // G ü l t i g e s Z e i c h e n g e f u n d e n if ( l e t t e r > 31) { // Z e i c h e n an den a k t u e l l e n Text a n f ü g e n input . i n s e r t C h a r( l e t t e r ); } else if ( l e t t e r == P A _ B A C K S P A C E) { // L e t z t e s Z e i c h e n l ö s c h e n input . d e l e t e C h a r (); } } P A _ W a i t F o r V B L (); 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 67 } 4.5 Spielfeld Das Spielfeld wird im Programm als 2D-Array verwaltet, in dem numerisch abgespeichert wird, ob ein Feld existiert, leer ist oder, welche Farbe dort platziert wurde. Abbildung 4.18 zeigt die Zuordnung der Position im hexagonalen Spielfeld mit der Position im Array. Beim Starten eines neuen Spiels wird dieses Array mit leeren Feldern gefüllt, abhängig von der Anzahl der Mitspieler. Hierfür wird ein Prototyp-Array verwendet, das in Listing 4.17 zu sehen ist, und es wird einfach dort, wo die Anzahl der Mitspieler größer gleich dem Wert im Prototyp-Array ist, ein leeres Feld gesetzt. Danach werden noch die sechs vorplatzierten Farbfelder gesetzt. Listing 4.18 zeigt die einfache Funktion, die zum Erzeugen eines neuen Spielfelds verwendet wird. Listing 4.17: Prototype des Spielfeldarrays. Das ungekürzte Listing befindet sich im Anhang in Listing B.1 1 2 3 4 5 6 u n s i g n e d short m a p P r o t o t y p e [ 3 5 ] [ 1 0 ] = { { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 { 0, 0, 0, 0, 4, 0, 0, 0, 0, { 0, 0, 0, 0, 4, 4, 0, 0, 0, 0 { 0, 0, 0, 4, 3, 4, 0, 0, 0, }, }, 0 }, }, 0 }, KAPITEL 4. DIE UMSETZUNG 68 Abbildung 4.18: Spielfeld mit den Zeilen und Spalten des Arrays, in dem es im Spiel verwaltet wird 7 8 9 10 11 12 1 2 3 4 5 6 7 8 { 0, 0, 0, 4, 3, 3, 4, 0, 0, 0 { 0, 0, 4, 3, 2, 3, 4, 0, 0, { 0 , ... ... 0 { 0, 0, 0, 0, 0, 0, 0, 0, 0, }; }, 0 }, }, 0 } Listing 4.18: Erzeugen eines neuen Spielfeldes void G a m e F i e l d :: c r e a t e F i e l d( int p l a y e r) { // G e s a m t e s Array d u r c h l a u f e n for ( int x = 0; x < 10; x ++) for ( int y = 0; y < 35; y ++) { // S p i e l e r a n z a h l mit P r o t o t y p e v e r g l e i c h e n if ( m a p P r o t o t y p e [ y ][ x ] > 0 && KAPITEL 4. DIE UMSETZUNG m a p P r o t o t y p e[ y ][ x ] <= p l a y e r) 9 { 10 // l e e r e s S p i e l f e l d map [ x ][ y ] = 1; 11 12 } else { // kein S p i e l f e l d map [ x ][ y ] = 0; } 13 14 15 16 17 18 } // Start - F a r b f e l d e r s e t z e n map [ 4 ] [ 6 ] = 5; map [ 2 ] [ 1 1 ] = 2; map [ 7 ] [ 1 1 ] = 7; map [ 2 ] [ 2 1 ] = 6; map [ 7 ] [ 2 1 ] = 3; map [ 4 ] [ 2 6 ] = 4; 19 20 21 22 23 24 25 26 27 69 } Um von den Spielfeldinformationen, die dem Programm zur Verfügung stehen, zum Beispiel, ob ein Feld leer oder mit welcher Farbe es belegt ist, zu den für den Zusammenbau des Spielfeldhintergrunds benötigten Kacheln zu kommen, musste ein System entwickelt werden, wie das Programm diese Informationen möglichst einfach erhält. Als Vorbereitung wurde die Nummerierung der Kacheln aus dem mit dem Werkzeug PaGfx umgewandelten Set an Kacheln durch Analyse der erzeugten Daten ermittelt. Abbildung 4.19 zeigt die Kacheln mit den jeweiligen Nummern. Danach wurde das in Listing 4.19 dargestellte Array erstellt, in dem das Programm mit wenig Aufwand die benötigten Kacheln abrufen kann. Es muss nur überprüft werden, ob das angrenzende Feld außerhalb des Spielfeldes ist, ob es leer ist, oder, ob dort ein Stein liegt. Wenn es leer ist, muss zum Farbwert des gewünschten Feldes 10 addiert werden. Wenn dort ein Stein liegt, muss 20 addiert werden, und man erhält die richtige Kachel. Dies ist jedoch nur bei Feldern notwendig, die auf vier Kacheln aufgeteilt sind. Bei Feldern, die auf sechs Kacheln aufgeteilt sind, reicht es, die mittleren zwei Kacheln zu setzen, da die anderen vier Kacheln von den angrenzenden Spielfeldern gesetzt werden, wie Abbildung 4.20 zeigt. Für die große Darstellung des Spielfelds gibt es ein weiteres Array, das in Listing 4.20 gezeigt wird, wo für jede Kachel in der verkleinerten Darstellung die vier Kacheln gespeichert sind, die für die vergrößerte Darstellung verwendet werden. Diese Berechnung wird zu Beginn des Spieles einmal für das gesamte Spielfeld ausgeführt, wie in Listing 4.21 zu sehen ist. Danach wird nur der Bereich um die neu gesetzten Steine neu berechnet, wie Listing 4.22 zeigt. KAPITEL 4. DIE UMSETZUNG Abbildung 4.19: Kachel-Set mit Index für den Zugriff im Programmcode Listing 4.19: Array mit der Zuordnung von Kacheln zu Spielsteinen. Das ungekürzte Listing befindet sich im Anhang in Listing B.2 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 u n s i g n e d short t i l e s S m a l l [ 2 8 ] [ 6 ] = { // 4 Werte für S t e i n e mit 4 K a c h e l n dann // 2 Werte für S t e i n e mit 6 K a c h e l n da // nur die m i t t l e r e n 2 b e n ö t i g t w e r d e n // l . o . r . o . l . u . r . u . o . u. { 12 , 12 , 12 , 12 , 12 , 12 } , // 0 kein Feld -> kein Feld { 43 , 44 , 59 , 60 , 47 , 63 } , // 1 l e e r e s Feld -> kein Feld { 2, 3, 21 , 22 , 9, 28 } , // 2 g e l b e r Stein -> kein Feld { 4, 5, 23 , 24 , 10 , 29 } , // 3 g r ü n e r Stein -> kein Feld { 6, 7, 25 , 26 , 11 , 30 } , // 4 b l a u e r Stein -> kein Feld { 39 , 40 , 55 , 56 , 45 , 61 } , // 5 lila Stein -> kein Feld 70 KAPITEL 4. DIE UMSETZUNG 71 Abbildung 4.20: Überprüfung der benachbarten Spielsteine bei den Spielsteinen, die aus 4 Kacheln zusammengesetzt sind { 41 , 42 , 57 , 58 , 46 , 62 } , // 6 o r a n g e r Stein -> kein Feld { 0, 1, 19 , 20 , 8, 27 } , // 7 roter Stein -> kein Feld { 12 , 12 , 12 , 12 , 12 , 12 } , // nicht v e r w e n d e t { 12 , 12 , ... ... 46 , 62 } , // 26 o r a n g e r Stein -> b e l i e b i g e r Stein { 72 , 73 , 93 , 94 , 8, 27 } // 27 roter Stein -> b e l i e b i g e r Stein 18 19 20 21 22 23 24 25 26 27 28 29 }; Listing 4.20: Array mit der Zuordnung von 4 Kacheln der vergrößerten Darstellung zu einer Kachel der kleinen Darstellung (Im Kommentar die entsprechende kleinere Kachel). Das ungekürzte Listing befindet sich im Anhang in Listing B.3 1 2 3 4 5 6 7 8 9 10 u n s i g n e d short t i l e s L a r g e [ 1 5 0 ] [ 4 ] = { 153 , 154 , 167 , 168 } , { 155 , 156 , 169 , 170 } , { 153 , 157 , 171 , 172 } , { 158 , 156 , 173 , 174 } , { 153 , 14 , 175 , 32 } , { 15 , 156 , 33 , 176 } , { 153 , 17 , 177 , 36 } , { 18 , 156 , 37 , 178 } , { 159 , 160 , 168 , 169 } , { // 0 // 1 // 2 // 3 // 4 // 5 // 6 // 7 // 8 KAPITEL 4. DIE UMSETZUNG { 161 , { 163 , { 165 , // Nicht { 12 , { 12 , { 12 , { 12 , { 181 , { 183 , { 185 , 11 12 13 14 15 16 17 18 19 20 21 22 { { { { 23 24 25 26 27 12 , 131 , 132 , 112 , 162 , 172 , 173 } , 164 , 179 , 33 } , 166 , 180 , 37 } , verwendete Felder 12 , 12 , 12 } , { 12 , 12 , 12 , 12 } , { 12 , 12 , 12 , 12 } , { 12 , 12 , 12 , 12 } , 182 , 196 , 197 } , 184 , 198 , 199 } , ... ... , 278 } , 130 , 12 , 150 } , 12 , 151 , 12 } , 112 , 113 , 2139 } , 133 , 152 , 111 } 72 // 9 // 10 // 11 12 , 12 , 12 , 12 , 12 , 12 , 12 } , 12 } , 12 } , // 19 // 20 // 145 // 146 // 147 // 148 // 149 }; Listing 4.21: Erzeugen der Spielfelddaten aus den Spielfeldinformationen 1 2 3 4 5 void G a m e F i e l d :: i n s e r t T i l e( short x , short y , u n s i g n e d short tile , short t i l e P o s) { // K l e i n e K a c h e l aus dem Array a b r u f e n int n e w t i l e = t i l e s S m a l l[ tile ][ t i l e P o s ]; 6 // In das k l e i n e S p i e l f e l d e i n t r a g e n small [ x + y * 32] = n e w t i l e; // Die 4 g r o ß e n K a c h e l n a b r u f e n und in das // große S p i e l f e l d e i n t r a g e n large [ x * 2 + y * 2 * 64] = t i l e s L a r g e[ n e w t i l e ][0]; large [ x * 2 + 1 + y * 2 * 64] = t i l e s L a r g e[ n e w t i l e ][1]; large [ x * 2 + ( y * 2 + 1) * 64] = t i l e s L a r g e[ n e w t i l e ][2]; large [ x * 2 + 1 + ( y * 2 + 1) * 64] = t i l e s L a r g e[ n e w t i l e ][3]; 7 8 9 10 11 12 13 14 15 16 17 18 19 if (! i s Z o o m) { // Wenn das k l e i n e S p i e l f e l d a n g e z e i g t // wird , kann es g l e i c h g e ä n d e r t w e r d e n // und muss nicht neu g e l a d e n w e r d e n P A _ S e t M a p T i l e (1 , 2 , x , y , n e w t i l e ); } 20 21 22 23 24 25 26 27 } 28 29 30 void G a m e F i e l d :: c a l c T i l e s( int x , int y ) { KAPITEL 4. DIE UMSETZUNG 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 // U n g e r a d e Z e i l e n b e s t e h e n aus S p i e l f eldern , // die aus 4 K a c h e l n z u s a m m e n g e s e t z t sind if ( y % 2) { // Links oben ü b e r p r ü f e n if ( map [ x - 1][ y - 1] > 1) { // S p i e l s t e i n v o r h a n d e n i n s e r t T i l e( x * 3 , y , map [ x ][ y ] + 20 , 0); } else { // Kein Feld (0 * 10) oder l e e r e s Feld ( 1 *10) i n s e r t T i l e( x * 3 , y , map [ x ][ y ] + map [ x - 1][ y - 1] * 10 , 0); } // Links unten ü b e r p r ü f e n if ( map [ x - 1][ y + 1] > 1) { i n s e r t T i l e( x * 3 , y + 1 , map [ x ][ y ] + 20 , 2); } else { i n s e r t T i l e( x * 3 , y + 1 , map [ x ][ y ] + map [ x - 1][ y + 1] * 10 , 2); } // R e c h t s oben ü b e r p r ü f e n if ( map [ x ][ y - 1] > 1) { i n s e r t T i l e( x * 3 + 1 , y , map [ x ][ y ] + 20 , 1); } else { i n s e r t T i l e( x * 3 + 1 , y , map [ x ][ y ] + map [ x ][ y - 1] * 10 , 1); } // R e c h t s unten ü b e r p r ü f e n if ( map [ x ][ y + 1] > 1) { i n s e r t T i l e( x * 3 + 1 , y + 1 , map [ x ][ y ] + 20 , 3); } else { i n s e r t T i l e( x * 3 + 1 , y + 1 , map [ x ][ y ] + map [ x ][ y + 1] * 10 , 3); } 73 KAPITEL 4. DIE UMSETZUNG } // G e r a d e Z e i l e n b e s t e h e n aus S p i e l feldern , // die aus 2 K a c h e l n z u s a m m e n g e s e t z t sind else { i n s e r t T i l e( x * 3 + 2 , y , map [ x ][ y ] , 4); i n s e r t T i l e( x * 3 + 2 , y + 1 , map [ x ][ y ] , 5); } 80 81 82 83 84 85 86 87 88 74 } 89 90 91 92 93 94 95 96 97 98 99 void G a m e F i e l d :: b u i l d T i l e M a p s () { // Das g e s a m t e Array außer dem Rand durchlaufen , // damit A b f r a g e n nicht a u ß e r h a l b des A r r a y s l i e g e n for ( int x = 1; x < 9; x ++) for ( int y = 1; y < 33; y ++) { c a l c T i l e s(x , y ) } } Listing 4.22: Aktualisieren der Spielfelddaten, wenn ein neuer Stein gesetzt wird 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 void G a m e F i e l d :: c h a n g e M a p T i l e ( int x , int y , int color ) { // S p i e l f e l d i n f o r m a t i o n ä n d e r n map [ x ][ y ] = color ; int s t a r t X = x - 2; if ( s t a r t X < 1) s t a r t X = 1; int s t a r t Y = y - 2; if ( s t a r t Y < 1) s t a r t Y = 1; int endX = x + 2; if ( endX > 9) endX = 9; int endY = y + 2; if ( endY > 33) endY = 33; // B e r e i c h um die g e ä n d e r t e S t e l l e neu b e r e c h n e n for ( int i = s t a r t X; i < endX ; i ++) for ( int j = s t a r t Y; j < endY ; j ++) { c a l c T i l e s(i , j ) } } Da beim Laden eines Hintergrunds eine Kopie der Arraydaten in der Hardware erstellt wird, müssen, wenn Steine ausgetauscht werden, beide Datensätze aktualisiert werden. Die Daten des Programms können durch einen einfachen Arrayzugriff geändert werden, für die Daten in der Hardware muss die Funktion PA_SetMapTile verwendet werden. Da es bei der PaLib jedoch zurzeit ein Problem beim Austauschen von Kacheln bei großen Hin- KAPITEL 4. DIE UMSETZUNG 75 tergründen gibt, muss bei der großen Spielfelddarstellung immer der gesamte Hintergrund neu geladen werden, was dann als kurzes Flackern zu sehen ist. Es muss natürlich immer auch der Datensatz für die zweite Zoom-Stufe der Map aktuell gehalten werden. Da das Spielfeld immer größer als der Bildschirm des Nintendo DS ist, muss es bewegbar sein. Das Bewegen erfolgt mit dem Stift auf einer verkleinerten Version des Spielfeldes, welches dann immer auf die Stiftposition zentriert dargestellt wird. Listing 4.23 zeigt die Ausrichtung des Hintergrunds, wobei die Grenzen des verkleinerten Spielfeldes bekannt sein müssen, um eine genau Berechnung zu ermöglichen. Diese hätten fix eingesetzt werden können, da jedoch Designänderungen möglich waren, wurden sie als Variablen gespeichert. Listing 4.23: Ausrichten des Spielfeldes am Stylus 1 2 3 4 5 6 7 8 void G a m e F i e l d :: s c r o l l T o( int x , int y ) { // Abbrechen , wenn a u ß e r h a l b des B e r e i c h e s if ( x < left || y < top || x > left + width || y > top + h e i g h t) { r e t u r n; } 9 10 11 12 13 14 // P o s i t i o n der k l e i n e n D a r s t e l l u n g b e r e c h n e n s c r o l l X _ s m a l l = ( int) ((( float )( x - left )) / width * 256 - 128); if ( s c r o l l X _ s m a l l < 0) s c r o l l X _ s m a l l = 0; if ( s c r o l l X _ s m a l l > 0) s c r o l l X _ s m a l l = 0; 15 16 17 18 19 s c r o l l Y _ s m a l l = ( int) ((( float )( y - top )) / h e i g h t * 280 - 96); if ( s c r o l l Y _ s m a l l < 0) s c r o l l Y _ s m a l l = 0; if ( s c r o l l Y _ s m a l l > 280 - 192) s c r o l l Y _ s m a l l = 88; 20 21 22 23 24 25 // P o s i t i o n der g r o ß e n D a r s t e l l u n g b e r e c h n e n s c r o l l X _ l a r g e = ( int) ((( float )( x - left )) / width * 512 - 128); if ( s c r o l l X _ l a r g e < 0) s c r o l l X _ l a r g e = 0; if ( s c r o l l X _ l a r g e > 512 - 256) s c r o l l X _ l a r g e = 256; 26 27 28 29 30 s c r o l l Y _ l a r g e = ( int) ((( float )( y - top )) / h e i g h t * 560 - 96); if ( s c r o l l Y _ l a r g e < 0) s c r o l l Y _ l a r g e = 0; if ( s c r o l l Y _ l a r g e > 560 - 192) s c r o l l Y _ l a r g e = 368; 31 32 33 34 // S p i e l f e l d b e w e g e n if ( i s Z o o m) { KAPITEL 4. DIE UMSETZUNG (a) Räumliche Darstellung der Ebenen 76 (b) Darstellung auf dem Bildschirm Abbildung 4.21: Parallax Scrolling in der 3D Darstellung (a), die simuliert wird, und das Ergebnis (b), dass dann am Bildschirm zu sehen ist. P A _ P a r a l l a x S c r o l l X (1 , s c r o l l X _ l a r g e ); P A _ P a r a l l a x S c r o l l Y (1 , s c r o l l Y _ l a r g e ); 35 36 } else { P A _ P a r a l l a x S c r o l l X (1 , s c r o l l X _ s m a l l ); P A _ P a r a l l a x S c r o l l Y (1 , s c r o l l Y _ s m a l l ); } 37 38 39 40 41 42 43 } Die verwendete Funktion, um das Spielfeld zu bewegen, ist eine Parallaxfunktion, bei der weitere Hintergründe mit unterschiedlichen Geschwindigkeiten mit bewegt werden, um räumliche Tiefe vorzutäuschen. Das Prinzip der Parallax-Bewegung [18] wird in Abbildung 4.21 sehr gut dargestellt. Um einen Hintergrund zu bewegen, ohne die anderen mit zu bewegen, müsste man nur die Funktion PA_ParallaxScrollX gegen die Funktion PA_BGScrollX austauschen. Um festzulegen, wie schnell welcher Hintergrund bewegt werden soll, muss der Bildschirm für das Parallax-Bewegen mit der Funktion void PA_InitParallaxY( u8 screen, s32 bg0, s32 bg1, s32 bg2, s32 bg3) initialisiert werden. Die hierbei verwendeten Werte für die vier HintergrundSchichten sind 0 bis 256, wobei 0 bedeutet, dass der Hintergrund auf dieser Schicht nicht mit bewegt wird, und 256, dass der Hintergrund mit voller Geschwindigkeit mit bewegt wird. 4.6 Computergegner Damit man das Spiel Einfach Genial DS auch alleine spielen kann, sind Computergegner notwendig. Da bis zu drei Computergegner möglich sind und sich diese nicht komplett gleich verhalten sollten, wurden drei verschiedene Strategien, basierend auf der Beobachtung realer Spieler, ausgearbeitet. KAPITEL 4. DIE UMSETZUNG 77 • Absichernde Strategie – Die schlechteste Farbe wird, wenn möglich, maximal verbessert, ansonsten die nächst-schlechtere Farbe usw. • Gierige Strategie – Die Farbe, bei der die meisten Punkte erzielt werden können, wird gesetzt • Maximierende Strategie – Der Spielstein, bei dem mit beiden Farben zusammen die meisten Punkte erzielt werden können, wird gesetzt Da für die Berechnung der Spielzüge der Computergegner ein Zugriff auf alle Daten des Spielfeldes notwendig ist, wurden die Funktionen in die Spielfeld-Klasse integriert. Zuerst wurde ein Array, ähnlich zu dem des Spielfelds, angelegt, wo für jede Farbe gespeichert wurde, wie viele Punkte ein Stein dieser Farbe erzielen würde. Nicht mehr leere Felder wurden natürlich ausgelassen. Außerdem wurde bei den ersten Spielzügen noch die Sonderregel berücksichtigt, dass keine zwei Spieler an der gleichen Startfarbe anlegen dürfen. Für die Berechnung der Punkte konnte dieselbe Methode verwendet werden, die auch beim wirklichen Setzen eines Spielsteins zum Auswerten verwendet wird. Listing 4.24 zeigt die Funktion, die zum Aktualisieren dieses Arrays verwendet wird. 1 2 3 4 5 6 7 8 9 10 11 Listing 4.24: Update der Gewichtungsdaten // G e s a m t e s S p i e l f e l d w e r t e n void G a m e F i e l d :: u p d a t e K i M a p () { for ( int i = 0; i < 10; i ++) for ( int j = 0; j < 35; j ++) { // Wenn l e e r e s Feld if ( map [ i ][ j ] == 1) { // für alle 6 F a r b e n for ( int k = 0; k < 6; k ++) 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 { // die m ö g l i c h e n P u n k t e b e r e c h n e n kiMap [ i ][ j ][ k ] = this - > c o u n t P o i n t s(i , j , cconv [ k ]); } } else { // Keine P u n k t e m ö g l i c h for ( int k = 0; k < 6; k ++) { kiMap [ i ][ j ][ k ] = -1; } } KAPITEL 4. DIE UMSETZUNG } // Wenn es die erste Runde des S p i e l e r s ist , // für die 6 S t a r t p o s i t i o n e n überprüfen , ob diese // b e r e i t s b e l e g t sind if ( p l a y e r s[ c u r P l a y e r] - > f i r s t R o u n d) { p l a y e r s[ c u r P l a y e r] - > f i r s t R o u n d = false ; // S t a r t p o s i t i o n b e l e g t if ( this - > c o u n t P o i n t s (4 , 6 , 5)) { // M ö g l i c h e P u n k t e hier l ö s c h e n for ( int i = 2; i < 7; i ++) for ( int j = 0; j < 14; j ++) { for ( int k = 0; k < 6; k ++) { kiMap [ i ][ j ][ k ] = -1; } } } if ( this - > c o u n t P o i n t s (2 , 11 , 2)) { // P u n k t e l ö s c h e n } if ( this - > c o u n t P o i n t s (7 , 11 , 7)) { // P u n k t e l ö s c h e n } if ( this - > c o u n t P o i n t s (2 , 21 , 6)) { // P u n k t e l ö s c h e n } if ( this - > c o u n t P o i n t s (7 , 21 , 3)) { // P u n k t e l ö s c h e n } if ( this - > c o u n t P o i n t s (4 , 26 , 4)) { // P u n k t e l ö s c h e n } } 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 78 } Für die Berechnung des nächsten Zugs werden nun alle möglichen Positionen für einen neuen Spielstein mit den drei verschiedenen Heuristiken gewichtet. Die Position mit der höchsten Gewichtung ist dann der nächste Zug. Listing 4.25 zeigt, wie alle möglichen Positionen überprüft werden, und Listing 4.26 die drei Heuristiken für das Werten der Position. Listing 4.25: Überprüfen aller möglichen Spielstein-Positionen KAPITEL 4. DIE UMSETZUNG 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 // N ä c h s t e n C o m p u t e r z u g b e r e c h n e n void G a m e F i e l d :: g e t N e x t K I M o v e ( K i S t r a t e g y strategy , int player , int *x , int *y , int * direction , int * stone ) { c u r P l a y e r = p l a y e r; // S p i e l f e l d w e r t e n u p d a t e K i M a p (); curX = 0; curY = 0; c u r D i r = 0; c u r P o i n t s = 0; c u r C o l o r = -1; c u r S t o n e = 0; // G e s a m t e s S p i e l f e l d ü b e r p r ü f e n for ( int i = 1; i < 9; i ++) for ( int j = 1; j < 33; j ++) { // Wenn das Feld leer ist if ( map [ i ][ j ] == 1) { int s e c o n d X = 0; int s e c o n d Y = 0; // für alle 6 R i c h t u n g e n s e c o n d Y = j + 1; if ( j % 2) s e c o n d X = i - 1; else secondX = i; // ü b e r p r ü f e n ob d i e s e s Feld e b e n f a l l s leer ist if ( map [ s e c o n d X ][ s e c o n d Y] == 1) { // Alle 6 S p i e l s t e i n e for ( int k = 0; k < 6; k ++) { // falls v o r h a n d e n if ( p l a y e r s[ p l a y e r] - > m y S t o n e s[ k ][0] != -1) { // b e w e r t e n this - > c a l c K I P o i n t s(i , j , secondX , secondY , p l a y e r s[ p l a y e r] - > m y S t o n e s[ k ][0] , p l a y e r s[ p l a y e r] - > m y S t o n e s[ k ][1] , strategy , 0 , k ); } } } s e c o n d Y = j + 2; secondX = i; if ( map [ s e c o n d X ][ s e c o n d Y] == 1) 79 KAPITEL 4. DIE UMSETZUNG 50 { 51 // KI b e r e c h n e n } s e c o n d Y = j + 1; if ( j % 2) secondX = i; else s e c o n d X = i + 1; if ( map [ s e c o n d X ][ s e c o n d Y] { // KI b e r e c h n e n } s e c o n d Y = j - 1; if ( j % 2) secondX = i; else s e c o n d X = i + 1; if ( map [ s e c o n d X ][ s e c o n d Y] { // KI b e r e c h n e n } s e c o n d Y = j - 2; secondX = i; if ( map [ s e c o n d X ][ s e c o n d Y] { // KI b e r e c h n e n } s e c o n d Y = j - 1; if ( j % 2) s e c o n d X = i - 1; else secondX = i; if ( map [ s e c o n d X ][ s e c o n d Y] { // KI b e r e c h n e n } 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 == 1) == 1) == 1) == 1) } } // R ü c k g a b e w e r t e s p e i c h e r n * x = curX ; * y = curY ; * d i r e c t i o n = c u r D i r; * stone = c u r S t o n e; 86 87 88 89 90 91 92 93 80 } Listing 4.26: Gewichtung der möglichen Spielstein-Positionen gemäß der Strategie des Computerspielers 1 2 // M ö g l i c h e n S p i e l z u g b e w e r t e n void G a m e F i e l d :: c a l c K I P o i n t s( int x , int y , KAPITEL 4. DIE UMSETZUNG int x1 , int y1 , int mColor , int sColor , K i S t r a t e g y strategy , int direction , int stone ) 3 4 5 6 7 8 9 { // Die b e i d e n F a r b e n des S t e i n s für die // V e r w e n d u n g im KI - Array k o n v e r t i e r e n int main = cconv [ m C o l o r ]; int s e c o n d = cconv [ s C o l o r ]; 10 11 12 13 int t e m p p o i n t s 1 ; int t e m p p o i n t s 2 ; int t e m p p o i n t s 3 ; 14 15 16 17 18 19 20 21 22 23 // Wenn noch kein Stein ausgewählt , // den e r s t e n w ä h l e n if ( curX == 0) { curX = x ; curY = y ; c u r D i r = d i r e c t i o n; c u r S t o n e = stone ; } 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 s w i t c h( s t r a t e g y) { // A b s i c h e r n d e S t r a t e g i e case G a m e F i e l d :: K I _ M a x L o w e s t C o l o r : // Noch keine Farbe g e w ä h l t if ( c u r C o l o r == -1 && // und P u n k t e e r z i e l b a r ( kiMap [ x ][ y ][ m C o l o r] > 0 || kiMap [ x1 ][ y1 ][ s C o l o r] > 0)) { // S p i e l z u g a u s w ä h l e n if ( kiMap [ x ][ y ][ m C o l o r] > kiMap [ x1 ][ y1 ][ s C o l o r ]) { c u r C o l o r = m C o l o r; c u r P o i n t s = kiMap [ x ][ y ][ m C o l o r]; } else { c u r C o l o r = s C o l o r; c u r P o i n t s = kiMap [ x1 ][ y1 ][ s C o l o r ]; } curX = x ; curY = y ; c u r D i r = d i r e c t i o n; c u r S t o n e = stone ; } 81 KAPITEL 4. DIE UMSETZUNG 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 // S p i e l s t e i n hat 2 F a r b e n if ( m C o l o r != s C o l o r) { // S t e i n f a r b e g e r i n g e r e P u n k t e als a k t u e l l e Farbe if (( p l a y e r s[ c u r P l a y e r] - > p o i n t s[ m C o l o r] < p l a y e r s[ c u r P l a y e r] - > p o i n t s[ c u r C o l o r] && // und P u n k t e e r z i e l b a r kiMap [ x ][ y ][ m C o l o r] > 0) || // oder S t e i n f a r b e g l e i c h viele P u n k t e // wie a k t u e l l e Farbe ( p l a y e r s[ c u r P l a y e r] - > p o i n t s[ m C o l o r] == p l a y e r s[ c u r P l a y e r] - > p o i n t s[ c u r C o l o r] && // und mehr P u n k t e e r z i e l b a r kiMap [ x ][ y ][ m C o l o r] > c u r P o i n t s )) { // S p i e l z u g a u s w ä h l e n c u r C o l o r = m C o l o r; c u r P o i n t s = kiMap [ x ][ y ][ m C o l o r]; curX = x ; curY = y ; c u r D i r = d i r e c t i o n; c u r S t o n e = stone ; } // 2. Farbe ü b e r p r ü f e n if (( p l a y e r s[ c u r P l a y e r] - > p o i n t s[ s C o l o r] < p l a y e r s[ c u r P l a y e r] - > p o i n t s[ c u r C o l o r] && kiMap [ x1 ][ y1 ][ s C o l o r] > 0) || ( p l a y e r s[ c u r P l a y e r] - > p o i n t s[ s C o l o r] == p l a y e r s[ c u r P l a y e r] - > p o i n t s[ c u r C o l o r] && kiMap [ x1 ][ y1 ][ s C o l o r] > c u r P o i n t s )) { c u r C o l o r = s C o l o r; c u r P o i n t s = kiMap [ x1 ][ y1 ][ s C o l o r ]; curX = x ; curY = y ; c u r D i r = d i r e c t i o n; c u r S t o n e = stone ; } } else { // E i n f ä r b i g e r S p i e l s t e i n if (( p l a y e r s[ c u r P l a y e r] - > p o i n t s[ m C o l o r] < p l a y e r s[ c u r P l a y e r] - > p o i n t s[ c u r C o l o r] && ( kiMap [ x ][ y ][ m C o l o r] > 0 || kiMap [ x1 ][ y1 ][ m C o l o r] > 0)) || ( p l a y e r s[ c u r P l a y e r] - > p o i n t s[ m C o l o r] == p l a y e r s[ c u r P l a y e r] - > p o i n t s[ c u r C o l o r] && kiMap [ x ][ y ][ m C o l o r] + 82 KAPITEL 4. DIE UMSETZUNG 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 kiMap [ x1 ][ y1 ][ m C o l o r] > c u r P o i n t s )) { c u r C o l o r = m C o l o r; c u r P o i n t s = kiMap [ x ][ y ][ m C o l o r]; curX = x ; curY = y ; c u r D i r = d i r e c t i o n; c u r S t o n e = stone ; } } break ; // M a x i m i e r e n d e S t r a t e g i e case G a m e F i e l d :: K I _ M a x T w o C o l o r: // 1. Farbe auf w i r k l i c h n ü t z l i c h e P u n k t e b e g r e n z e n t e m p p o i n t s 1 = kiMap [ x ][ y ][ m C o l o r ]; if ( t e m p p o i n t s 1 > 18 - p l a y e r s[ c u r P l a y e r] - > p o i n t s[ m C o l o r ]) t e m p p o i n t s 1 = 18 p l a y e r s[ c u r P l a y e r] - > p o i n t s[ m C o l o r ]; // 2. Farbe auf w i r k l i c h n ü t z l i c h e P u n k t e b e g r e n z e n t e m p p o i n t s 2 = kiMap [ x1 ][ y1 ][ s C o l o r]; if ( t e m p p o i n t s 2 > 18 - p l a y e r s[ c u r P l a y e r] - > p o i n t s[ s C o l o r ]) t e m p p o i n t s 2 = 18 p l a y e r s[ c u r P l a y e r] - > p o i n t s[ s C o l o r ]; // Beide F a r b e n a d d i e r e n t e m p p o i n t s 3 = t e m p p o i n t s 1 + t e m p p o i n t s 2; // E i n f ä r b i g e S p i e l s t e i n e n o c h m a l s b e g r e n z e n if ( m C o l o r == s C o l o r && t e m p p o i n t s 3 > 18 - p l a y e r s[ c u r P l a y e r] - > p o i n t s[ s C o l o r ]) t e m p p o i n t s 3 = 18 p l a y e r s[ c u r P l a y e r] - > p o i n t s[ s C o l o r ]; // Mehr P u n k t e e r z i e l b a r if ( t e m p p o i n t s 3 > c u r P o i n t s) { // S p i e l s t e i n a u s w ä h l e n c u r P o i n t s = t e m p p o i n t s 3; curX = x ; curY = y ; c u r D i r = d i r e c t i o n; c u r S t o n e = stone ; } break ; // G i e r i g e S t r a t e g i e // Ä h n l i c h zu m a x i m i e r e n d e r Strategie , j e d o c h nur // auf eine Farbe s p e z i a l i s i e r t case G a m e F i e l d :: K I _ M a x S i n g e l C o l o r : t e m p p o i n t s 1 = kiMap [ x ][ y ][ m C o l o r ]; if ( t e m p p o i n t s 1 > 83 KAPITEL 4. DIE UMSETZUNG 18 - p l a y e r s[ c u r P l a y e r] - > p o i n t s[ m C o l o r ]) t e m p p o i n t s 1 = 18 p l a y e r s[ c u r P l a y e r] - > p o i n t s[ m C o l o r ]; t e m p p o i n t s 2 = kiMap [ x1 ][ y1 ][ s C o l o r]; if ( t e m p p o i n t s 2 > 18 - p l a y e r s[ c u r P l a y e r] - > p o i n t s[ s C o l o r ]) t e m p p o i n t s 2 = 18 p l a y e r s[ c u r P l a y e r] - > p o i n t s[ s C o l o r ]; // G r ö ß t e Farbe a u s w ä h l e n if ( m C o l o r == s C o l o r) { t e m p p o i n t s 3 = t e m p p o i n t s 1 + t e m p p o i n t s 2; } else { if ( t e m p p o i n t s 1 > t e m p p o i n t s 2) t e m p p o i n t s 3 = t e m p p o i n t s 1; else t e m p p o i n t s 3 = t e m p p o i n t s 2; } // Mehr P u n k t e e r z i e l b a r if ( t e m p p o i n t s 3 > c u r P o i n t s || t e m p p o i n t s 3 == c u r P o i n t s && t e m p p o i n t s 1 && t e m p p o i n t s 2) { // S p i e l s t e i n a u s w ä h l e n c u r P o i n t s = t e m p p o i n t s 3; curX = x ; curY = y ; c u r D i r = d i r e c t i o n; c u r S t o n e = stone ; } break ; 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 } 182 183 84 } 4.7 Multiplayer Die Multiplayer-Funktion des Spieles Einfach Genial DS wurde mit Hilfe der Wireless-LAN-Schnittstelle des Nintendo DS realisiert. Der gesamte Netzwerk-spezifische Programmcode wurde in der Network-Klasse gekapselt. Allerdings muss, um das Netzwerk verwenden zu können, zuerst eine Verbindung zu einem Access-Point hergestellt und eine IP-Adresse bezogen werden. Der Access Point, mit dem verbunden werden soll, kann vom Spieler über die SSID angegeben werden. Zusätzlich kann noch ein WEP Key eingegeben werden, falls der Access Point damit gesichert ist. Unterstützt werden Längen bis zu 128 Bit. Die IP-Adresse muss von einem DHCP-Server zur Verfügung gestellt werden, eine manuelle Eingabe der IP-Adresse ist zur- KAPITEL 4. DIE UMSETZUNG 85 zeit nicht geplant. Die Herstellung der Verbindung zum Access-Point erfolgt durch die in der Auflistung dargestellten Schritte, welche als Programmcode in Listing 4.27 zu sehen sind. • Initialisieren der WLAN-Schnittstelle • Nach Access-Points suchen • Gewünschten Access-Point heraussuchen • Den WEP-Key ins Hexadezimal-System umwandeln • Verbinden und auf IP-Adresse warten Listing 4.27: Verbinden des Nintendo DS mit dem gewählten Access-Point (falls verfügbar) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 void N e t w o r k :: c o n n e c t T o A P ( char * ssid , char * key ) { // W i r e l e s s LAN i n i t i a l i s i e r e n P A _ I n i t W i f i (); W i f i _ A c c e s s P o i n t ap ; // Nach Access - P o i n t s s u c h e n W i f i _ S c a n M o d e (); ap . ssid [0] = 0; int numap = W i f i _ G e t N u m A P (); // Nach dem g e w ü n s c h t e n Access - Point s u c h e n for ( int i = 0; i < numap ; i ++) { if ( W i f i _ G e t A P D a t a (i ,& ap ) == W I F I _ R E T U R N _ O K ) { if ( s t r c m p( ap . ssid , ssid ) == 0) { break ; } } } // Access - Point wurde nicht g e f u n d e n if ( s t r c m p( ap . ssid , ssid ) != 0) { r e t u r n; } 26 27 28 // IP von DHCP b e z i e h e n W i f i _ S e t I P (0 ,0 ,0 ,0 ,0); 29 30 31 32 33 // WEP - Key von ASCII in HEX u m w a n d e l n u n s i g n e d char w e p k e y [32]; int i = 0; while (* key ) KAPITEL 4. DIE UMSETZUNG 86 { 34 int j = 0; if (* key >= ’0 ’ && * key <= ’9 ’) j =* key - ’0 ’; if (* key >= ’A ’ && * key <= ’F ’) j =* key - ’A ’ +10; if (* key >= ’a ’ && * key <= ’f ’) j =* key - ’a ’ +10; 35 36 37 38 39 if ( i &1) w e p k e y[ i /2] = ( w e p k e y[ i /2]&0 xF0 )| j ; else w e p k e y[ i /2] = ( w e p k e y[ i /2]&0 xF )|( j < <4); i ++; key ++; 40 41 42 43 } 44 45 // Mit Access - Point v e r b i n d e n int res = W i f i _ C o n n e c t A P (& ap , 1 , 0 , w e p k e y ); if ( res == -1) r e t u r n; // V e r b i n d u n g s s t a t u s für die T e x t a u s g a b e char * a s s o c s t a t u s _ s t r i n g s [] = { " ASSOCSTATUS_DISCONNECTED", " ASSOCSTATUS_SEARCHING", " ASSOCSTATUS_AUTHENTICATING ", " ASSOCSTATUS_ASSOCIATING", " ASSOCSTATUS_ACQUIRINGDHCP ", " ASSOCSTATUS_ASSOCIATED", " ASSOCSTATUS_CANNOTCONNECT " }; 46 47 48 49 50 51 52 53 54 55 56 57 58 59 // Auf V e r b i n d u n g oder V e r w e i g e r u n g w a r t e n while ( W i f i _ A s s o c S t a t u s () != A S S O C S T A T U S _ A S S O C I A T E D && W i f i _ A s s o c S t a t u s () != A S S O C S T A T U S _ C A N N O T C O N N E C T ) { // A k t u e l l e n S t a t u s für die F e h l e r s u c h e a u s g e b e n // P A _ O u t p u t T e x t (0 , 0 , 18 , // a s s o c s t a t u s _ s t r i n g s [ W i f i _ A s s o c S t a t u s ()]); P A _ W a i t F o r V B L (); } // Speichern , ob e r f o l g r e i c h v e r b u n d e n if ( W i f i _ A s s o c S t a t u s () == A S S O C S T A T U S _ A S S O C I A T E D ) a p C o n n e c t e d = true ; 60 61 62 63 64 65 66 67 68 69 70 71 72 } Nachdem eine Verbindung zum Access-Point hergestellt ist, unterscheidet sich der Server vom Client bei der darauf folgenden Texteingabe in der Weise, dass beim Server die IP-Adresse, welche dann bei den Clients eingetragen werden muss, schon eingesetzt ist, wie Abbildung 4.22 zeigt. Nach der Texteingabe wird das eigentliche Spiel gestartet und dabei auch ein Socket in der Network-Klasse erzeugt. Die Methode ist bei Server und Client recht ähnlich, einzig die Server-IP kommt beim Client dazu, wie in Listing 4.28 zu sehen ist. Danach wartet der Server darauf, dass sich die Clients mit ihm verbinden. KAPITEL 4. DIE UMSETZUNG (a) Texteingabe des Servers mit voreingesetzter IP-Adresse 87 (b) Texteingabe des Clients Abbildung 4.22: Unterschiede bei der Texteingabe zwischen Server (a) und Client (b) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 Listing 4.28: Erzeugen der Netzwerk-Sockets int N e t w o r k :: start ( char * host ) { if ( s e r v e r) { // L i s t e n an Socket , m a x i m a l 3 V e r b i n d u n g e n r e t u r n P A _ I n i t S e r v e r (& s e r v e r Socket , 7777 , P A _ N O N B L O C KI NG_ TC P , 3); } else { // V e r b i n d e mit S e r v e r r e t u r n P A _ I n i t S o c k e t (& c l i e n t Socket , host , 7777 , P A _ N O N B L O C K I N G _ T C P ); } } 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 int N e t w o r k :: c h e c k F o r C l i e n t s () { s t r u c t s o c k a d d r _ i n sa ; int s a L e n g t h; // Neue V e r b i n d u n g a k z e p t i e r e n int res = a c c e p t( s e r v e r Socket , ( s t r u c t s o c k a d d r *) & sa , & s a L e n g t h ); if ( res == -1) { r e t u r n 0; } else { // C l i e n t e r f o l g r e i c h v e r b u n d e n KAPITEL 4. DIE UMSETZUNG c l i e n t S o c k e t s [ c l i e n t C o u n t] = res ; c l i e n t C o u n t ++; r e t u r n 1; 31 32 33 } 34 35 88 } Für jede unterschiedliche Type von Nachricht, die über das Netzwerk gesendet wird, wurde eine eigene Funktion zum Senden und eine zum Empfangen in der Network-Klasse implementiert und ein numerischer Identifier definiert. Beim Empfänger wird dann beim Abfragen, ob eine neue Nachricht empfangen wurde, der Identifer der neuen Nachricht zurückgegeben. So kann die richtige Funktion zum Empfangen verwendet werden. Listing 4.29 zeigt Beispiele für Funktionen zum Senden und Empfangen von Nachrichten. 1 2 3 4 5 6 7 Listing 4.29: Senden und Empfangen von Nachrichten über das Netzwerk // Daten der N e t z w e r k n a c h r i c h t lesen u16 R e a d U I n t 1 6 F r o m B y t e A r r a y ( u n s i g n e d char * buffer , int o f f s e t) { r e t u r n ( u16 )( b u f f e r[ o f f s e t] | ( b u f f e r[ o f f s e t + 1] << 8)); } 8 9 10 11 12 13 14 15 16 17 18 19 20 s16 R e a d I n t 1 6 F r o m B y t e A r r a y ( u n s i g n e d char * buffer , int o f f s e t) { u16 t e m p _ v a l u e = R e a d U I n t 1 6 F r o m B y t e A r r a y ( buffer , o f f s e t); int r e s u l t = ( t e m p _ v a l u e & 0 x7FFF ); if (( t e m p _ v a l u e & 0 x8000 ) != 0) { r e s u l t = - r e s u l t; } r e t u r n ( s16 ) r e s u l t; } 21 22 23 24 25 26 27 28 29 30 31 // Daten in die N e t z w e r k n a c h r i c h t s c h r e i b e n void W r i t e U I n t 1 6 T o B y t e A r r a y ( u16 value , u n s i g n e d char * buffer , int o f f s e t) { for ( int i = 0; i < 2; ++ i ) { b u f f e r[ o f f s e t + i ] = ( u n s i g n e d char )( ( value >> ( i * 8)) & 0 xFF ); } } 32 33 34 void W r i t e I n t 1 6 T o B y t e A r r a y ( s16 value , u n s i g n e d char * buffer , int o f f s e t) KAPITEL 4. DIE UMSETZUNG 35 { W r i t e U I n t 1 6 T o B y t e A r r a y (( u16 )(( value < 0) ? ((( - value ) & 0 x7FFF ) | 0 x8000 ) : ( value & 0 x7FFF )) , buffer , o f f s e t ); 36 37 38 89 } 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 // Neue Daten e m p f a n g e n int N e t w o r k :: u p d a t e () { int j = -1; int l e n g t h = 0; if ( s e r v e r) { // Daten von allen 3 C l i e n t s e m p f a n g e n // und a n e i n a n d e r h ä n g e n for ( int i = 0; i < 3; i ++) { if ( c l i e n t S o c k e t s [ i ]) j = recv ( c l i e n t S o c k e t s [ i ] , t c p _ t e m p + length , 14 , 0); if ( j != -1) l e n g t h += j ; } } else { // Daten vom S e r v e r e m p f a n g e n l e n g t h = recv ( c l i e n t Socket , tcp_temp , 1024 , 0); } // Wenn Daten e m p f a n g e n w u r d e n if ( l e n g t h > 0) { m e s s a g e L e n g t h = l e n g t h; // Nachricht - I d e n t i f i z i e r u n g z u r ü c k g e b e n r e t u r n R e a d U I n t 1 6 F r o m B y t e A r r a y ( tcp_temp , 0); } else r e t u r n -1; } 71 72 73 74 75 76 77 78 79 80 81 82 83 // S p i e l z u g aus N a c h r i c h t lesen int N e t w o r k :: r e c v N e w S t o n e ( u16 * player , u16 * mColor , u16 * sColor , u16 *x , u16 *y , u16 * dir ) { * p l a y e r = R e a d U I n t 1 6 F r o m B y t e A r r a y ( tcp_temp , o f f s e t +2); * m C o l o r = R e a d U I n t 1 6 F r o m B y t e A r r a y ( tcp_temp , o f f s e t +4); * s C o l o r = R e a d U I n t 1 6 F r o m B y t e A r r a y ( tcp_temp , o f f s e t +6); * x = R e a d U I n t 1 6 F r o m B y t e A r r a y ( tcp_temp , o f f s e t + 8); * y = R e a d U I n t 1 6 F r o m B y t e A r r a y ( tcp_temp , o f f s e t + 10); * dir = R e a d U I n t 1 6 F r o m B y t e A r r a y ( tcp_temp , o f f s e t + 12); o f f s e t += 14; // Ist noch eine N a c h r i c h t v o r h a n d e n KAPITEL 4. DIE UMSETZUNG if ( o f f s e t < m e s s a g e L e n g t h) // Nachricht - I d e n t i f i z i e r u n g z u r ü c k g e b e n r e t u r n R e a d U I n t 1 6 F r o m B y t e A r r a y ( tcp_temp , o f f s e t ); else { o f f s e t = 0; r e t u r n -1; } 84 85 86 87 88 89 90 91 92 90 } 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 // N a c h r i c h t s e n d e n void N e t w o r k :: s e n d M e s s a g e ( u n s i g n e d char * message , int m e s s a g e L e n g t h) { if ( s e r v e r) { // An alle 3 C l i e n t s for ( int i = 0; i < 3; i ++) { if ( c l i e n t S o c k e t s [ i ]) send ( c l i e n t S o c k e t s [ i ] , message , m e s s a g eLen gth , 0); } } else { // An den S e r v e r send ( c l i e n t Socket , message , m e s s a g eLen gth , 0); } } 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 // S p i e l z u g in N a c h r i c h t s c h r e i b e n void N e t w o r k :: s e n d N e w S t o n e( u16 player , u16 mColor , u16 sColor , u16 x , u16 y , u16 dir ) { u n s i g n e d char * m e s s a g e = new u n s i g n e d char [14]; // Nachricht - I d e n t i f i z i e r u n g aus D e f i n e W r i t e U I n t 1 6 T o B y t e A r r a y ( N E W S T O N E _ MESS AGE , message , 0); W r i t e U I n t 1 6 T o B y t e A r r a y ( player , message , 2); W r i t e U I n t 1 6 T o B y t e A r r a y ( mColor , message , 4); W r i t e U I n t 1 6 T o B y t e A r r a y ( sColor , message , 6); W r i t e U I n t 1 6 T o B y t e A r r a y (x , message , 8); W r i t e U I n t 1 6 T o B y t e A r r a y (y , message , 10); W r i t e U I n t 1 6 T o B y t e A r r a y ( dir , message , 12); s e n d M e s s a g e ( message , 14); } Tabelle 4.3 zeigt eine Übersicht über alle in Einfach Genial DS verwendeten Netzwerk-Nachrichten und Abbildung 4.23, wie diese Nachrichten bei einem normalen Spiel verwendet werden. KAPITEL 4. DIE UMSETZUNG Nachricht Spielername Spielstart Spielzug Timeout Disconnect 91 Parameter char *name char *name1, char *name2, char *name3, char *name4 int player, int mColor, int sColor, int x, int y, int dir int player, char *newName Tabelle 4.3: Netzwerk-Nachrichten des Spieles Einfach Genial DS Abbildung 4.23: Netzwerk-Nachrichten im Zeitablauf bei einem normalen Spiel Vom Spielablauf her sind fast keine Unterschiede zwischen einem normalen Spiel und einem Multiplayer-Spiel festzustellen. Einzig die Zeit, die ein Spieler für seinen Zug hat, ist beim Multiplayer-Spiel begrenzt, damit kein Spieler das Spiel blockieren kann. Sollte ein Client ausfallen, wird der Spieler von einem Computerspieler übernommen, damit sich die Spieleranzahl nicht ändert, da die Spielfeldgröße an die Spieleranzahl angepasst ist. Kapitel 5 Evaluierung Wie bei jedem Projekt, bei dem eine neue Technologie verwendet wird, sind die Erfahrungen, die man damit sammelt, nicht nur positiv. Oft werden die verlockendsten Konzepte durch Kleinigkeiten unmöglich gemacht und müssen Funktionen mehrmals geändert werden, bis diese funktionieren. Für den größeren Teil des Projektes wurden Funktionen der PaLib verwendet, doch da diese noch unvollständig ist, wurden für manche Bereiche auch Funktionen der NdsLib verwendet. Da die NdsLib sehr hardwarenah ist im Gegensatz zur PaLib, sind die Funktionen sehr unterschiedlich, weshalb auch die Probleme sich unterscheiden. Deswegen werden die beiden SDKs einzeln betrachtet und evaluiert. 5.1 PaLib Die PaLib ist noch in der Entwicklung und verändert sich deswegen kontinuierlich. Viele der Basisfunktionen sind schon sehr weit ausgereift, jedoch treten vor allem in den neueren Bereichen immer wieder Fehler auf. Da die PaLib mit dem kompletten Source-Code installiert wird, kann man versuchen kleinere Probleme selbst zu beheben. Aufgrund der Komplexität ist dies jedoch nur für jemanden ratsam, der bereits einige Erfahrung mit der Nintendo DS Programmierung hat. Ein sehr gutes Beispiel für ein solches Problem trat bei dem Versuch, die Spieleanleitung für das Spiel Einfach Genial DS“ als Video anzeigen ” zu lassen auf. Videos aus dem Dateisystem abzuspielen ist eine relativ neue Funktion in der PaLib und vielleicht deswegen noch sehr problematisch. Die Videos müssen mit dem Werkzeug viDeoconverterS in ein spezielles Format umgewandelt werden. Das Video wird in mehreren .vid Dateien gespeichert, die je 1 MB groß sind, und zu jeder wird eine .h Datei erzeugt über die es dann im Programmcode geladen werden kann. Die ersten Probleme traten bereits beim Umwandeln auf, da aus einem Grund, der nicht geklärt werden konnte, die obere Hälfte des Videos eine 92 KAPITEL 5. EVALUIERUNG (a) Bild des Originalvideos 93 (b) Videodarstellung auf dem Nintendo DS Abbildung 5.1: Vergleich zwischen einem Video (a) und dessen Darstellung auf dem Nintendo DS (b) sehr schlechte Qualität hatte, während die untere Hälfte normal aussah. Abbildung 5.1 zeigt wie das Video auf dem Nintendo DS angezeigt wird, wobei hier nur die untere Hälfte für die Informationen verwendet wurde, da der Text nur dort lesbar war. Das nächste Problem, das auftrat, war, dass die Videos beim Abspielen nicht unterbrochen werden konnten. Hier wurde die Video Funktion in der PaLib angepasst um dies zu ermöglichen. Listing 5.1 zeigt welche Bereiche in der PaLib dafür verändert wurden. Listing 5.1: Modifizierte Video-Funktionen der PaLib 1 2 3 4 5 6 7 int P A _ F S L o a d V i d( s8 file , int * taille , int fps , int d e p a r t) { tab = ( int *) m a l l o c( s i z e o f( int ) * 500); int a , j ; for ( a = 0; a < 500; a ++) { tab [ a ] = t a i l l e[ a + d e p a r t]; KAPITEL 5. EVALUIERUNG } int i = 0; while ( tab [ i ] > 0) { if ( tab [ i ] == 1) P A _ W a i t F o r V B L (); else if ( j p g c o r r e c t (( u8 *) P A _ P A F S F i l e( file ) , i , tab )) P A _ L o a d J p e g (1 , ( void *) b u f f e r j p g ); r e m p l a c e s i z e( i ); i ++; for ( j =0; ( float ) j < ( float )7 * (16 / fps ); j ++) { // Zeile e i n g e f ü g t um Video a b z u b r e c h e n if ( S t y l u s. N e w p r e s s) r e t u r n 1; P A _ W a i t F o r V B L (); } } for ( j = i ; deja0 == 0; j ++){ r e m p l a c e s i z e( j ); } free ( tab ); return i; 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 } 30 31 32 33 34 35 36 37 38 39 40 41 42 int P A _ F S L o a d M u l t i V i d ( s8 file ) { h v i d f i l e = file ; int start = 0 , n u m v i d = 0; v i d s i z e s = ( int *) m a l l o c( s i z e o f( int) * 500); char name [ 2 5 5 ] ; int end = 0; int fps = P A _ F S V i d f p s( file ); m e m s e t( vidsizes , 0 , s i z e o f( v i d s i z e s )); P A _ F S s i z e s( file , start , & end ); start = end ; v i d p o s = end ; 43 44 45 46 47 48 49 50 51 52 53 54 55 56 94 while ( true ) { deja0 = 0; s p r i n t f( name , " % s % d " , c o r r e c t n a m e ( P A _ F S F i l e[ file ]. Name ) , n u m v i d ); s8 v i d f i l e; v i d f i l e = P A _ F S G e t F i l e (0 , name , " vid " ); int a ; for ( a = 0; a < 100; a ++) { // Zeile e i n g e f ü g t um Video a b z u b r e c h e n if ( S t y l u s. N e w p r e s s) r e t u r n 1; P A _ W a i t F o r V B L (); KAPITEL 5. EVALUIERUNG } if ( v i d f i l e > -1) { P A _ F S L o a d V i d( vidfile , vidsizes , fps , 1); } else { r e t u r n 1; } n u m v i d ++; 57 58 59 60 61 62 63 64 65 66 } r e t u r n 1; 67 68 69 95 } Das letzte Problem, das beim Video auftrat, war der Grund, warum dann vorerst auf die Verwendung eines Videos im Spiel Einfach Genial DS“ ” verzichtet wurde, stattdessen werden die Regeln zur Zeit als Bilder-Show dargestellt. Das Problem war, dass anscheinend das Einfügen von weiteren Funktionen in das Spiel dazu führte, dass das Video beim Abspielen zu einem Abstürzen des Nintendo DS führt. Da nicht nach jeder Eingefügten Funktion das Video getestet wurde, lässt sich nicht mehr feststellen, welche Funktion der Grund für den Fehler ist. Da der Fehler sowohl auf dem Gerät als auch im Emulator auftritt ist es bisher noch nicht gelungen die Ursache dafür zu finden. Bei einigen anderen Funktionen der PaLib, wie den Sprites, den gekachelten Hintergründen oder dem Sound, traten ähnliche Probleme auf, jedoch konnten hier Lösungen gefunden werden und um die Fehler herum gearbeitet werden. Ein anderer Bereich, in dem es zu einigen Problemen kam, war die Dokumentation der PaLib. Es gibt zwar eine Reihe von Tutorials, die die ersten Schritte darstellen, jedoch ist es schwer genauere Hintergrundinformationen zu bekommen. So musste der Aufbau des PaLib Dateisystems durch Analysieren einer Datei im Hexeditor ermittelt werden. 5.2 NdsLib Im Gegensatz zur PaLib hat die NdsLib keine zentrale Website auf der Informationen erhältlich sind. Die Informationen sind, falls sie vorhanden sind, über eine Vielzahl von Websites verstreut und müssen mühsam zusammengesucht werden. Da die NdsLib jedoch hardwarenah ist, benötigen die Informationen, wenn man sie findet, keine weiteren Hintergrundinformationen mehr. So kann man eine Vielzahl von Funktionen der PaLib besser verstehen, wenn man sich die entsprechenden Funktionen der NdsLib ansieht. Wenn man zu Bereichen der NdsLib keine Informationen im Internet findet, kann man als nächsten Schritt versuchen, die notwendigen Informa- KAPITEL 5. EVALUIERUNG 96 tionen aus einem der Beispiele, die bei der NdsLib mitinstalliert werden, herauszufinden. Dies war zum Beispiel beim Spiel Einfach Genial DS“ bei ” der Netzwerkprogrammierung notwendig. Das Beispiel, das verwendet wurde um den Netzwerk-Code des Spieles Einfach Genial DS“ zu entwickeln, war ” ca. 2200 Zeilen lang und fast ohne Kommentare. Durch mehrmaliges Durcharbeiten des Beispiels und viele Tests konnten die notwendigen Bereiche, die über das gesamte Beispiel verstreut waren, isoliert werden und die NetzwerkKlasse mit nur 125 Zeilen (Ohne Nachrichten) realisiert werden. 5.3 Allgemeine Nintendo DS Programmierung Bei der Nintendo DS Programmierung ist man, unabhängig davon welche SDK man verwendet, von den Fähigkeiten des Nintendo DS abhängig. Solange man diese bereits bei der Planung berücksichtigt wird man hier kaum auf Probleme stoßen. Jedoch wird man, vor allem bei den ersten Programmen für den Nintendo DS, oft darauf stoßen, dass man eine Besonderheit oder Einschränkung des Nintendo DS übersehen hat, was auch bei den Vorprojekten des Spiels Einfach Genial DS“ der Fall war. Meistens wird einem ” dann keine andere Möglichkeit offen bleiben als ganze Bereiche neu zu planen und neu zu programmieren. Eine Fehlerquelle, die ebenfalls unabhängig von der verwendeten SDK ist, sind Speicherverletzungen. Da alle Funktionen über ihre Speicherbereiche gesteuert werden und diese alle nebeneinander liegen, hat hier jeder Fehler Auswirkungen, die oft zu einem Absturz führen können. Kapitel 6 Schlussbemerkungen 6.1 Erreichte Ziele Das Hauptziel, nämlich das Brettspiel Einfach Genial“ für den Nintendo ” DS umzusetzten, wurde bis auf einige Sonderregeln (Ein Spieler Modus, Team Modus) komplett erreicht. Das Spielgefühl des Brettspieles wurde laut Aussagen der Tester ebenfalls sehr gut erreicht. Abbildung 6.1 zeigt den direkten Vergleich zwischen dem Brettspiel und der Umsetzung für den Nintendo DS. Eines der sekundären Ziele war es, möglichst vielfältig die Möglichkeiten des Nintendo DS zu nutzen. Dieses Ziel wurde zwar nicht vollständig erreicht, aber es wurden ein Großteil der Funktionen des Nintendo DS verwendet. Die Gründe warum einige Funktionen nicht implementiert wurden reichen von nicht behebbaren Fehlern (Video) über nicht ins Spiel integrierbar (Mikrophon) bis zu nicht genug Zeit / Resourcen (3D). 6.2 Ausblick Bei dem Testen des Spiels Einfach Genial“ wurden noch einige Verbes” serungsmöglichkeiten aufgezeigt. So wurde vorgeschlagen, vor dem Setzten des Spielsteins noch eine Möglichkeit zu bieten die Position und Ausrichtung des Steins zu korrigieren, da der Stylus sehr ungenau ist, und hier oft Spielsteine falsch platziert wurden. Dieser Zwischenschritt könnte etwa wie in Abbildung 6.2 aussehen. 97 KAPITEL 6. SCHLUSSBEMERKUNGEN (a) Original Brettspiel Einfach Genial“ ” (b) Einfach Genial DS“ ” (c) Einfach Genial DS“ (Spielfeld ge” zoomt) Abbildung 6.1: Vergleich des Original Brettspieles (a) zu der Umsetzung für den Nintendo DS (b–c) 98 KAPITEL 6. SCHLUSSBEMERKUNGEN Abbildung 6.2: Entwurf für die Grafik des Menüs zum Anpassen der Spielsteinposition beim Setzten 99 Anhang A Inhalt der CD-ROM Dateisystem: Joliet Modus: Single-Session (DVD-ROM) A.1 Diplomarbeit Pfad: /diplomarbeit/ dm05027 wolfgang schermann da.pdf Diplomarbeit (PDF-File) dm05027 wolfgang schermann da.ps Diplomarbeit (PS-File) A.2 Projekt Pfad: /projekt/ EinfachGenial.rar . . . . EinfachGenial.nds . . . desmume.exe . . . . . . A.3 Literatur Pfad: /literatur/ . . . . . . . . . . . . . . A.4 Sonstiges Pfad: /latex/ . . . . . . . . . . . . . . images/ . . . . . . . . . Projekt Einfach Genial Spiel Einfach Genial für den Emulator Emulator DeSmuMe Dokumente und Bildmaterial Latex Dateien Bilder und Grafiken 100 Anhang B Listings 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 Listing B.1: Prototype des Spielfeldarrays u n s i g n e d short m a p P r o t o t y p e [ 3 5 ] [ 1 0 ] = { { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, { 0, 0, 0, 0, 4, 0, 0, 0, 0, 0 }, { 0, 0, 0, 0, 4, 4, 0, 0, 0, 0 }, { 0, 0, 0, 4, 3, 4, 0, 0, 0, 0 }, { 0, 0, 0, 4, 3, 3, 4, 0, 0, 0 }, { 0, 0, 4, 3, 2, 3, 4, 0, 0, 0 }, { 0, 0, 4, 3, 2, 2, 3, 4, 0, 0 }, { 0, 4, 3, 2, 2, 2, 3, 4, 0, 0 }, { 0, 4, 3, 2, 2, 2, 2, 3, 4, 0 }, { 0, 3, 2, 2, 2, 2, 2, 3, 0, 0 }, { 0, 4, 2, 2, 2, 2, 2, 2, 4, 0 }, { 0, 3, 2, 2, 2, 2, 2, 3, 0, 0 }, { 0, 4, 2, 2, 2, 2, 2, 2, 4, 0 }, { 0, 3, 2, 2, 2, 2, 2, 3, 0, 0 }, { 0, 4, 2, 2, 2, 2, 2, 2, 4, 0 }, { 0, 3, 2, 2, 2, 2, 2, 3, 0, 0 }, { 0, 4, 2, 2, 2, 2, 2, 2, 4, 0 }, { 0, 3, 2, 2, 2, 2, 2, 3, 0, 0 }, { 0, 4, 2, 2, 2, 2, 2, 2, 4, 0 }, { 0, 3, 2, 2, 2, 2, 2, 3, 0, 0 }, { 0, 4, 2, 2, 2, 2, 2, 2, 4, 0 }, { 0, 3, 2, 2, 2, 2, 2, 3, 0, 0 }, { 0, 4, 3, 2, 2, 2, 2, 3, 4, 0 }, { 0, 4, 3, 2, 2, 2, 3, 4, 0, 0 }, { 0, 0, 4, 3, 2, 2, 3, 4, 0, 0 }, { 0, 0, 4, 3, 2, 3, 4, 0, 0, 0 }, { 0, 0, 0, 4, 3, 3, 4, 0, 0, 0 }, { 0, 0, 0, 4, 3, 4, 0, 0, 0, 0 }, { 0, 0, 0, 0, 4, 4, 0, 0, 0, 0 }, { 0, 0, 0, 0, 4, 0, 0, 0, 0, 0 }, { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, 101 ANHANG B. LISTINGS 34 35 36 37 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 } }; Listing B.2: Array mit der Zuordnung von Kacheln zu Spielsteinen u n s i g n e d short t i l e s S m a l l [ 2 8 ] [ 6 ] = { // 4 Werte für S t e i n e mit 4 K a c h e l n dann // 2 Werte für S t e i n e mit 6 K a c h e l n da // nur die m i t t l e r e n 2 b e n ö t i g t w e r d e n // l . o . r . o . l . u . r . u . o . u. { 12 , 12 , 12 , 12 , 12 , 12 } , // 0 kein Feld -> kein Feld { 43 , 44 , 59 , 60 , 47 , 63 } , // 1 l e e r e s Feld -> kein Feld { 2, 3, 21 , 22 , 9, 28 } , // 2 g e l b e r Stein -> kein Feld { 4, 5, 23 , 24 , 10 , 29 } , // 3 g r ü n e r Stein -> kein Feld { 6, 7, 25 , 26 , 11 , 30 } , // 4 b l a u e r Stein -> kein Feld { 39 , 40 , 55 , 56 , 45 , 61 } , // 5 lila Stein -> kein Feld { 41 , 42 , 57 , 58 , 46 , 62 } , // 6 o r a n g e r Stein -> kein Feld { 0, 1, 19 , 20 , 8, 27 } , // 7 roter Stein -> kein Feld { 12 , 12 , 12 , 12 , 12 , 12 } , // nicht v e r w e n d e t { 12 , 12 , 12 , 12 , 12 , 12 } , // nicht v e r w e n d e t { 147 , 146 , 127 , 126 , 12 , 12 } , // 10 kein Feld -> l e e r e s Feld { 128 , 129 , 148 , 149 , 47 , 63 } , // 11 l e e r e s Feld -> l e e r e s Feld { 82 , 83 , 103 , 104 , 9 , 28 } , // 12 g e l b e r Stein -> l e e r e s Feld { 84 , 85 , 105 , 106 , 10 , 29 } , // 13 g r ü n e r Stein -> l e e r e s Feld { 86 , 87 , 107 , 108 , 11 , 30 } , // 14 b l a u e r Stein -> l e e r e s Feld { 122 , 123 , 142 , 143 , 45 , 61 } , // 15 lila Stein -> l e e r e s Feld { 124 , 125 , 144 , 145 , 46 , 62 } , // 16 o r a n g e r Stein -> l e e r e s Feld { 80 , 81 , 101 , 102 , 8 , 27 } , // 17 roter Stein -> l e e r e s Feld { 12 , 12 , 12 , 12 , 12 , 12 } , // 18 102 ANHANG B. LISTINGS { 12 , 12 , 12 , 12 , 12 , 12 } , // 19 { 139 , 138 , 119 , 118 , 12 , 12 } , // 20 kein Feld -> b e l i e b i g e r Stein { 120 , 121 , 140 , 141 , 47 , 63 } , // 21 l e e r e s Feld -> b e l i e b i g e r Stein { 74 , 75 , 95 , 96 , 9, 28 } , // 22 g e l b e r Stein -> b e l i e b i g e r Stein { 76 , 77 , 97 , 98 , 10 , 29 } , // 23 g r ü n e r Stein -> b e l i e b i g e r Stein { 78 , 79 , 99 , 100 , 11 , 30 } , // 24 b l a u e r Stein -> b e l i e b i g e r Stein { 114 , 115 , 134 , 135 , 45 , 61 } , // 25 lila Stein -> b e l i e b i g e r Stein { 116 , 117 , 136 , 137 , 46 , 62 } , // 26 o r a n g e r Stein -> b e l i e b i g e r Stein { 72 , 73 , 93 , 94 , 8, 27 } // 27 roter Stein -> b e l i e b i g e r Stein 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 103 }; Listing B.3: Array mit der Zuordnung von 4 Kacheln der vergrößerten Darstellung zu einer Kachel der kleinen Darstellung (Im Kommentar die entsprechende kleinere Kachel) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 u n s i g n e d short t i l e s L a r g e [ 1 5 0 ] [ 4 ] = { 153 , 154 , 167 , 168 } , { 155 , 156 , 169 , 170 } , { 153 , 157 , 171 , 172 } , { 158 , 156 , 173 , 174 } , { 153 , 14 , 175 , 32 } , { 15 , 156 , 33 , 176 } , { 153 , 17 , 177 , 36 } , { 18 , 156 , 37 , 178 } , { 159 , 160 , 168 , 169 } , { 161 , 162 , 172 , 173 } , { 163 , 164 , 179 , 33 } , { 165 , 166 , 180 , 37 } , // Nicht v e r w e n d e t e F e l d e r { 12 , 12 , 12 , 12 } , { 12 , { 12 , 12 , 12 , 12 } , { 12 , { 12 , 12 , 12 , 12 } , { 12 , { 12 , 12 , 12 , 12 } , { 181 , 182 , 196 , 197 } , { 183 , 184 , 198 , 199 } , { 185 , 186 , 200 , 201 } , { 187 , 188 , 202 , 199 } , { 189 , 49 , 196 , 65 } , { 50 , 190 , 66 , 203 } , { 191 , 53 , 204 , 69 } , { 54 , 190 , 70 , 205 } , { // 0 // 1 // 2 // 3 // 4 // 5 // 6 // 7 // 8 // 9 // 10 // 11 12 , 12 , 12 , // 19 // 20 // 21 // 22 // 23 // 24 // 25 // 26 12 , 12 , 12 , 12 } , 12 } , 12 } , ANHANG B. LISTINGS 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { 182 , 192 , 49 , 194 , 12 , 12 , 12 , 12 , 153 , 209 , 153 , 211 , 88 , 1115 , 212 , 214 , 91 , 12 , 12 , 12 , 12 , 226 , 227 , 229 , 231 , 130 , 112 , 3290 , 234 , 112 , 12 , 12 , 12 , 12 , 244 , 155 , 244 , 158 , 244 , 15 , 244 , 18 , 13 , 155 , 13 , 158 , 13 , 15 , 13 , 183 , 197 , 187 , 201 , 193 , 65 , 195 , 207 , 12 , 12 , 12 12 , 12 , 12 12 , 12 , 12 12 , 12 , 12 208 , 216 , 156 , 218 , 210 , 220 , 156 , 222 , 91 , 109 , 89 , 112 , 213 , 217 , 215 , 225 , 1115 , 112 , 12 , 12 , 12 12 , 12 , 12 12 , 12 , 12 12 , 12 , 12 3290 , 204 , 228 , 237 , 230 , 196 , 232 , 239 , 112 , 150 , 131 , 152 , 227 , 240 , 235 , 242 , 112 , 2139 , 12 , 12 , 12 12 , 12 , 12 12 , 12 , 12 12 , 12 , 12 154 , 247 , 245 , 169 , 157 , 249 , 245 , 173 , 14 , 251 , 245 , 33 , 17 , 253 , 245 , 37 , 246 , 255 , 16 , 169 , 157 , 257 , 16 , 173 , 14 , 31 , 16 , 33 , 17 , 35 , 104 206 } , 202 } , 66 } , 70 } , { 12 , { 12 , { 12 , { 12 , 217 } , 219 } , 221 } , 223 } , 112 } , 110 } , 218 } , 222 } , 112 } , { 12 , { 12 , { 12 , // 27 // 28 // 29 // 30 12 , 12 , 12 , 12 , // 39 // 40 // 41 // 42 // 43 // 44 // 45 // 46 // 47 12 , 12 , 12 , 236 } , 205 } , 238 } , 199 } , 2139 } , 151 } , 241 } , 239 } , 243 } , } , { 12 , } , { 12 , } , { 12 , } , { 12 , 168 } , 248 } , 172 } , 250 } , 32 } , 252 } , 36 } , 254 } , 168 } , 256 } , 172 } , 258 } , 32 } , 34 } , 36 } , // 55 // 56 // 57 // 58 // 59 // 60 // 61 // 62 // 63 12 , 12 , 12 , 12 , // 72 // 73 // 74 // 75 // 76 // 77 // 78 // 79 // 80 // 81 // 82 // 83 // 84 // 85 // 86 }, }, }, }, }, }, }, }, 12 , 12 , 12 , 12 , 12 12 12 12 }, }, }, }, 12 , 12 , 12 , 12 } , 12 } , 12 } , 12 , 12 , 12 , 12 , 12 12 12 12 }, }, }, }, ANHANG B. LISTINGS 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { 18 , 12 , 12 , 12 , 259 , 183 , 261 , 187 , 263 , 50 , 265 , 54 , 267 , 183 , 269 , 187 , 48 , 50 , 52 , 54 , 12 , 12 , 12 , 280 , 209 , 280 , 211 , 12 , 282 , 268 , 283 , 13 , 209 , 286 , 211 , 12 , 89 , 90 , 1115 , 12 , 12 , 297 , 227 , 299 , 231 , 12 , 184 , 301 , 112 , 16 , 12 , 12 , 12 , 12 , 12 , 12 , 182 , 260 , 186 , 262 , 49 , 264 , 53 , 266 , 182 , 268 , 186 , 270 , 49 , 51 , 53 , 51 , 12 , 12 , 12 , 12 , 12 , 12 , 208 , 259 , 210 , 259 , 281 , 12 , 91 , 284 , 208 , 285 , 210 , 16 , 88 , 12 , 91 , 92 , 12 , 12 , 12 , 12 , 3290 , 298 , 230 , 300 , 181 , 12 , 112 , 302 , 105 37 , 12 12 12 271 , 198 , 272 , 202 , 271 , 66 , 275 , 70 , 277 , 198 , 279 , 202 , 64 , 66 , 68 , 70 , 12 12 12 287 , 218 , 289 , 222 , 12 , 291 , 278 , 112 , 293 , 218 , 295 , 222 , 12 , 110 , 111 , 112 , 12 12 248 , 237 , 272 , 239 , 12 , 199 , 256 , 307 , 38 } , } , { 12 , } , { 12 , }, 197 } , 247 } , 273 } , 247 } , 65 } , 274 } , 207 } , 276 } , 197 } , 278 } , 201 } , 278 } , 65 } , 67 } , 69 } , 71 } , } , { 12 , } , { 12 , }, 217 } , 288 } , 221 } , 290 } , 167 } , 12 } , 112 } , 292 } , 217 } , 294 } , 221 } , 296 } , 109 } , 12 } , 112 } , 113 } , } , { 12 , } , { 12 , 236 } , 276 } , 238 } , 247 } , 204 } , 12 } , 2139 } , 255 } , // 87 12 , 12 , 12 , 12 , 12 } , 12 } , // 93 // 94 // 95 // 96 // 97 // 98 // 99 // 100 // 101 // 102 // 103 // 104 // 105 // 106 // 107 // 108 12 , 12 , 12 , 12 , 12 } , 12 } , // 114 // 115 // 116 // 117 // 118 // 119 // 120 // 121 // 122 // 123 // 124 // 125 // 126 // 127 // 128 // 129 12 , 12 , 12 , 12 , // 134 // 135 // 136 // 137 // 138 // 139 // 140 // 141 12 } , 12 } , ANHANG B. LISTINGS { { { { { { { { 125 126 127 128 129 130 131 132 133 }; 303 , 227 , 305 , 231 , 12 , 131 , 132 , 112 , 3290 , 304 , 230 , 306 , 130 , 12 , 112 , 133 , 106 68 , 237 , 308 , 239 , 12 , 151 , 113 , 152 , 236 71 238 278 150 12 2139 111 }, }, }, }, }, }, }, } // 142 // 143 // 144 // 145 // 146 // 147 // 148 // 149 Literaturverzeichnis [1] Blickenstorfer, C. H.: Graffiti: Wow!!!! . Pen Computing Magazine, S. 30–31, Jänner 1995. [2] CAPCOM und Nintendo: The Legend of Zelda - The Minish Cap, 2005. Gameboy Advance Spiel. [3] Deutsches Bundesministerium der Justiz: Gesetz zur Regelung des Urheberrechts in der Informationsgesellschaft. Bundesgesetzblatt, 46:1774–1784, 2003. Kopie als PDF (Urheberrechtsgesetz.pdf). [4] DS News: Nintendo DS Development Emulator . URL, http:// nintendo-ds.dcemu.co.uk/dsdevemu.shtml, 2006. Kopie als PDF (Nintendo DS Development Emulator.pdf). [5] DSLinux Team: DSLinux . URL, http://www.dslinux.org, 2004. [6] Goldberg, D. und C. Richardson: Touch-typing with a stylus. In: CHI ’93: Proceedings of the INTERACT ’93 and CHI ’93 conference on Human factors in computing systems, S. 80–87, New York, NY, USA, 1993. ACM Press. [7] GP2X Community: GP2X . URL, http://wiki.gp2x.org/wiki/GP2X, 2005. Kopie als PDF (GP2X.pdf). [8] Isokoski, P.: A minimal device-independent text input method. MSc Thesis, University of Tampere, Tampere, Finland, 1999. Kopie als PDF (A-1999-14.pdf). [9] Knizia, R.: Spielanleitung Einfach Genial. http://www.kosmos.de, 2004. Kopie als PDF (Spielanleitung Einfach Genial.pdf). [10] KONAMI Computer Entertainment Kobe: Castlevania, 2001. Gameboy Advance Spiel. [11] M3 Adapter: NDS & GBA Movie Player . URL, http://www. m3adapter.com, 2005. Kopie als PDF (GBA movie player.pdf). 107 LITERATURVERZEICHNIS 108 [12] MacKenzie, S. und W. R. Soukoreff: Text Entry for Mobile Computing: Models and Methods,Theory and Practice. Human-Computer Interaction, 17:147–198, 2002. Kopie als PDF (hci3-2002.pdf). [13] Meier, S.: Civilization. MicroProse, 1991. Computerspiel. [14] NEXUS.gaming: Nintendo DS connects with the Opera web browser . URL, http://gaming.hexus.net/content/item.php?item=6410, 2006. Kopie als PDF (Nintendo DS Browser.pdf). [15] Nintendo: Nintendo DS . URL, http://www.nintendo-europe.com/ NOE/de/DE/system/nds topic1.jsp, 2004. Kopie als PDF (Nintendo DS.pdf). [16] Nintendo: Consolidated Financial Highlights. URL, http://www. nintendo.co.jp/ir/pdf/2007/070725e.pdf#page=8, 2007. Kopie als PDF (Consolidated Financial Highlights.pdf). [17] Sören, S.: Das neue Urheberrecht. URL, http://www.e-recht24.de/ artikel/urheberrecht/71.html, 2003. Kopie als PDF (Das neue Urheberrecht.pdf). [18] Steinke, L.: Spieleprogrammierung. bhv Buch Verlag, 2003. ISBN 3-8266-8075-8. [19] Wikipedia Community: Nintendo DS . URL, http://de.wikipedia.org/ wiki/Nintendo DS, 2007. Kopie als PDF (Nintendo DS - Wikipedia.pdf). [20] Wikipedia Community: Nintendo Entertainment System. URL, http://de.wikipedia.org/wiki/Nintendo Entertainment System, 2007. Kopie als PDF (Nintendo Entertainment System - Wikipedia.pdf). [21] Zhai, S., M. Hunter und B. A. Smith: The metropolis keyboard - an exploration of quantitative techniques for virtual keyboard design. In: UIST ’00: Proceedings of the 13th annual ACM symposium on User interface software and technology, S. 119–128, New York, NY, USA, 2000. ACM Press.