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.