Entwicklung eines flexiblen Objektmodells für ein ... - Jens Pfau
Entwicklung eines flexiblen Objektmodells für ein ... - Jens Pfau
Entwicklung eines flexiblen Objektmodells für ein ... - Jens Pfau
Erfolgreiche ePaper selbst erstellen
Machen Sie aus Ihren PDF Publikationen ein blätterbares Flipbook mit unserer einzigartigen Google optimierten e-Paper Software.
Technische Universität Darmstadt<br />
Fachbereich Informatik<br />
<strong>Entwicklung</strong> <strong><strong>ein</strong>es</strong> <strong>flexiblen</strong><br />
<strong>Objektmodells</strong> <strong>für</strong> <strong>ein</strong><br />
Gaming-Framework<br />
Bachelorarbeit von<br />
<strong>Jens</strong> <strong>Pfau</strong>, Stephan Mehlhase<br />
Betreuer: Dipl.-Inform. Patric Kabus<br />
Eingereicht bei: Prof. Alejandro P. Buchmann, Ph. D.<br />
Fachgebiet Datenbanken und Verteilte Systeme<br />
Fachbereich Informatik<br />
TU Darmstadt<br />
31. August 2006
Ehrenwörtliche Erklärung<br />
Hiermit versicheren wir, die vorliegende Arbeit ohne Hilfe Dritter und nur mit den angegebenen<br />
Quellen und Hilfsmitteln angefertigt zu haben. Alle Stellen, die aus den Quellen entnommen<br />
wurden, sind als solche kenntlich gemacht worden. Diese Arbeit hat in gleicher oder ähnlicher<br />
Form noch k<strong>ein</strong>er Prüfungsbehörde vorgelegen.<br />
Darmstadt, den 31. August 2006
Zusammenfassung<br />
Jedes Computerspiel basiert auf <strong>ein</strong>er Vielzahl von virtuellen Objekten, die unter anderem als<br />
Repräsentation visuell dargestellter Gegenstände, Charaktere und Gebäude fungieren. Deren<br />
Modellierung und Verwaltung ist daher eminent <strong>für</strong> die Funktionalität <strong><strong>ein</strong>es</strong> Spielesystems.<br />
Ein solches als Objektmodell bezeichnetes Subsystem wird üblicherweise <strong>für</strong> jede Spieleproduktion<br />
und deren spezifische Anforderungen erneut entworfen. Das bedarf den wiederholten<br />
Einsatz von Ressourcen zur Lösung des gleichen Problems.<br />
Diese Arbeit beschäftigt sich daher mit der Frage, welche Aspekte bei der Modellierung <strong><strong>ein</strong>es</strong> solchen<br />
Systems von Belang sind und welche Ansätze zur Umsetzung derselben existieren. Darauf<br />
aufbauend wird <strong>ein</strong>e Architektur entwickelt, die als vorrangige Anforderungen die Erweiterbarkeit<br />
und Wiederverwendbarkeit erfüllen soll, um flexibel an unterschiedliche Einsatzszenarien<br />
anpassbar zu s<strong>ein</strong>. Dadurch sollen Kosten und <strong>Entwicklung</strong>szeit in der Spieleproduktion gesenkt<br />
werden.
Danksagung<br />
Wir möchten uns an dieser Stelle ganz herzlich bei Patric Kabus bedanken, der es uns ermöglicht<br />
hat, <strong>ein</strong>en tiefgreifenden Einblick in <strong>ein</strong> interessantes Themengebiet zu erhalten, das uns auch<br />
in der Freizeit begeistert. S<strong>ein</strong>e Reaktionsgeschwindigkeit bei Anfragen war vor allem in der<br />
Endphase der Arbeit hilfreich.<br />
Ebenfalls Dank gebührt Silvia Graumann, Markus Günther und Paul Bächer, die wiederholt<br />
dieses Dokument korrekturgelesen und uns auf Unzulänglichkeiten und mögliche Erweiterungen<br />
hingewiesen haben.<br />
Abschließend möchten wir uns bei unseren Eltern <strong>für</strong> die sicher nicht selbstverständliche finanzielle<br />
Rückendeckung sowie bei Stephans Freundin Sabrina Jäger <strong>für</strong> ihre Geduld und Standhaftigkeit<br />
im Angesicht <strong><strong>ein</strong>es</strong> dauerhaft gestressten Partners bedanken.
Inhaltsverzeichnis<br />
1 Einleitung 1<br />
2 Motivation 3<br />
3 Taxonomie 5<br />
3.1 Objektstruktur . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6<br />
3.1.1 Daten . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6<br />
3.1.1.1 Modellierung . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7<br />
3.1.1.2 Definition . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10<br />
3.1.2 Verhalten . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 14<br />
3.1.2.1 Modellierung . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 14<br />
3.1.2.2 Definition . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 16<br />
3.1.3 Typen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 17<br />
3.1.3.1 Modellierung . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 17<br />
3.1.3.2 Definition . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 19<br />
3.1.3.3 Vererbung . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 20<br />
3.1.4 Objektordnung . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 21<br />
3.2 Dynamik zur Laufzeit . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 22<br />
3.3 Kommunikation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 22<br />
3.3.1 Initiative . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 23<br />
3.3.1.1 Polling . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 23<br />
3.3.1.2 Publish/Subscribe . . . . . . . . . . . . . . . . . . . . . . . . . . 23<br />
3.3.2 Änderungsübermittlung . . . . . . . . . . . . . . . . . . . . . . . . . . . . 24<br />
3.3.2.1 Delta Updates . . . . . . . . . . . . . . . . . . . . . . . . . . . . 24<br />
3.3.2.2 Aktionen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 24<br />
3.3.3 Kompression . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 24<br />
3.3.3.1 Semantik-abhängig . . . . . . . . . . . . . . . . . . . . . . . . . . 24<br />
3.3.3.2 Semantik-unabhängig . . . . . . . . . . . . . . . . . . . . . . . . 25<br />
3.3.4 Quality of Service (QoS) . . . . . . . . . . . . . . . . . . . . . . . . . . . . 25<br />
3.3.4.1 Semantik-abhängig . . . . . . . . . . . . . . . . . . . . . . . . . . 25<br />
3.3.4.2 Semantik-unabhängig . . . . . . . . . . . . . . . . . . . . . . . . 25<br />
3.4 Übersicht . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 25<br />
4 Anforderungen 28<br />
4.1 Darstellung der Objektstruktur . . . . . . . . . . . . . . . . . . . . . . . . . . . . 28<br />
4.2 Unabhängigkeit des Systems vom Spieldesign . . . . . . . . . . . . . . . . . . . . 29<br />
4.3 Persistenz . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 29
IV Inhaltsverzeichnis<br />
4.4 Performanz . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 29<br />
4.5 Erweiterbarkeit und Wiederverwendbarkeit . . . . . . . . . . . . . . . . . . . . . 30<br />
4.6 Abgrenzung und Einschränkung der Aufgabe . . . . . . . . . . . . . . . . . . . . 30<br />
5 Bestehende Systeme 31<br />
5.1 Dungeon Siege . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 31<br />
5.1.1 Systembeschreibung . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 31<br />
5.1.2 Einordnung . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 33<br />
5.1.3 Evaluation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 33<br />
5.2 Thief und Deus Ex . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 35<br />
5.2.1 Systembeschreibung . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 35<br />
5.2.2 Einordnung . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 35<br />
5.2.3 Evaluation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 36<br />
5.3 Worldforge Atlas Middleware . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 37<br />
5.3.1 Systembeschreibung . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 37<br />
5.3.2 Einordnung . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 38<br />
5.3.3 Evaluation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 38<br />
5.4 Britt L. Hannahs Vorschlag <strong>für</strong> <strong>ein</strong> Objektmodell . . . . . . . . . . . . . . . . . . 40<br />
5.4.1 Systembeschreibung . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 40<br />
5.4.2 Einordnung . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 43<br />
5.4.3 Evaluation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 44<br />
6 Design und Implementierung 45<br />
6.1 Spielobjekte . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 46<br />
6.1.1 Daten . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 46<br />
6.1.2 Verhalten . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 47<br />
6.1.3 Typisierung . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 50<br />
6.1.4 Anordnung . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 51<br />
6.2 Dynamik zur Laufzeit . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 53<br />
6.3 Kommunikation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 54<br />
6.3.1 Initiative . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 54<br />
6.3.2 Änderungsübermittlung . . . . . . . . . . . . . . . . . . . . . . . . . . . . 57<br />
6.3.3 Kompression und Quality of Service . . . . . . . . . . . . . . . . . . . . . 60<br />
6.4 Überblick . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 61<br />
7 Integration 65<br />
7.1 Objekthierachien . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 65<br />
7.2 Filter . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 68<br />
7.3 Aktionen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 69<br />
8 Zusammenfassung und Ausblick 73<br />
8.1 Kritikpunkte . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 73<br />
8.2 Ausblick . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 74<br />
IV <strong>Jens</strong> <strong>Pfau</strong> · Stephan Mehlhase
Abbildungsverzeichnis<br />
3.1 Baumansicht der Taxonomie (Objektstruktur) . . . . . . . . . . . . . . . . . . . . 26<br />
3.2 Baumansicht der Taxonomie (Dynamik) . . . . . . . . . . . . . . . . . . . . . . . 27<br />
3.3 Baumansicht der Taxonomie (Kommunikation) . . . . . . . . . . . . . . . . . . . 27<br />
5.1 Skizze des Dungeon Siege <strong>Objektmodells</strong> . . . . . . . . . . . . . . . . . . . . . . . 32<br />
5.2 Einordnung des von Dungeon Siege verwendeten Systems in die Taxonomie . . . 34<br />
5.3 Einordnung des von Thief und Deus Ex verwendeten Systems in die Taxonomie . 36<br />
5.4 Einordnung des in Atlas verwendeten Systems in die Taxonomie . . . . . . . . . 39<br />
5.5 UML Diagramm der von Britt L. Hannah vorgeschlagenen Architektur . . . . . . 41<br />
5.6 Einordnung des von Britt L. Hannah vorgeschlagenen Systems in die Taxonomie 44<br />
6.1 UML Diagramm der Zustände . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 47<br />
6.2 UML Diagramm der Aktionen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 49<br />
6.3 UML Diagramm der Typen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 51<br />
6.4 UML Diagramm der Anordnung von Objekten . . . . . . . . . . . . . . . . . . . 53<br />
6.5 UML Diagramm des TreeManager . . . . . . . . . . . . . . . . . . . . . . . . . . 54<br />
6.6 UML Diagramm des Observermechanismus . . . . . . . . . . . . . . . . . . . . . 57<br />
6.7 UML Diagramm der Deltaobjekte . . . . . . . . . . . . . . . . . . . . . . . . . . . 59<br />
6.8 UML Diagramm der Property Objekte . . . . . . . . . . . . . . . . . . . . . . . 61<br />
6.9 Überblick über die entwickelte Architektur . . . . . . . . . . . . . . . . . . . . . . 63<br />
6.10 Einordnung des von uns entwickelten Systems in die Taxonomie . . . . . . . . . . 64
Listings<br />
7.1 ROT13 Filter . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 68<br />
7.2 Die IncreaseHP Aktion . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 70<br />
7.3 Methode zum Beenden <strong>ein</strong>er Aktion . . . . . . . . . . . . . . . . . . . . . . . . . 71<br />
7.4 Ausführung <strong>ein</strong>er in Lua geschriebenen Aktion . . . . . . . . . . . . . . . . . . . 72
Kapitel 1<br />
Einleitung<br />
Eine lange Reise beginnt mit dem<br />
ersten Schritt.<br />
Chinesisches Sprichwort<br />
Die Computerspielebranche hat in den letzten Jahren immer <strong>ein</strong>drucksvollere Produkte auf den<br />
Markt geworfen. Mit der <strong>Entwicklung</strong> vom <strong>ein</strong>fachen Tetris zur komplexen Spielewelt hat sich<br />
vordergründig vor allem die visuelle Darstellung verändert, aber auch die Technologie, die <strong>ein</strong><br />
Spiel mit s<strong>ein</strong>er Logik und s<strong>ein</strong>en Regeln erst zum Leben erweckt, musste Adaptionen erfahren,<br />
um den wachsenden Anforderungen gerecht zu werden.<br />
Seit etwa <strong>ein</strong>em Jahrzehnt bietet sich den Spielern die Möglichkeit, ihre Fähigkeiten auf vielfältige<br />
Weise unter<strong>ein</strong>ander im Internet zu messen. Dabei sind die Möglichkeiten nur von der<br />
eigenen Fantasie begrenzt. Spieler tragen virtuelle Schlachten aus oder kreieren Charaktere, die<br />
zu tausenden mit eigener Geschichte und eigenem Beruf Teil persistenter Spielewelten sind, die<br />
nicht nur durch deren Betreiber, sondern auch durch die Teilnehmer selber be<strong>ein</strong>flusst werden.<br />
Dies bedeutet natürlich <strong>ein</strong>en enormen Aufwand sowohl administrativer als auch technischer<br />
Natur, denn schließlich wollen zahlende Spieler „ihre“ Welt zu möglichst jedem Zeitpunkt in<br />
konsistentem Zustand vorfinden. Um diesen Ansprüchen gerecht zu werden, betreiben die Spieleentwickler<br />
eigene Rechenzentren, in denen die Technologie untergebracht ist, die zur Aufrechterhaltung<br />
der virtuellen Welt nötig ist.<br />
Ein dezentraler Ansatz, bei dem die Ressourcen, die die Spieler in Form ihres eigenen Computers<br />
in das System <strong>ein</strong>bringen, zur Unterstützung der gesamten Anwendung verwendet werden,<br />
könnte hier Abhilfe schaffen. Das Abtreten von Verwantwortung seitens der Spielbetreiber birgt<br />
allerdings auch Probleme. Wenn Spiellogik nicht mehr nur auf <strong>ein</strong>er überschaubaren Anzahl<br />
von Computern stattfindet, ist die Gefahr gegeben, dass Spieler dies zu ihrem Vorteil ausnutzen.<br />
Zur Untersuchung dieser Probleme wird <strong>ein</strong> Framework <strong>für</strong> Onlinespiele nach dem oben<br />
vorgestellten, als Peer-to-Peer bezeichneten Ansatz erstellt.<br />
Das Ziel der vorliegenden Arbeit ist, <strong>ein</strong> Objektmodell zu entwickeln, welches sich in dieses<br />
Framework <strong>ein</strong>fügt. Als Objektmodell wird der Teil <strong><strong>ein</strong>es</strong> Spiels bezeichnet, welcher <strong>für</strong> die<br />
Verwaltung der Spielobjekte, nämlich der Elemente der Spielwelt wie Charaktere, Gegenstände,<br />
Gebäude und Fahrzeuge, verantwortlich zeichnet. Die Anforderungen an <strong>ein</strong> solches Subsystem<br />
sind vor allem bei Onlinespielen vielfältig, denn die Spielwelt muss zu jedem Zeitpunkt über
2 I. Einleitung<br />
alle Teilnehmer konsistent s<strong>ein</strong>.<br />
Zu diesem Thema existiert vor allem Literatur aus der Praxis, die <strong>für</strong> bestimmte Spiele spezifische<br />
Ansätze vorstellt. Jeder Spielehersteller entwirft naheliegenderweise <strong>ein</strong> Objektmodell,<br />
das auf s<strong>ein</strong>e Anforderungen maßgeschneidert ist. Im Folgenden wird deshalb, nachdem zunächst<br />
die Motivation <strong>für</strong> diese Arbeit dargelegt (Kapitel 2) wurde, <strong>ein</strong>e Taxonomie (Kapitel 3)<br />
erarbeitet, um <strong>ein</strong>en Überblick über die Möglichkeiten und Ideen zur Implementierung <strong><strong>ein</strong>es</strong><br />
solchen Systems zu erlangen. Darauf aufbauend werden die Anforderungen an das Objektmodell,<br />
welches im Laufe dieser Arbeit entstehen soll, identifiziert (Kapitel 4). Nachfolgend werden<br />
verschiedene Ansätze und Spieleentwicklungen nach der Taxonomie <strong>ein</strong>geordnet und gegen die<br />
Anforderungen geprüft (Kapitel 5). Daraus folgend wird <strong>ein</strong> Architekturvorschlag gegeben und<br />
<strong>ein</strong>e Referenzimplementierung erstellt (Kapitel 6). Schließlich wird die Verwendung der Implementierung<br />
beschrieben (Kapitel 7) sowie die Ergebnisse der Arbeit zusammengefasst und <strong>ein</strong><br />
Ausblick <strong>für</strong> Erweiterungen und weitergehende Untersuchungen geboten (Kapitel 8).<br />
2 <strong>Jens</strong> <strong>Pfau</strong> · Stephan Mehlhase
Kapitel 2<br />
Motivation<br />
Man muß das Unmögliche versuchen,<br />
um das Mögliche zu erreichen.<br />
Hermann Hesse<br />
Die Softwareentwicklung hat seit ihrem Beginn viele <strong>Entwicklung</strong>sschritte durchwandert. Die<br />
prägnantesten waren wohl die, die den Wechsel von <strong>ein</strong>em Programmierparadigma zum nächsten<br />
<strong>ein</strong>läuteten. Mit wachsenden Anforderungen entstanden zunächst die prozedurale und dann die<br />
objektorientierte Programmierung, deren Vorteile in der Erweiterbarkeit und Wiederverwendbarkeit<br />
bestehender Komponenten liegen. Doch erst mit der Erforschung der sich neu bietenden<br />
Möglichkeiten und dem Entwurf von allgem<strong>ein</strong>gültigen <strong>Entwicklung</strong>smethoden konnten diese in<br />
Zeit- und Kostenersparnisse umgemünzt werden. Die derzeitige Krönung dieser Evolution sind<br />
Frameworks, die den Entwicklern von Softwaresystemen wiederkehrende Aufgaben abnehmen,<br />
indem sie Komponenten und deren Schnittstellen vordefinieren.<br />
Die Spieleentwicklung hingegen, deren Produkte ebenfalls Softwaresysteme sind, hat noch nicht<br />
vollständig den Übergang zur objektorientierten Programmierung gemeistert. Doch die Anforderungen,<br />
die die Kunden und Vertreiber an die Entwickler stellen, wachsen stetig. Vor allem<br />
werden kurze <strong>Entwicklung</strong>szeiten gefordert, damit <strong>ein</strong> Produkt zum Zeitpunkt s<strong>ein</strong>er Veröffentlichung<br />
möglichst auf dem aktuellen Stand der Technik ist. Deshalb bedarf es in dieser immer<br />
bedeutender werdenden Sparte der Softwareentwicklung der Erforschung allgem<strong>ein</strong> anwendbarer<br />
Methoden, um den Entwurf von Spielen zu optimieren, das heißt, den Aufwand an Zeit und<br />
damit auch Kosten zu senken. Erste wissenschaftliche Arbeiten und Frameworkentwicklungen<br />
stoßen in diese Richtung vor.<br />
Zentral <strong>für</strong> <strong>ein</strong> Spiel sind offensichtlich die Objekte, mit denen der Benutzer über Ein- und<br />
Ausgabe interagiert. Deren virtuelle Repräsentation und Wechselbeziehungen sind jedoch komplexer,<br />
als es auf den ersten Blick ersch<strong>ein</strong>t. Um den unterschiedlichen Subsystemen <strong><strong>ein</strong>es</strong> Spiels<br />
diese Entitäten in geeigneter Weise zur Verfügung zu stellen, braucht es <strong>ein</strong>er durchdachten<br />
Verwaltung derselben.<br />
Das Ziel dieser Arbeit ist, <strong>ein</strong> solches Objektmodell als eigenständige Komponente <strong><strong>ein</strong>es</strong> Frameworks<br />
zu entwickeln, das die <strong>Entwicklung</strong> von Multiplayerspielen unterschiedlichster Architektur<br />
unterstützen soll. Eben diese Flexibilität des Einsatzes ist <strong>ein</strong> bedeutendes Charakteristikum<br />
von Frameworks. Natürlich muss auch das Objektmodell diesen Punkt berücksichtigen.<br />
Darüberhinaus soll mit <strong>ein</strong>er leichten Erweiter- und Wiederverwendbarkeit auch unabhängig<br />
von dem genannten Framework gezeigt werden, dass die wiederholte <strong>Entwicklung</strong> <strong>ein</strong>er solchen
4 II. Motivation<br />
Komponente <strong>für</strong> jedes neue Projekt nicht unbedingt notwendig ist und damit durch die Wiederverwendung<br />
<strong><strong>ein</strong>es</strong> <strong>flexiblen</strong> bestehenden <strong>Objektmodells</strong> Ressourcen gespart werden können.<br />
An <strong>ein</strong>em Spieleprojekt arbeiten heute nicht mehr nur Programmierer sondern auch Grafiker,<br />
Musiker und Angehörige vieler weiterer Berufsgruppen. Damit der Inhalt <strong><strong>ein</strong>es</strong> Spiels nicht erst<br />
entwickelt werden kann, nachdem die Programmierer ihre Aufgabe erfüllt haben, bedarf es der<br />
Entkopplung von Quelltext und Spielinhalt. Dadurch lässt sich zum <strong>ein</strong>en die Produktionszeit<br />
weiter deutlich kürzen, indem beide Teile parallel entworfen werden. Zum anderen erlaubt dies<br />
den Austausch von Spielinhalt sowohl zur <strong>Entwicklung</strong>s- als auch Laufzeit unabhängig von<br />
dem darunter liegenden Softwaresystem. Das erleichtert sowohl das Testen des entwickelten<br />
Spielinhalts als auch das Anpassen desselben während des Spielverlaufs, was vor allem bei<br />
Onlinespielen von Interesse s<strong>ein</strong> kann, bei denen Wartungsarbeiten mit Unannehmlichkeiten<br />
<strong>für</strong> den Kunden verbunden sind. Deshalb sollte das Objektmodell, welches eben genau den<br />
genannten Spielinhalt repräsentiert, dieses in flexibler Weise unterstützen.<br />
4 <strong>Jens</strong> <strong>Pfau</strong> · Stephan Mehlhase
Kapitel 3<br />
Taxonomie<br />
Das letzte Ziel aller<br />
wissenschaftlichen Erkenntnis besteht<br />
darin, das größtmögliche<br />
Tatsachengebiet aus der<br />
kl<strong>ein</strong>stmöglichen Anzahl von<br />
Axiomen und Hypothesen zu erhellen.<br />
Albert Einst<strong>ein</strong><br />
In diesem Kapitel soll zunächst versucht werden, <strong>ein</strong>e Kategorisierung von Objektmodellen im<br />
Allgem<strong>ein</strong>en und letztlich vor allem <strong>für</strong> Spiele im Speziellen aufzustellen. Eine solche Taxonomie<br />
kann zum <strong>ein</strong>en hilfreich s<strong>ein</strong>, Systeme nach definierten Aspekten <strong>ein</strong>zuordnen, zum anderen<br />
soll sie dem Leser vor allem <strong>ein</strong>en Überblick über die Vielfalt der Problemstellung und <strong>ein</strong><br />
klares Bild von dem Begriff „Objektmodell“ bieten sowie die unterschiedlichen Möglichkeiten<br />
der Implementierung <strong><strong>ein</strong>es</strong> solchen aufzeigen. Dabei soll vor allem von der <strong>ein</strong>dimensionalen<br />
Differenzierung derartiger Systeme nach ihrem Grad der Komponenten- oder Objektorientierung<br />
Abstand genommen werden [Doherty, 2003] und <strong>ein</strong>e breitere Betrachtung durchgeführt werden.<br />
Die hier<strong>für</strong> benötigten Erkenntnisse ergehen aus der spärlichen Literatur zu diesem Thema sowie<br />
den Architekturen von Opensource Spieleprojekten. Exemplare beider Gruppen erfahren dann<br />
im späteren Verlauf ihre Einordnung anhand der aufgestellten Klassifizierung.<br />
Die zu erarbeitende Kategorisierung wird uns im weiteren Verlauf der Arbeit begleiten und<br />
vor allem Alternativen <strong>für</strong> grundlegende Architekturentscheidungen aufzeigen. Zum jetzigen<br />
Zeitpunkt ist uns k<strong>ein</strong>e vergleichbare Untersuchung bekannt.<br />
Wir beschränken uns hierbei nicht nur auf die Repräsentation der Spielobjekte an sich, sondern<br />
wollen auch die peripheren Gesichtspunkte <strong>ein</strong>beziehen, deren Funktionalität, Implementierung<br />
und Effizienz von eben jenem Kern abhängig sind. Dazu zählen wir zum <strong>ein</strong>en die Kommunikation<br />
zum Datenabgleich zweier teilnehmender Netzwerkknoten sowie zum anderen die Frage,<br />
inwiefern <strong>ein</strong> System zur Laufzeit dynamische Eigenschaften aufweist. Letzteres bestimmt vor<br />
allem den Grad der Flexibilität und die Einsatzmöglichkeiten desselben.
6 III. Taxonomie<br />
3.1 Objektstruktur<br />
Zentral <strong>für</strong> <strong>ein</strong> Objektmodell ist selbstverständlich die Art der Repräsentation der Spielobjekte<br />
sowie ihrer Daten in der Implementation. Nicht erst seit der objektorientierten Programmierung<br />
gesellen sich hierzu die Typisierung der Objekte sowie die auf diesen auszuführenden Operationen.<br />
Diese drei Faktoren und die Möglichkeiten zur Anordnung der Objekte in Relation<br />
zu<strong>ein</strong>ander werden im Folgenden beleuchtet.<br />
Offensichtlich bietet es sich heutzutage an, die objektorientierte <strong>Entwicklung</strong> heranzuziehen,<br />
um <strong>ein</strong>e Abbildung der genannten Aspekte auf äquivalente Elemente der Programmiersprache<br />
vornehmen zu können. Es sei jedoch angemerkt, dass dies k<strong>ein</strong> notwendiger Schritt ist. Eine<br />
Modellierung der Vorstellung, die wir von Objekten haben, ist auch auf andere Weise möglich,<br />
wie im Verlauf dieses Kapitels noch deutlich werden wird. Dennoch liegt der Fokus eben auf jener<br />
Ausprägung der Programmierung, da deren Möglichkeiten die Modellierung <strong><strong>ein</strong>es</strong> auf Objekten<br />
und deren Beziehungen basierenden Systems zum jetzigen Zeitpunkt am besten unterstützen.<br />
Da<strong>für</strong> spricht die breite Akzeptanz dieses Paradigmas in der Softwareentwicklung allgem<strong>ein</strong>,<br />
aber auch in der Spieleentwicklung im Speziellen.<br />
Folgende Aspekte werden deshalb betrachtet:<br />
• Die Modellierung der Daten von Spielobjekten<br />
• Die Modellierung des Verhaltens von Spielobjekten<br />
• Die Modellierung der Typisierung oder Klassifizierung von Spielobjekten<br />
• Die Anordnung der Spielobjekte in Relation zu<strong>ein</strong>ander<br />
3.1.1 Daten<br />
Die Basis jedes Objekts sowohl in s<strong>ein</strong>er physikalischen oder virtuellen Form als auch s<strong>ein</strong>er<br />
Repräsentation in <strong>ein</strong>er Programmiersprache sind zweifelsohne die ihm eigenen Daten, s<strong>ein</strong>e<br />
Eigenschaften. Hierbei interessieren vorrangig folgende Fragen:<br />
• Wie werden die Daten der Spielobjekte im Quelltext modelliert?<br />
• Wo werden die Daten der Spielobjekte definiert?<br />
In beide Punkte spielt die Idee des data-driven Design her<strong>ein</strong>. Deshalb soll hier kurz <strong>ein</strong>e Definition<br />
und Motivation der Methode sowie deren Vorteile gegenüber traditionellen Vorgehensweisen<br />
gegeben werden.<br />
Computerspiele unterliegen während ihrer <strong>Entwicklung</strong> und zumeist auch darüber hinaus stetigen<br />
Änderungen. Diese beschränken sich üblicherweise nicht auf Anpassungen der visuellen<br />
Elemente, sondern beziehen auch Arbeiten an der Logik des Spiels und Eigenschaften <strong>ein</strong>zelner<br />
6 <strong>Jens</strong> <strong>Pfau</strong> · Stephan Mehlhase
III. Taxonomie 7<br />
Spielobjekte <strong>ein</strong>. Dabei werden solche Anpassungen von Personen durchgeführt, die nicht notwendigerweise<br />
auch Programmierer sind und dementsprechend nur beschränkte Möglichkeiten<br />
haben, Arbeiten am Quelltext <strong><strong>ein</strong>es</strong> Spiels vorzunehmen.<br />
Um diese Adaptionen zu ver<strong>ein</strong>fachen, stützt sich die Spiel<strong>ein</strong>dustrie daher auf die Idee, dass<br />
Daten über das Verhalten der Spiele von diesen aus externen Datenquellen <strong>ein</strong>gelesen und zur<br />
Anwendung gebracht werden [Bilas, 2002; Church, 2002; Doherty, 2003; Duran, 2003; Rabin,<br />
2000; Riley, 2003], anstatt dass jede spielmechanische Information in <strong>ein</strong>em Teil des Quelltextes<br />
festgehalten ist. Das bietet folgende Vorteile [Riley, 2003]:<br />
• Spieldesigner können ohne Programmierkenntnisse Spielinhalt be<strong>ein</strong>flussen.<br />
• Jede Änderung am Spielinhalt kann sofort ohne Neukompilierung des Systems evaluiert<br />
werden.<br />
• Es braucht weniger Quelltext, was wiederum das Risiko von Fehlern minimiert und die<br />
Wartung erleichtert.<br />
• Die Spielentwicklung wird beschleunigt, da der Inhalt <strong><strong>ein</strong>es</strong> Spiels bereits unabhängig vom<br />
eigentlichen Quelltext entworfen werden kann.<br />
• Es können externe Applikationen genutzt werden, die die Kreation von Spielinhalt von<br />
der Repräsentation der manipulierten Daten abstrahieren und somit ver<strong>ein</strong>fachen.<br />
• Die Wiederverwendbarkeit des Systems wird unterstützt, da die eigentliche Logik des<br />
Spiels nicht im Quelltext kodiert ist.<br />
• Es bietet sich damit an, Kunden oder Anwendern die Möglichkeit zu geben, das Spiel nach<br />
ihren Vorstellungen zu verändern (Modding).<br />
Die Nachteile dieses Konzepts werden in Abhängigkeit der genauen Implementierung in den<br />
nächsten Abschnitten erläutert. Die traditionelle Herangehensweise, bei der Daten der Spielobjekte<br />
im Quelltext definiert werden, wird ebenfalls betrachtet. Wie wir sehen werden, hat diese<br />
Methode ebenso ihre Vorteile.<br />
3.1.1.1 Modellierung<br />
In den folgenden Abschnitten werden Methoden vorgestellt, wie die zu <strong>ein</strong>em Spielobjekt gehörenden<br />
Daten mit demselben verknüpft werden können. Die Möglichkeiten reichen von <strong>ein</strong>er<br />
streng objektorientierten Modellierung mit Hilfe von Konstrukten der <strong>ein</strong>gesetzten Programmiersprache<br />
bis hin zu <strong>ein</strong>em System, welches sich nicht mehr auf dieses Paradigma stützt,<br />
sondern dieses emuliert.<br />
<strong>Jens</strong> <strong>Pfau</strong> · Stephan Mehlhase 7
8 III. Taxonomie<br />
Objekte als Sprachkonstrukte<br />
In der objektorientierten Programmierung werden die potentiellen Eigenschaften von Objekten<br />
über ihre Klassen definiert. Dadurch sind diese Teil des Quelltextes. Mit Hilfe dieser naheliegenden<br />
Methode können natürlich auch Spielobjekte in der Form modelliert werden, dass <strong>für</strong><br />
jeden Typ von Spielobjekt <strong>ein</strong>e Klasse definiert wird, von der dann wiederum Instanzen kreiert<br />
werden können, die sich schließlich in den Werten ihrer Eigenschaften unterscheiden.<br />
Vorteile [Riley, 2003; Duran, 2003]:<br />
• Es wird weniger Speicher benötigt, da gem<strong>ein</strong>same Attribute von Spielobjekten auf die<br />
Klassenebene verlagert werden können.<br />
• Typüberprüfungen <strong>für</strong> die Belegungen der Eigenschaften können auf der Sprachebene in<br />
effizienter Form durchgeführt werden.<br />
• Typen von Objekten können durch ihre Klasse identifiziert werden.<br />
• Die Modellierung erfolgt nach <strong>ein</strong>em gutverstandenen Prinzip.<br />
Nachteile:<br />
• Es entsteht <strong>ein</strong>e große Menge Quelltext und somit <strong>ein</strong> höheres Fehlerrisiko sowie <strong>ein</strong>e<br />
komplexere Wartung des Systems.<br />
• Es ist schwierig, nur Teile des Spiels zu laden, da alles fest kompiliert ist.<br />
• Für jede kl<strong>ein</strong>e Änderung muss der Quelltext neu kompiliert werden.<br />
• Folglich ist es kompliziert, das Spiel zur Laufzeit zu ändern.<br />
• Typen von Objekten können nur durch ihre Klasse identifiziert werden.<br />
• Der übersetzte Quelltext ist nicht unbedingt von Menschen lesbar.<br />
Dynamische Attribute<br />
Bei dieser Architektur existiert nur <strong>ein</strong>e Klasse, die als <strong>ein</strong>zige Variable <strong>ein</strong>e leere Hashtable<br />
definiert [Riley, 2003]. Die Instanzen dieser Klasse füllen dann jeweils diese Hashtable mit Zuordnungen<br />
von Schlüsselwörtern zu Werten, wobei die Werte nicht typisiert sind und die Typen<br />
folglich zur Laufzeit bestimmt werden müssen. Jede Eigenschaft <strong><strong>ein</strong>es</strong> Objekts kann somit über<br />
das entsprechende Schlüsselwort abgefragt werden.<br />
Vorteile:<br />
• Es wird nur <strong>ein</strong>e <strong>ein</strong>zige Klasse benötigt.<br />
8 <strong>Jens</strong> <strong>Pfau</strong> · Stephan Mehlhase
III. Taxonomie 9<br />
• Das System ist sehr flexibel, da jede Instanz unterschiedlich s<strong>ein</strong> kann.<br />
• Die Eigenschaften jeder <strong>ein</strong>zelnen Instanz können zur Laufzeit geändert werden.<br />
Nachteile:<br />
• Es wird mehr Speicher als bei anderen Verfahren benötigt, da alle Daten auf der Instanzebene<br />
und k<strong>ein</strong>e auf der Klassenebene gehalten werden.<br />
• Es entstehen zusätzliche Laufzeitkosten dadurch, dass die Typen der Attribute zur Laufzeit<br />
bestimmt und festgelegt werden müssen.<br />
• Es kann schwierig s<strong>ein</strong>, den wirklichen Typ <strong><strong>ein</strong>es</strong> Objektes herauszufinden.<br />
• Das System ist durch die fehlende Typisierung fehleranfälliger.<br />
Dynamische Zustände<br />
Der Name dieses Modells ist von uns in Anlehnung an obiges System und [Hannah, 2004]<br />
gewählt worden. Tatsächlich unterscheidet sich dieses nur in <strong>ein</strong>em Detail von den dynamischen<br />
Attributen: Die Eigenschaften sind nun typisiert. Die Vorteile bleiben unverändert, die Nachteile<br />
hingegen reduzieren sich damit auf folgende:<br />
• Es wird mehr Speicher als bei anderen Verfahren benötigt, da alle Daten auf der Instanzebene<br />
und k<strong>ein</strong>e auf der Klassenebene gehalten werden.<br />
• Es kann schwierig s<strong>ein</strong>, den wirklichen Typ <strong><strong>ein</strong>es</strong> Objektes herauszufinden.<br />
Komponentenmodell<br />
In den Spielen Thief und Deus Ex des ehemaligen Entwicklers Ion Storm 1 kommt <strong>ein</strong> alternatives<br />
System zum Einsatz, welches sich nicht mehr auf die Konstrukte der objektorientierten<br />
<strong>Entwicklung</strong> stützt [Church, 2002; Duran, 2003]. Stattdessen basiert dieses auf sogenannten<br />
Komponenten, die lediglich Container <strong>für</strong> Paare aus Objekt-IDs und Eigenschaftswerten sind.<br />
Um zum Beispiel die Farbe <strong><strong>ein</strong>es</strong> Objektes zu bestimmen, wird die <strong>für</strong> die Farb-Eigenschaft<br />
zuständige Komponente nach der Farbe <strong>für</strong> die ID des relevanten Objektes befragt.<br />
Vorteile [Doherty, 2003; Church, 2002; Duran, 2003]:<br />
• Jede Komponente kann ihre Daten in <strong>ein</strong>er Struktur halten, deren Mechanismen <strong>für</strong> die<br />
zugreifenden Subsysteme des Spiels effizient sind. Die <strong>für</strong> die Physikengine interessanten<br />
Daten könnten zum Beispiel in <strong>ein</strong>em Container gehalten werden, über dessen Elemente<br />
effizient iteriert werden kann.<br />
1 http://de.wikipedia.org/wiki/Ion_Storm<br />
<strong>Jens</strong> <strong>Pfau</strong> · Stephan Mehlhase 9
10 III. Taxonomie<br />
• Das System verleiht dem data-driven Design Nachdruck. Objekte sind nichts anderes als<br />
Daten und ihr Verhalten muss daher in Form von Daten definiert s<strong>ein</strong>.<br />
• Die Struktur der Komponenten als Vektoren von Daten unterstützt den Einsatz von relationalen<br />
Datenbanken <strong>für</strong> die Persistenz.<br />
Nachteile<br />
• Es ist aufwendig, Objekte aus dem Modell zu entfernen, da in jeder Komponente <strong>ein</strong><br />
entsprechender Eintrag <strong>für</strong> das relevante Objekt gesucht und dann gelöscht werden muss.<br />
• Die Elemente der objektorientierten Programmierung können nicht direkt umgesetzt werden,<br />
da Komponenten orthogonal zu dem Prinzip der Objekte umgesetzt sind. Das heißt,<br />
dass das Implementieren von Typisierung, Vererbung, etc. in <strong>ein</strong>er objektorientierten Programmiersprache<br />
weiteren Aufwands bedarf.<br />
• Zudem bedingt dies <strong>ein</strong>e steilere Lernkurve <strong>für</strong> Mitarbeiter, die mit diesem Verfahren noch<br />
nicht vertraut sind.<br />
• Das gesamte Modell basiert auf dem System der Komponenten. Änderungen an diesem<br />
be<strong>ein</strong>flussen große Teile des Quelltextes.<br />
3.1.1.2 Definition<br />
Offensichtlich müssen die Daten, die zusammen mit dem Verhalten die Spielobjekte ausmachen,<br />
an <strong>ein</strong>em Punkt definiert werden. Die zentrale Frage, die im Folgenden behandelt wird, ist, wo<br />
dies geschieht. Der Ansatz des data-driven Design zeigt hier Möglichkeiten auf, die über die<br />
traditionelle Methode der sogenannten Hartkodierung der Eigenschaften der Spielobjekte im<br />
Quelltext hinausgehen. Wir werden hier jedoch zunächst den althergebrachten Weg betrachten,<br />
um danach auf die Vorteile unterschiedlicher Ausprägungen des data-driven Design <strong>ein</strong>zugehen.<br />
Dabei hat die Entscheidung über die Art der Modellierung der Daten Einfluss auf die<br />
Implementierung dieses Aspekts.<br />
Definition im Quelltext<br />
Sollen die Eigenschaften von Objekten im Quelltext definiert werden, geschieht dies entweder<br />
über das Definieren von Klassenvariablen oder über <strong>ein</strong> entsprechendes Setzen der Variablen<br />
<strong>ein</strong>er Instanz zum Beispiel über den Konstruktor. Wir gehen davon aus, dass auch die Werte<br />
der Daten fest im Quelltext stehen.<br />
Dann ergeben sich folgende Vorteile:<br />
• Diese Methode ist effizient, da alles hartkodiert ist und k<strong>ein</strong>e weiteren Verwaltungsarbeiten<br />
zum Bestimmen und Setzen der Werte der Variablen anfallen.<br />
• Es kann zur Laufzeit absolute Typsicherheit <strong>für</strong> die Eigenschaften beziehungsweise Variablen<br />
garantiert werden.<br />
10 <strong>Jens</strong> <strong>Pfau</strong> · Stephan Mehlhase
III. Taxonomie 11<br />
Nachteile:<br />
• Das System ist sehr unflexibel und kaum wartbar.<br />
• Jede Datenänderung bedingt <strong>ein</strong> komplettes Neukompilieren.<br />
• Das System ist schlecht wiederverwendbar, da alle spielspezifischen Elemente im Quelltext<br />
verankert sind.<br />
Definition in Textdateien<br />
Dies ist die erste <strong>ein</strong>er Reihe von Methoden, die auf dem Ansatz des data-driven Design basieren.<br />
Die Daten <strong>für</strong> die Spielobjekte werden vom Spiel aus <strong>ein</strong>fachen Textdateien <strong>ein</strong>gelesen.<br />
Vorteile [Riley, 2003]:<br />
• Textdateien sind von Menschen lesbar und daher <strong>ein</strong>fach zu manipulieren.<br />
• Es entsteht kaum Overhead, da die Daten nur weniger Steuerzeichen bedürfen.<br />
Nachteile:<br />
• Das Verwalten von vielen Textdateien kann sehr mühsam s<strong>ein</strong><br />
• Textdateien an sich können nicht die Integrität ihrer Daten garantieren. Dies muss all<strong>ein</strong>e<br />
vom Programm bewerkstelligt werden.<br />
Definition in Binärdateien<br />
In Binärdateien sind Daten nicht im Klartext abgelegt, sondern in <strong>ein</strong>em dem jeweiligen Programm<br />
eigenen binären Format.<br />
Vorteile:<br />
• Es entsteht kaum Overhead, da die Daten nur weniger Steuerzeichen bedürfen.<br />
Nachteile:<br />
• Die Dateien sind nicht direkt von Menschen lesbar und daher schlecht zu manipulieren.<br />
• Die Integrität der Daten kann lediglich durch das Programm selber sichergestellt werden.<br />
<strong>Jens</strong> <strong>Pfau</strong> · Stephan Mehlhase 11
12 III. Taxonomie<br />
Definition in XML Dateien<br />
XML soll als Standard <strong>für</strong> das Ablegen strukturierter Daten dienen. Da es mittlerweile in vielen<br />
Bereichen Anwendung findet, gibt es <strong>ein</strong>e Vielzahl an Werkzeugen und Bibliotheken <strong>für</strong> die<br />
gängigen Programmiersprachen zur Manipulation von XML Dateien.<br />
Vorteile [Riley, 2003]:<br />
• Zur Manipulation von XML Dateien stehen ausgereifte Werkzeuge zur Verfügung.<br />
• XML als Technologie ist direkt abgestimmt auf die Ablage strukturierter Daten.<br />
• Die Struktur der XML Dateien kann sehr flexibel gestaltet werden.<br />
• XML bietet inhärent die Unterstützung zur Sicherstellung der Integrität der Daten.<br />
Nachteile:<br />
• XML Dateien haben <strong>ein</strong>en hohen Bedarf an Speicherplatz <strong>für</strong> Verwaltungsdaten.<br />
[Riley, 2003] spricht von <strong>ein</strong>er schlechten Werkzeugunterstützung <strong>für</strong> XML, wohingegen wir<br />
jedoch anderer M<strong>ein</strong>ung sind. Dies lässt sich aber auch darauf zurückführen, dass der Artikel<br />
bereits drei Jahre alt ist und die Situation sich seitdem geändert hat. Mittlerweile sind zum<br />
Beispiel sogar zwei Bibliotheken zur Manipulation von XML Dateien im Java Development<br />
Kit 2 unabhängig von<strong>ein</strong>ander integriert.<br />
Definition in <strong>ein</strong>er Skriptsprache<br />
Die Option, die Daten der Spielobjekte in <strong>ein</strong>er Skriptsprache zu definieren, ist sicherlich interessant<br />
<strong>für</strong> die Systeme, deren Verhalten ebenfalls in <strong>ein</strong>er Skriptsprache beschrieben ist. Auf<br />
diesem Weg lassen sich zwei Probleme über die selbe Schnittstelle lösen. Bekannte Skriptsprachen<br />
<strong>für</strong> Spiele sind Python 3 , Tcl 4 und insbesondere Lua 5 .<br />
Vorteile [Riley, 2003]:<br />
• Die Methode lässt sich gut in das System integrieren, wenn das Verhalten auch in derselben<br />
Skriptsprache modelliert wird.<br />
• Es muss k<strong>ein</strong> eigener Parser geschrieben werden.<br />
• Skriptsprachen sind sehr dazu geeignet, komplexe Daten zu beschreiben.<br />
• Die Daten können auch von der Skriptsprache selber sehr <strong>ein</strong>fach manipuliert werden.<br />
2 http://java.sun.com<br />
3 http://www.python.org<br />
4 http://www.tcl.tk<br />
5 http://www.lua.org<br />
12 <strong>Jens</strong> <strong>Pfau</strong> · Stephan Mehlhase
III. Taxonomie 13<br />
Nachteile:<br />
• Wenn k<strong>ein</strong>e Werkzeuge <strong>für</strong> die Spieldesigner bereitgestellt werden, bedarf es deren Einarbeitung<br />
in die Skriptsprache.<br />
Definition in <strong>ein</strong>er Relationalen Datenbank<br />
Relationale Datenbanken stellen trotz des Siegeszuges der Objektorientierung in der Programmierung<br />
immer noch den bedeutendsten Anteil an Persistenzsystemen <strong>für</strong> große Datenmengen.<br />
Vorteile [Riley, 2003]:<br />
• Relationale Datenbanken haben weithin unterstützte Standardschnittstellen.<br />
• Sie bieten ausgezeichnete Fähigkeiten zur Handhabung von konkurrierenden Zugriffen.<br />
• Sie bieten von Haus aus gute Möglichkeiten <strong>für</strong> Backups.<br />
• Sie können problemlos mit sehr großen Datenmengen umgehen.<br />
• Sie bieten standardisierte und potente Möglichkeiten zur Datenabfrage und -manipulation<br />
(SQL).<br />
• Es existieren mächtige Werkzeuge zur Administration und Wartung von Datenbanken<br />
sowie zur Datenmanipulation.<br />
Nachteile:<br />
• Relationale Datenbanken können sehr wartungsintensiv s<strong>ein</strong>.<br />
• Es werden möglicherweise produktspezifische Bibliotheken, Werkzeuge und Lizenzen gebraucht.<br />
• Die Daten, die in relationalen Datenbanken gespeichert werden können, unterliegen produktspezifischen<br />
Einschränkungen. Dies kann die Austauschbarkeit der Datenbank in <strong>ein</strong>em<br />
System unterbinden, das gegen <strong>ein</strong>e bestimmte Spezifikation entwickelt wurde.<br />
• Die objektorientierten Daten <strong><strong>ein</strong>es</strong> <strong>Objektmodells</strong> sind nicht unbedingt <strong>ein</strong>fach auf <strong>ein</strong><br />
relationales Schema abzubilden.<br />
• Auf lokalen Clients oder in Singleplayer-Spielen bedeuten Datenbanken, wenn hauptsächlich<br />
statischer Inhalt ausgeliefert wird, wohlmöglich <strong>ein</strong>en unnötig großen Aufwand an<br />
Ressourcen.<br />
<strong>Jens</strong> <strong>Pfau</strong> · Stephan Mehlhase 13
14 III. Taxonomie<br />
Definition in <strong>ein</strong>er objektorientierten Datenbank<br />
Objektorientierte Datenbanken haben sich kaum durchgesetzt. Sie seien hier jedoch der Vollständigkeit<br />
halber erwähnt. Dabei bringen sie in diesem Kontext die gleichen oben genannten<br />
Vor- und Nachteile mit sich wie die relationalen Datenbanken, mit der Ausnahme, dass es leichter<br />
fällt, die Daten objektorientierter Systeme auf Schemata dieser Datenbanken abzubilden.<br />
3.1.2 Verhalten<br />
Erst ihre Aktivität und Reaktivität erweckt die Spielobjekte zum Leben. Die Modellierung dieses<br />
Aspektes ist abhängig von der Wahl der Objektrepräsentation, wie sie in dem vorigen Abschnitt<br />
vorgestellt wurde. Im Folgenden soll aufgezeigt werden, welche Möglichkeiten sich bieten, die<br />
Beziehung zwischen Spielobjekten und ihrem Verhalten herzustellen. Es interessieren wiederum<br />
folgende Fragen:<br />
• Wie wird das Verhalten der Spielobjekte im Quelltext modelliert?<br />
• Wo wird das Verhalten der Spielobjekte definiert?<br />
3.1.2.1 Modellierung<br />
Es soll hier um die Frage gehen, in welcher Form das Verhalten programmiertechnisch mit den<br />
Spielobjekten in Verbindung gebracht wird. Erst in dem nächsten Abschnitt interessiert, wo das<br />
Verhalten an sich definiert wird. Drei Ansätze konnten ausgemacht werden, die im Folgenden<br />
vorgestellt werden.<br />
als Methoden<br />
Ein zentrales Prinzip der objektorientierten <strong>Entwicklung</strong> ist, Daten und das zugehörige Verhalten<br />
an <strong>ein</strong>em Punkt zusammenzubringen. So können <strong>für</strong> <strong>ein</strong>e Klasse und damit auch <strong>für</strong> deren<br />
Instanzen Methoden definiert werden, die dann mit den objekteigenen Daten arbeiten. Auf diese<br />
Weise die Logik <strong><strong>ein</strong>es</strong> Spiels an die Spielobjekte zu knüpfen, liegt dann nahe, wenn letztere als<br />
Instanzen von Klassen <strong>ein</strong>er objektorientierten Programmiersprache modelliert sind.<br />
Vorteile:<br />
• Dies ist <strong>ein</strong>e naheliegende und somit gut verständliche Umsetzung nach dem Paradigma<br />
der verwendeten Programmiersprache, sofern objektorientiert.<br />
• Das Verfahren ist effizient, da das Verhalten direkt an die Daten geknüpft ist, die es nutzt<br />
und manipuliert.<br />
14 <strong>Jens</strong> <strong>Pfau</strong> · Stephan Mehlhase
III. Taxonomie 15<br />
Nachteile:<br />
• Das Verhalten ist fest verknüpft mit den Spielobjekten. Dieses kann während der <strong>Entwicklung</strong>szeit<br />
und nach der Auslieferung des Produktes nur schlecht ausgetauscht werden.<br />
als Komponenten<br />
Spiele befinden sich in ständiger Veränderung. Auch das Verhalten von Spielobjekten wird zumindest<br />
während der <strong>Entwicklung</strong>sphase <strong><strong>ein</strong>es</strong> Spiels häufig umdefiniert. Diesem Umstand trägt<br />
das Komponentenmodell Rechnung [Bilas, 2002]. Spielobjekte bestehen nicht nur aus Daten, sondern<br />
auch aus sogenannten Komponenten, die <strong>ein</strong> in sich abgeschlossenes Verhalten definieren,<br />
zum Beispiel in Form von weiteren Objekten, welche sowohl <strong>ein</strong>en Zustand als auch Methoden<br />
anbieten können. Jedem Spielobjekt kann <strong>ein</strong>e beliebige Menge an Komponenten zugeordnet<br />
werden, die natürlich auch noch zur Laufzeit geändert werden kann. Das Modell kann vor allem<br />
in der Granularität der Komponenten variiert werden [Duran, 2003]. Vorstellbar sind sowohl<br />
umfassende Komponenten, hinter denen möglicherweise mehrere Objekte stehen oder kl<strong>ein</strong>gewichtige,<br />
die nur <strong>ein</strong>e <strong>ein</strong>zelne ausführbare Aktion repräsentieren [Hannah, 2004]. Dieses System<br />
steht nicht in Zusammenhang mit dem in Kapitel 3.1.1.1 vorgestellten Komponentenmodell.<br />
Vorteile:<br />
• Jedes Spielobjekt kann jedes beliebige Verhalten annehmen, indem ihm die entsprechende<br />
Komponente zugeordnet wird.<br />
• Das data-driven Design wird unterstützt. Spieldesigner können mit Hilfe externer Werkzeuge<br />
Verhalten bestimmten Spielobjekten zuordnen.<br />
• Weitere Spiellogik kann hinzugefügt werden, ohne dass bestehender Quelltext neu übersetzt<br />
werden muss.<br />
Nachteile:<br />
extern<br />
• Das Auflösen von Abhängigkeiten zwischen Komponenten kann sich als schwierig herausstellen.<br />
• Dies ist k<strong>ein</strong>e direkte Modellierung mit Hilfe der Elemente der Programmiersprache und<br />
bedarf daher zusätzlichen Aufwands sowohl in der <strong>Entwicklung</strong> als auch zur Laufzeit.<br />
Als letzten Weg haben wir die Option identifiziert, das Verhalten komplett von den zugehörigen<br />
Objekten abzukoppeln. Dies kann zum Beispiel durch das Anlegen <strong>ein</strong>er Bibliothek von Funktionen<br />
geschehen, die Spielobjekte als Argumente entgegennehmen, oder durch das Entwurfsmuster<br />
Command, wodurch der objektorientierten <strong>Entwicklung</strong> Genüge geleistet wäre [Gamma, 1997].<br />
<strong>Jens</strong> <strong>Pfau</strong> · Stephan Mehlhase 15
16 III. Taxonomie<br />
Vorteile:<br />
• Jedes Spielobjekt kann jedes beliebiges Verhalten annehmen, indem es an die Verhaltenslogik<br />
übergeben wird.<br />
• Weitere Spiellogik kann hinzugefügt werden, ohne dass bestehender Quelltext neu übersetzt<br />
werden muss.<br />
Nachteile:<br />
• Dies ist k<strong>ein</strong>e direkte Modellierung mit Hilfe der Elemente der Programmiersprache und<br />
bedarf daher zusätzlichen Aufwands sowohl in der <strong>Entwicklung</strong> als auch zur Laufzeit.<br />
• Es bedarf weiterer Logik, um festzulegen, welches Spielobjekt welches Verhalten besitzt.<br />
3.1.2.2 Definition<br />
Nachdem die Möglichkeiten aufgezeigt wurden, Verhalten mit Daten in Verbindung zu bringen,<br />
soll untersucht werden, wie ersteres definiert werden kann.<br />
Quelltext<br />
Die konventionelle Methode, Verhalten festzulegen, ist, dieses im Quelltext zu beschreiben, zum<br />
Beispiel in Form von Methoden- oder Funktionskörpern.<br />
Vorteile:<br />
• Der Quelltext wird kompiliert, was die Effizienz zur Laufzeit steigert.<br />
• Ein gewisses Maß an Sicherheit durch die von der Programmiersprache bereitgestellten<br />
Überprüfungen, zum Beispiel Typüberprüfungen, ist gegeben.<br />
Nachteile:<br />
• Die Methode ist unflexibel, da jede Neudefinition von Verhalten <strong>ein</strong>e erneute Kompilierung<br />
erfordert.<br />
• Es entsteht mehr Quelltext, also auch mehr Fehlermöglichkeiten.<br />
Skriptsprache<br />
Viele moderne Spiele bieten die Möglichkeit, Verhalten von Spielobjekten in Skriptsprachen<br />
zu definieren [Rabin, 2000]. Das heißt, dass das Spiel in der Lage s<strong>ein</strong> muss, Skriptdateien<br />
16 <strong>Jens</strong> <strong>Pfau</strong> · Stephan Mehlhase
III. Taxonomie 17<br />
<strong>ein</strong>zulesen, und <strong>ein</strong> entsprechender Compiler oder Interpreter bereitgestellt werden muss. Als<br />
Skriptsprachen kommen zum Beispiel Python, Tcl oder Lua zum Einsatz, aber ebenso ist der<br />
Einsatz von höheren Sprachen wie Java denkbar [Rabin, 2000].<br />
Vorteile:<br />
• Spiellogik muss nicht mehr von den Programmierern bestimmt werden, sondern kann auch<br />
von den Spieldesignern definiert werden.<br />
• Dies ist sinnvoll, wenn auch die Daten in der gleichen Skriptsprache abgelegt werden, denn<br />
dann kann beides über <strong>ein</strong>e <strong>ein</strong>zige Schnittstelle definiert werden.<br />
• Verhalten kann auch noch zur Laufzeit nachgeladen werden, ohne dass das Projekt neu<br />
kompiliert werden muss.<br />
Nachteile:<br />
• Für den Fall, dass das Verhalten nicht kompiliert, sondern zur Laufzeit interpretiert wird,<br />
ist dies weniger effizient.<br />
• Es ist verführerisch, auch komplizierte Spiellogik in Skripte auszulagern. Dies führt jedoch<br />
zu erhöhten Anforderungen an die Skriptsprache, die wiederum durch deren Compiler oder<br />
Interpreter sowie weiteren Werkzeugen wie Debuggern unterstützt werden muss.<br />
• Spieldesigner müssen in der Handhabung der Skriptsprache geschult werden.<br />
3.1.3 Typen<br />
Sollen die Spielobjekte nach ihrem Verhalten und ihren Daten in Relation zu<strong>ein</strong>ander gestellt<br />
werden, kommt die Typisierung ins Spiel, die sich in objektorientierten Sprachen in Form von<br />
Klassen manifestiert. Diese seien im Folgenden als Typen bezeichnet und ihre Instanzen als<br />
Objekte. Wir betrachten hier folgende drei Aspekte:<br />
• Wie werden die Typen der Spielobjekte im Quelltext modelliert?<br />
• Wo werden die Typen der Spielobjekte definiert?<br />
• In welcher Form lassen sich Typen hierarchisch strukturieren, das heißt das Prinzip der<br />
Vererbung anwenden?<br />
3.1.3.1 Modellierung<br />
Zur Zeit sind zwei unterschiedliche Methoden zur Modellierung von Typen <strong>für</strong> Spielobjekte in<br />
Verwendung. Diese sollen hier vorgestellt werden. Ihre Implementierung hängt wiederum von<br />
den Entscheidungen ab, die zur Modellierung der Daten und des Verhaltens getroffen wurden.<br />
<strong>Jens</strong> <strong>Pfau</strong> · Stephan Mehlhase 17
18 III. Taxonomie<br />
Typen als Klassen<br />
Die objektorientierte Programmierung bietet an, Typen als Sprachkonstrukte zu realisieren,<br />
nämlich als sogenannte Klassen, die <strong>für</strong> ihre Instanzen sowohl <strong>ein</strong>e gem<strong>ein</strong>same Datenbasis als<br />
auch das Verhalten vordefinieren. Es bleibt offen, wie detailliert die Typen spezifiziert werden.<br />
Gibt es nur <strong>ein</strong>e <strong>ein</strong>zige Klasse, wie bei der Modellierung der Daten mit Hilfe von dynamischen<br />
Attributen oder gibt es <strong>für</strong> jeden denkbaren Typ von Spielobjekt <strong>ein</strong>e korrespondierende Klasse<br />
mit spezifischen Daten und Verhalten? Es ist auch vorstellbar, jeweils <strong>für</strong> Kategorien von Spielobjekten<br />
<strong>ein</strong>e Klasse anzulegen, die konkreten Typen und deren Instanzen jedoch weiterhin<br />
mit dynamischen Attributen oder Eigenschaften und eigenem Verhalten zu individualisieren.<br />
Letzteres findet als Kategorieobjekte Erwähnung in der Literatur [Riley, 2003].<br />
Vorteile:<br />
• Da Elemente der Programmiersprache genutzt werden, ist das Verfahren effizienter als<br />
andere.<br />
• Diese Methode kann sehr flexibel <strong>ein</strong>gesetzt werden. Der Grad der Typisierung kann <strong>für</strong><br />
verschiedene Spielobjektkategorien unterschiedlich gehalten werden.<br />
Nachteile:<br />
• Werden die Typen der Spielobjekte zu genau vorgegeben, wird das System im Fall von<br />
Änderungen schwerfällig. Dann muss möglicherweise <strong>ein</strong>e ganze Klassenhierarchie aufgebrochen<br />
werden, um neuen Anforderungen gerecht zu werden.<br />
• Auch kl<strong>ein</strong>ere Änderungen erfordern möglicherweise <strong>ein</strong>e Neukompilierung der gesamten<br />
Klassenhierarchie.<br />
Typen als Muster<br />
Basiert <strong>ein</strong> System auf Methoden des data-driven Design, ist es nicht unüblich, Typen von<br />
Spielobjekten als Muster zu definieren [Bilas, 2002]. Diese bestehen dann aus Eigenschaften<br />
und Verhalten, die in den Formen modelliert sind, wie sie in den vorigen Abschnitten vorgestellt<br />
wurden. Jedes Spielobjekt wird als Kopie <strong><strong>ein</strong>es</strong> solchen Prototyps angelegt oder hält<br />
<strong>ein</strong>en Verweis auf s<strong>ein</strong> Muster und erbt somit dessen Daten und Verhalten. Dabei ist nicht<br />
ausgeschlossen, dass <strong>ein</strong>e Instanz die Eigenschaften ihres Musters überschreibt.<br />
Vorteile:<br />
• Das System unterstützt das data-driven Design. Typen können von Spieldesignern in externen<br />
Werkzeugen angelegt werden.<br />
• Das System ist sehr flexibel. Typen können sehr unterschiedlich s<strong>ein</strong> und Instanzen können<br />
in jeder Eigenschaft verschieden von ihren Typen s<strong>ein</strong>.<br />
18 <strong>Jens</strong> <strong>Pfau</strong> · Stephan Mehlhase
III. Taxonomie 19<br />
Nachteile:<br />
• Enthalten die Spielobjekte Verweise auf ihre Muster, entsteht Overhead, wenn Eigenschaften<br />
der Objekte abgefragt werden sollen, da in dem Fall eventuell auch die Muster befragt<br />
werden müssen.<br />
• Werden die Spielobjekte aus ihren Mustern kopiert, ist es später schwierig, <strong>für</strong> <strong>ein</strong>e ganze<br />
Klasse von Objekten Eigenschaften zu ändern, denn diese haben nach ihrer Erstellung<br />
k<strong>ein</strong>e Verbindung mehr zu ihren Mustern.<br />
• Dieses System umgeht <strong>ein</strong> zentrales Prinzip der objektorientierten Programmierung, nämlich<br />
das der Klassen. Dies führt zu höherem Aufwand bei der <strong>Entwicklung</strong> und zur Laufzeit.<br />
3.1.3.2 Definition<br />
Typen können auf zweierlei Wegen definiert werden. Zusammen mit der Art der Modellierung<br />
ergeben sich vier verschiedene Modelle, von denen hier drei in der Praxis gebräuchliche erwähnt<br />
werden sollen.<br />
Definition im Quelltext<br />
Wenn Typen als Klassen modelliert werden, liegt es nahe, diese direkt im Quelltext zu definieren.<br />
Vorteile:<br />
• Dies ist effizient, da die Definition hartkodiert und somit kompiliert ist.<br />
• Es entsteht k<strong>ein</strong>e Indirektion bei der Erstellung der Klassen und somit weniger potentielle<br />
Probleme.<br />
Nachteile:<br />
• Jede Typänderung bedingt <strong>ein</strong>e Neukompilierung, wodurch die Methode unflexibel wird.<br />
Definition mit Hilfe des data-driven Design<br />
Wenn die Typen data-driven definiert und im Quelltext als Klassen modelliert werden, muss<br />
<strong>ein</strong> Werkzeug existieren, das den Quelltext <strong>für</strong> die Klassen aus ihrer Definition generiert [Riley,<br />
2003]. Die andere Option ist, Typen als Muster zu modellieren und data-driven zu definieren<br />
[Bilas, 2002]. Folgende Datenquellen, die bereits in Kapitel 3.1.1.2 vorgestellt wurden, bieten<br />
sich primär an:<br />
• Textdateien<br />
<strong>Jens</strong> <strong>Pfau</strong> · Stephan Mehlhase 19
20 III. Taxonomie<br />
• Binärdateien<br />
• XML Dateien<br />
Vorteile:<br />
• Typen können mit externen Werkzeugen durch Spieldesigner erstellt werden.<br />
• Typen können zur Laufzeit geändert und nachgeladen werden, ohne dass das gesamte<br />
System neu kompiliert werden muss.<br />
Nachteile:<br />
• Jedes Laden <strong><strong>ein</strong>es</strong> Typs erfordert das Lesen von Daten aus externen Quellen wie Dateien<br />
oder Datenbanken. Dies benötigt Ressourcen wie Speicher und Rechenzeit.<br />
3.1.3.3 Vererbung<br />
Die Typisierung von Spielobjekten ist der erste Schritt; die Vererbung von Typen zu erlauben<br />
und damit Hierarchien derselben zu ermöglichen, ist der zweite. Sind Typen als Klassen modelliert,<br />
werden alle Aspekte bereits von der Programmiersprache bereitgestellt. Wenn Typen<br />
hingegen als Muster modelliert sind, bedarf es <strong>ein</strong>er proprietären Nachbildung dieses Prinzips.<br />
Im Folgenden wird untersucht, welche Konzepte der Vererbung sich anbieten und welche Komponenten<br />
der Typen <strong>für</strong> die Vererbung geeignet sind.<br />
Konzept der Vererbung<br />
In allen objektorientierten Programmiersprachen ist die Einfachvererbung umgesetzt, bei der<br />
jede Klasse maximal <strong>ein</strong>e Vaterklasse hat. In C++ zum Beispiel wird auch Mehrfachvererbung<br />
unterstützt. Dies führt allerdings zu dem Problem von Mehrdeutigkeiten, wenn zwei Vaterklassen<br />
<strong>ein</strong>er Klasse die gleiche Variable oder Methode definieren [Stroustrup, 2000]. Es ist<br />
abzuwiegen, ob der <strong>Entwicklung</strong>s- und Verwaltungsaufwand zur Laufzeit <strong>ein</strong>e Umsetzung der<br />
Mehrfachvererbung in <strong>ein</strong>em Objektmodell rechtfertigen.<br />
Durch die Einfachvererbung entsteht in der üblichen Umsetzung, bei der jeder Typ mindestens<br />
von <strong>ein</strong>em durch das System vorgegebenen Ausgangstyp erbt, <strong>ein</strong> gewurzelter Baum. In diesem<br />
repräsentiert jeder Knoten <strong>ein</strong>en Typ und jede Kante <strong>ein</strong>en Vererbungsschritt. Die Mehrfachvererbung<br />
führt zu <strong>ein</strong>em gerichteten Graphen, der mehrere „Wurzeln“ hat und bei dem jeder<br />
Knoten mehrere Väter haben kann. Für beide Strukturen lassen sich allerdings Blätter identifizieren,<br />
die dann die Enden ihrer Vererbungslinie kennzeichnen.<br />
20 <strong>Jens</strong> <strong>Pfau</strong> · Stephan Mehlhase
III. Taxonomie 21<br />
Gegenstand der Vererbung<br />
Da wir bisher Spielobjekte als Komposition von Daten und Verhalten betrachtet haben, bieten<br />
sich diese als Gegenstände der Vererbung an; das heißt, <strong>ein</strong> Typ, der von <strong>ein</strong>em anderen<br />
erbt, übernimmt dessen Daten und/oder Verhalten. Eine Instanz dieses Typs ist dann auch<br />
gleichzeitig <strong>ein</strong>e Instanz des Vatertyps.<br />
Im Fall der Typen als Muster ist zu entscheiden, ob nur die Typen an den Blättern der Hierarchie<br />
instanziierbar s<strong>ein</strong> sollen. Damit wären zur Laufzeit die inneren Knoten des Baumes nicht mehr<br />
interessant und es wäre denkbar und sinnvoll, dann a priori von der Wurzel des Baumes an<br />
die Eigenschaften der Typen in deren Kinder zu kopieren, so dass diese bereits direkt Daten<br />
und Verhalten all ihrer Vorfahren enthielten. Die Typen, die k<strong>ein</strong>e Blätter der Hierarchie sind,<br />
könnten <strong>für</strong> den weiteren Verlauf verworfen werden, da von ihnen nach Annahme k<strong>ein</strong>e Instanzen<br />
erstellt werden könnten.<br />
Das verhindert, dass bei Zugriffen auf Eigenschaften der Objekte möglicherweise der Typ dieses<br />
Objekts sowie alle s<strong>ein</strong>e Vorfahren nach dieser Eigenschaft befragt werden müssten, um diese<br />
ausfindig zu machen. Zudem erspart dies Speicherplatz, da die inneren Knoten der Hierarchie<br />
nicht vorgehalten werden müssen; es verhindert aber auch, dass während der Laufzeit Eigenschaften<br />
der Typen an den inneren Knoten so geändert werden können, dass sich auch ihre<br />
Kinder und damit deren Instanzen ändern [Bilas, 2002]. Letzteres ist vor allem zur <strong>Entwicklung</strong><br />
interessant, wenn Typen zu Testzwecken verändert werden sollen, ohne dass das Projekt neu<br />
kompiliert werden muss.<br />
3.1.4 Objektordnung<br />
Sind die Spielobjekte erst <strong>ein</strong>mal erstellt, muss ihre Anordnung zu<strong>ein</strong>ander bestimmt werden.<br />
Hier beziehen wir uns ausschließlich auf die Komposition als Beziehung zwischen Objekten. Andere<br />
Arten von Relationen wie die Assoziation als Modellierung der Verwendung <strong><strong>ein</strong>es</strong> Objektes<br />
durch <strong>ein</strong> anderes sind zu dynamisch und situationsabhängig, als dass aus ihnen <strong>ein</strong>e Struktur<br />
gewonnen werden könnte.<br />
Jedes Subsystem <strong><strong>ein</strong>es</strong> Spiels bringt s<strong>ein</strong>e eigenen Anforderungen an den Container der Spielobjekte<br />
mit. Ein Physikmodul zum Beispiel wird nur Wert darauf legen, dass es schnell über<br />
alle aktuell im Szenegraph relevanten Objekte iterieren kann, während andere Teile der Spiellogik<br />
darauf angewiesen s<strong>ein</strong> könnten, effizient die Spielobjekte bestimmen zu können, die <strong>ein</strong>em<br />
anderen in irgend<strong>ein</strong>er logischen Relation zugeordnet sind. Diesen Ansprüchen muss das Objektsystem<br />
gerecht werden. Wir konnten drei mögliche Modellierungen ausfindig machen:<br />
• k<strong>ein</strong>e Anordnung der Objekte zu<strong>ein</strong>ander<br />
• hierarchische Anordnung der Objekte zu<strong>ein</strong>ander<br />
• Gruppierung von Objekten<br />
<strong>Jens</strong> <strong>Pfau</strong> · Stephan Mehlhase 21
22 III. Taxonomie<br />
Ersteres beschreibt <strong>ein</strong> Modell, in dem zwischen zwei Spielobjekten k<strong>ein</strong>e direkte Kompositionsbeziehung<br />
vorhanden ist.<br />
Zweiteres repräsentiert <strong>ein</strong> System, in dem Spielobjekte wiederum andere Spielobjekte enthalten<br />
können. Es entsteht <strong>ein</strong>e Baumstruktur [Hannah, 2004] oder <strong>ein</strong> Graph. Letztere Option konnten<br />
wir wahrsch<strong>ein</strong>lich wegen der <strong>ein</strong>hergehenden Komplexität nicht ausfindig machen. Ersteres<br />
hingegen bietet den Vorteil <strong>ein</strong>er zusätzlichen logischen Strukturierung, die es erlaubt, effizient<br />
<strong>für</strong> <strong>ein</strong>e Operation relevante Teilmengen ausfindig zu machen und auf diesen zu arbeiten. Dabei<br />
ist es <strong>ein</strong>fach modellierbar, dass Operationen, die auf <strong>ein</strong> Spielobjekt angewendet werden, auch<br />
<strong>für</strong> dessen Kinder gültig sind. Eine strukturierte Anordnung der Objekte bringt allerdings auch<br />
immer <strong>ein</strong>en Verwaltungsaufwand und <strong>ein</strong>en bestimmten Zugriffsmechanismus mit sich, denn<br />
auf <strong>ein</strong>e Baumstruktur wird anders zugegriffen als zum Beispiel auf <strong>ein</strong>e Liste.<br />
Mit Hilfe der Gruppierung können Teilmengen der Spielobjekte in Hinblick auf bestimmte Aufgaben<br />
zusammengefasst werden. So ist es denkbar, jeweils die zur Zeit <strong>für</strong> den Szenegraphen<br />
relevanten Objekte in <strong>ein</strong>er Gruppe zu halten oder solche Objekte zu gruppieren, die hinsichtlich<br />
der Kommunikation mit anderen Teilnehmern betreffend gleiche Eigenschaften aufweisen<br />
[Griwodz, 2002].<br />
3.2 Dynamik zur Laufzeit<br />
Eine der zentralen Fragen an <strong>ein</strong> Objektmodell ist, wie wandlungsfähig es zur Laufzeit ist. Dadurch<br />
kann sich entscheiden, ob <strong>ein</strong> System <strong>für</strong> <strong>ein</strong>en bestimmten Einsatzzweck geeignet ist oder<br />
nicht. Gerade Betreiber von Onlinespielen können es sich nur in größeren Zeitabständen leisten,<br />
ihre Server <strong>für</strong> Wartungsarbeiten herunterzufahren. In Hinblick auf die vorigen Untersuchungen<br />
interessieren folgende Fragen:<br />
• Kann das System zur Laufzeit Spielobjekte nachladen?<br />
• Kann das System zur Laufzeit Verhalten nachladen?<br />
• Kann das System zur Laufzeit Typen nachladen?<br />
• Können Typen zur Laufzeit geändert werden, so dass dies Auswirkungen auf bereits geladene<br />
Spielobjekte hat?<br />
Jedes denkbare komplexere Spiel ist auf die erste dieser Anforderungen angewiesen, es sei denn,<br />
<strong>ein</strong> komplettes Spiel wird ab s<strong>ein</strong>em Start im Speicher gehalten. Das ist aber <strong>für</strong> die wenigsten<br />
Spiele vertretbar.<br />
Gerade die drei letzten Fragen betreffen die Zeit der <strong>Entwicklung</strong> <strong><strong>ein</strong>es</strong> Spiels, in der oft Spielelemente<br />
zum Test ausgetauscht werden müssen, sowie eben jene Onlinespiele, die auch im<br />
Produktionsbetrieb die Möglichkeiten <strong>für</strong> Veränderungen offen halten müssen. Wie wir bisher<br />
gesehen haben, wird Flexibilität jedoch stets mit Kosten an Speicherplatz oder Rechenzeit erkauft.<br />
Es gilt also <strong>für</strong> jedes Produkt abzuwiegen, welche Anforderungen wirklich nötig sind.<br />
22 <strong>Jens</strong> <strong>Pfau</strong> · Stephan Mehlhase
III. Taxonomie 23<br />
3.3 Kommunikation<br />
Zunächst <strong>ein</strong>mal ersch<strong>ein</strong>t dieser Gesichtspunkt nicht relevant <strong>für</strong> die Ausarbeitung <strong><strong>ein</strong>es</strong> <strong>Objektmodells</strong>,<br />
allerdings kann letzteres im Falle des Einsatzes in <strong>ein</strong>em Multiplayerspiel durch<br />
<strong>ein</strong>e geeignete Implementierung das Design <strong>ein</strong>er performanten Netzwerkschnittstelle fördern.<br />
Und genau dies ist <strong>ein</strong>e der zentralen Anforderungen an jedes Onlinespiel. Um die Notwendigkeit<br />
der Betrachtung dieses Punktes zu veranschaulichen, werden wir die <strong>für</strong> diese Arbeit wichtigen<br />
Aspekte und ihre Auswirkungen erörtern. Diese sind im Einzelnen:<br />
• Die Frage des Startpunktes jeder Kommunikation zur Initiierung <strong><strong>ein</strong>es</strong> Datenabgleichs<br />
• Die Frage, in welcher Form Änderungen übertragen werden<br />
• Kompression des Datenverkehrs<br />
• Unterstützung der Effizienz des Datenverkehrs durch Quality of Service Dienste<br />
3.3.1 Initiative<br />
Wenn zwei Partner mit<strong>ein</strong>ander kommunizieren wollen, bedarf es <strong>ein</strong>er Methode zur Iniitierung<br />
des Austauschs neuer Informationen. In der <strong>Entwicklung</strong> verteilter Systeme existieren zu diesem<br />
Zweck prinzipiell zwei Wege [Tanenbaum u. van Steen, 2002].<br />
3.3.1.1 Polling<br />
Beim Polling fragt <strong>ein</strong> an bestimmten Informationen interessierter Client regelmäßig bei s<strong>ein</strong>em<br />
Kommunikationspartner an, ob Neuigkeiten vorliegen. Dies passiert zum Beispiel in jedem das<br />
POP-Protokoll 6 unterstützenden E-Mail Client, der den Server erst nach neuen E-Mails befragen<br />
muss, um Kenntnis von diesen zu erlangen.<br />
Der Nachteil ist offensichtlich: Es entsteht auch Kommunikationsverkehr, wenn gar k<strong>ein</strong>e neuen<br />
Informationen ausgetauscht werden müssen. Der Vorteil ist, dass k<strong>ein</strong> Verwaltungsaufwand anfällt,<br />
um zu bestimmen, welche Informationen <strong>für</strong> welchen Kommunikationspartner interessant<br />
sind, da diese explizit nach bestimmten Änderungen fragen können.<br />
3.3.1.2 Publish/Subscribe<br />
Dieses Prinzip basiert darauf, dass Teilnehmer ihre Entitäten, die Änderungen unterliegen,<br />
bekannt geben und interessierte Kommunikationsteilnehmer diese abonnieren, um neue Informationen<br />
direkt zu erhalten.<br />
6 http://de.wikipedia.org/wiki/Post_Office_Protocol<br />
<strong>Jens</strong> <strong>Pfau</strong> · Stephan Mehlhase 23
24 III. Taxonomie<br />
Zwar reduziert das den Kommunikationsverkehr, da nur Informationen versendet werden, wenn<br />
neue vorliegen, aber es benötigt <strong>ein</strong>en gewissen Aufwand zur Verwaltung der Abonnenten.<br />
3.3.2 Änderungsübermittlung<br />
Eine entscheidene Rolle spielt die Frage, welche Informationen zwischen Kommunikationspartnern<br />
übertragen werden. Für die Änderungen am Objektmodell existieren da<strong>für</strong> zwei verschiedene<br />
Möglichkeiten.<br />
3.3.2.1 Delta Updates<br />
Bei den sogenannten Delta Updates werden nur die bloßen Änderungen des Spielzustandes seit<br />
dem letzten Abgleich übertragen. In Bezug auf die Objektstruktur können das nur die Spielobjekte<br />
s<strong>ein</strong>, die sich geändert haben, in Bezug auf Spielobjekte sind dies nur die Felder des<br />
Objektes, die sich tatsächlich verändert haben. Bei dieser Methode werden weniger Informationen<br />
übertragen als dies bei <strong>ein</strong>er Übermittlung des gesamten Objektbestandes respektive<br />
kompletter Objekte der Fall wäre. Auf der anderen Seite benötigt <strong>ein</strong> solcher Mechanismus Rechenzeit,<br />
um zu evaluieren, welche Felder geändert wurden, sowie <strong>für</strong> die Logik zum Erstellen<br />
und Anwenden der Updates [Ludwig, 2005].<br />
3.3.2.2 Aktionen<br />
Eine andere Möglichkeit ist, die Aktionen, die die Objektstruktur verändern, zu übertragen.<br />
Dies verringert die zu übertragende Menge von Daten noch weiter. Ein zusätzlicher Verwaltungsaufwand<br />
entfällt bei dieser Methode, <strong>ein</strong>zig die Serialisierung der ausgeführten Aktionen<br />
fällt an. Diese wird bei geeigneter Implementierung aber nicht weiter ins Gewicht fallen. Der<br />
Nachteil dieses Verfahrens ist, dass divergierende Spielzustände entstehen können, wenn k<strong>ein</strong>e<br />
weiteren regelmäßigen Zustandssynchronisierungen stattfinden. Zudem basiert diese Vorgehensweise<br />
auf der <strong>ein</strong>schränkenden Annahme, dass nur Aktionen, die auch versendbar sind, Einfluss<br />
auf die Objekte nehmen.<br />
3.3.3 Kompression<br />
Um den Netzwerkverkehr zu verringern, können Daten komprimiert werden. Hier bieten sich<br />
zwei verschiedene Möglichkeiten, die sich nicht zwangsweise gegenseitig ausschließen.<br />
3.3.3.1 Semantik-abhängig<br />
Die erste Möglichkeit ist <strong>ein</strong>e Kompression, die unwichtige Daten, die nicht zwingend übertragen<br />
werden müssen, herausfiltert. Das ist in dem Sinne semantik-äbhängig, als dass es von<br />
24 <strong>Jens</strong> <strong>Pfau</strong> · Stephan Mehlhase
III. Taxonomie 25<br />
der Modellierung abhängt und das Spielsystem selbst zu entscheiden hat, welche Daten <strong>ein</strong>er<br />
Übermittlung bedürfen und welche nicht. Vorteilhaft ist die Reduzierung des Netzwerkverkehrs<br />
auf das wirklich Notwendige. Nachteilig wirkt sich aus, dass <strong>ein</strong>e weitere Logik zur Filterung<br />
benötigt wird. Dies geht zu Lasten der Rechenzeit.<br />
3.3.3.2 Semantik-unabhängig<br />
Die andere Möglichkeit, Daten zu komprimieren, ist, auf bereits existierende, allgem<strong>ein</strong>gültige<br />
Verfahren wie GZIP 7 oder BZIP2 8 zurückzugreifen, die nicht vom Inhalt der Informationen<br />
abhängen. Der Vorteil ist, dass k<strong>ein</strong>e zusätzliche Logik anfällt und die Implementierungen der<br />
meisten Kompressionsbibliotheken ausgiebig getestet sind. Unvorteilhaft ist, dass bei kl<strong>ein</strong>en<br />
Datenmengen der Rechenaufwand <strong>für</strong> <strong>ein</strong>e Kompression zu hoch s<strong>ein</strong> kann. Dieses kann aber<br />
durch die Konkatenation mehrerer Daten kompensiert werden.<br />
3.3.4 Quality of Service (QoS)<br />
Ein weiterer wichtiger Faktor bei der Kommunikation ist die Steuerung des anfallenden Netzwerkverkehrs,<br />
so dass gewisse Qualitätsmerkmale <strong>für</strong> die Teilnehmer erreicht werden. Die Möglichkeiten<br />
lassen sich auch hier in zwei Kategorien <strong>ein</strong>teilen.<br />
3.3.4.1 Semantik-abhängig<br />
Die erste Methode ergibt sich aus der Modellierung der Objekte. Das Spielsystem selbst verwaltet<br />
die Priorität <strong>ein</strong>zelner Nachrichten oder Teilnehmer. Beispielsweise kann das Abfeuern<br />
<strong>ein</strong>er Waffe als wichtiger <strong>ein</strong>gestuft werden als die zeitgleiche Chatnachricht. Dementsprechend<br />
kann das System die zu versendenen Daten in <strong>ein</strong>er nach ihrer Priorität sortierten Liste halten<br />
und abarbeiten. Diese Implementierung basiert darauf, dass die Entwickler bewusst bestimmte<br />
Informationen priorisieren können. Allerdings bedeutet dies natürlich <strong>ein</strong>en weiteren Verwaltungsaufwand.<br />
3.3.4.2 Semantik-unabhängig<br />
Die andere Möglichkeit, Netzwerkverkehr zu steuern, kann über die verschiedenen, in Netzwerkprotokollen<br />
verankerten QoS Mechanismen geschehen. Vorteil ist hierbei, dass der Rechenaufwand<br />
entfällt. Der Nachteil hingegen ist ganz klar, dass die Netzwerkprotokolle QoS zwar<br />
theoretisch unterstützen, in der Praxis dies aber von der Umsetzung durch die Netzbetreiber<br />
abhängig ist [Tanenbaum, 2002; Mühlhäuser, 2005].<br />
7 http://www.gzip.org<br />
8 http://www.bzip.org<br />
<strong>Jens</strong> <strong>Pfau</strong> · Stephan Mehlhase 25
26 III. Taxonomie<br />
3.4 Übersicht<br />
Die erarbeitete Taxonomie wird der Übersicht halber noch <strong>ein</strong>mal in <strong>ein</strong>er Baumstruktur dargestellt.<br />
26 <strong>Jens</strong> <strong>Pfau</strong> · Stephan Mehlhase
III. Taxonomie 27<br />
Objektstruktur<br />
Daten<br />
Modellierung<br />
Objekte als Sprachkonstrukte<br />
Dynamische Attribute<br />
Dynamische Zustände<br />
Komponentenmodell<br />
Definition<br />
Quelltext<br />
Textdateien<br />
Binärdateien<br />
XML Dateien<br />
Skriptsprache<br />
Datenbanken<br />
Verhalten<br />
Modellierung<br />
Methoden<br />
Komponenten<br />
extern<br />
Definition<br />
Quelltext<br />
Skriptsprache<br />
Typen<br />
Modellierung<br />
Klassen<br />
Muster<br />
Definition<br />
Quelltext<br />
data-driven<br />
Vererbung<br />
Konzept<br />
Einfach<br />
Mehrfach<br />
Gegenstand<br />
Daten<br />
Verhalten<br />
Objektordnung<br />
K<strong>ein</strong>e<br />
Hierarchische Ordnung<br />
Logische Gruppierung<br />
Abbildung 3.1: Baumansicht der Taxonomie (Objektstruktur)<br />
<strong>Jens</strong> <strong>Pfau</strong> · Stephan Mehlhase 27
28 III. Taxonomie<br />
Dynamik zur Laufzeit<br />
Typen nachladbar<br />
Objekte nachladbar<br />
Verhalten nachladbar<br />
Typen veränderbar<br />
Kommunikation<br />
Initiative<br />
Polling<br />
Publish/Subscribe<br />
Änderungsübermittlung<br />
Delta Updates<br />
Aktionen<br />
Kompression<br />
Semantik-abhängig<br />
Semantik-unabhängig<br />
Quality of Service<br />
Semantik-abhängig<br />
Semantik-unabhängig<br />
Abbildung 3.2: Baumansicht der Taxonomie (Dynamik)<br />
Abbildung 3.3: Baumansicht der Taxonomie (Kommunikation)<br />
28 <strong>Jens</strong> <strong>Pfau</strong> · Stephan Mehlhase
Kapitel 4<br />
Anforderungen<br />
Das Problem kennen ist wichtiger, als<br />
die Lösung zu finden, denn die genaue<br />
Darstellung des Problems führt<br />
automatisch zur richtigen Lösung.<br />
Albert Einst<strong>ein</strong><br />
Im Folgenden sollen die funktionalen und nicht-funktionalen Vorraussetzungen des zu entwickelnden<br />
<strong>Objektmodells</strong> dargelegt werden. Diese ergehen zum <strong>ein</strong>en aus der Aufgabenstellung<br />
und zum anderen aus den praktischen Anforderungen der Spieleentwickler. Dabei wird vor allem<br />
Rücksicht darauf genommen, dass der endgültige Einsatzzweck des Systems offen gehalten und<br />
das Framework, von dem <strong>ein</strong> Subsystem das zu entwickelnde Objektmodell s<strong>ein</strong> wird, in unterschiedlichen<br />
Projekten <strong>ein</strong>setzbar s<strong>ein</strong> soll. Dass die geplante Verwendung jedoch hauptsächlich<br />
im Bereich der Multiplayer-Spiele angesiedelt ist, schafft natürlich erweiterte Bedingungen. Darüber<br />
hinaus unterliegen alle modernen Softwaresysteme, zu denen sich sicher auch Spiele zählen<br />
lassen, <strong>ein</strong>er gewissen Grundmenge an Anforderungen, die ebenfalls genannt werden sollen.<br />
4.1 Darstellung der Objektstruktur<br />
Die zentrale Aufgabe <strong><strong>ein</strong>es</strong> <strong>Objektmodells</strong> ist eben die Verwaltung der Spielobjekte. Dazu müssen<br />
diese selber sowie ihre Beziehungen in geeigneter Weise modelliert s<strong>ein</strong>. Die Möglichkeiten dazu<br />
wurden in Kapitel 3 dargestellt.<br />
Dieses System soll möglichst flexibel s<strong>ein</strong>, um unterschiedlichsten Anforderungen gerecht werden<br />
zu können und dennoch <strong>ein</strong>en effizienten Zugriff auf <strong>ein</strong>zelne Objekte oder abgeschlossene<br />
Teilmengen zu ermöglichen. Von Interesse sind hierbei Einfüge- sowie Lösch- und Änderungsoperationen<br />
auf der Objektstruktur. Natürlich müssen diese auch über Netzwerkgrenzen hinweg<br />
<strong>ein</strong>deutig s<strong>ein</strong>.<br />
Letztlich erfüllen Spielobjekte ihre Das<strong>ein</strong>sberechtigung erst dann, wenn auf ihnen Operationen<br />
ausgeführt werden können. Dies muss von dem System in leicht zu konfigurierbarer Art und<br />
Weise angeboten werden.
30 IV. Anforderungen<br />
4.2 Unabhängigkeit des Systems vom Spieldesign<br />
Bei der Spieleentwicklung kommt es auf das schnelle Umsetzen von Inhalt durch Projektmitglieder,<br />
die k<strong>ein</strong>e oder kaum Erfahrungen mit Programmiersprachen haben, an. Deren Arbeit<br />
aber unterliegt während des Projektes <strong>ein</strong>er andauernden <strong>Entwicklung</strong> und häufigen Tests. Eine<br />
stetige Neukompilierung der gesamten Codebasis ist aber nicht akzeptabel.<br />
Deswegen geschieht die Spezifikation von Entitäten über externe Datencontainer. Ein solcher<br />
als data-driven bezeichneter Ansatz, wie er in Kapitel 3.1 <strong>ein</strong>geführt wurde, erlaubt den Einsatz<br />
unabhängiger Werkzeuge zur Gestaltung des Spielinhalts und die mühelose Anpassung des<br />
Spielverhaltens.<br />
4.3 Persistenz<br />
Jedes Spiel benötigt <strong>ein</strong>e Möglichkeit, s<strong>ein</strong>en aktuellen Zustand persistent zu machen. Dies<br />
kann zum Beispiel durch <strong>ein</strong>e Datenbank, aber auch <strong>ein</strong>fach mit Hilfe von Dateien realisiert<br />
werden. Um die Austauschbarkeit dieser Wege zu ermöglichen, braucht es <strong>ein</strong>er <strong>ein</strong>heitlichen<br />
Schnittstelle, deren Realisierungen an spezifische Anforderungen angepasst sind.<br />
4.4 Performanz<br />
Immer noch ist die Netzwerkschnittstelle <strong>ein</strong>er der engsten Flaschenhälse beim Einsatz von<br />
Multiplayer-Spielen. Dies betrifft sowohl die Latenz bei der Kommunikation zwischen zwei<br />
Netzwerkknoten als auch die genutzte Bandbreite. Während ersteres heute zwar das größere<br />
Problem darstellt, aber vornehmlich durch die Netzwerkarchitektur des Spiels bestimmt und<br />
letztlich durch physikalische Gesetze begrenzt ist, lässt sich zweiteres hingegen in begrenztem<br />
Maß auch von anderen Subsystemen be<strong>ein</strong>flussen. Zur Unterstützung von Nutzern mit geringerer<br />
Bandbreite ist das Bereitstellen der Objekte oder Teilen derselben zur Versendung über das<br />
Netzwerk also kritisch, denn liefert dies doch die Basis <strong>für</strong> jede weitere Optimierung beziehungsweise<br />
Reduzierung des Datenstroms. Geeignete Mechanismen wie die mögliche Priorisierung<br />
<strong>ein</strong>zelner Nachrichten sollen hier Abhilfe schaffen. Schließlich bleibt es jedoch anderen Arbeiten<br />
vorbehalten, den Netzwerkstrom zu steuern. Gleichwohl soll <strong>ein</strong>e Schnittstelle zwischen Datenstruktur<br />
und Netzwerkschicht angeboten werden, die in flexibler Weise an unterschiedliche<br />
Einsatzszenarios angepasst werden kann.<br />
Ebenso bedingt die mögliche Verwendung in Echtzeit-Spielen <strong>ein</strong>e gewisse Leistungsfähigkeit in<br />
Hinblick auf die Reaktivität. Spieler werden k<strong>ein</strong>e erkennbare Verzögerung zwischen Eingabe<br />
und Ausführung <strong>ein</strong>er Aktion dulden. Dies wird nicht nur durch die Netzwerkschicht bestimmt,<br />
sondern auch durch weitere Teile des Systems.<br />
30 <strong>Jens</strong> <strong>Pfau</strong> · Stephan Mehlhase
IV. Anforderungen 31<br />
4.5 Erweiterbarkeit und Wiederverwendbarkeit<br />
Um das Produkt <strong>für</strong> Erweiterungen offen zu halten und damit die Anpassung an geänderte<br />
Anforderungen zu ver<strong>ein</strong>fachen, soll auf <strong>ein</strong>en modularen Aufbau geachtet werden. Jedes Subsystem<br />
soll <strong>ein</strong>e hohe Kohäsion aufweisen, das Gesamtsystem hingegen durch lose Kopplung<br />
den Austausch von Komponenten ver<strong>ein</strong>fachen. Eine ausführliche Dokumentation und <strong>ein</strong>fache<br />
Struktur soll zudem die Erlernbarkeit des Systems verbessern.<br />
4.6 Abgrenzung und Einschränkung der Aufgabe<br />
Diese Arbeit ist Teil <strong>ein</strong>er akademischen Studie. Ihr primäres Ziel ist nicht, <strong>ein</strong> Produktivsystem<br />
zu entwickeln, sondern Möglichkeiten auszuloten und <strong>ein</strong>e Referenzimplementierung zu liefern,<br />
die als Ausgangspunkt <strong>für</strong> weitere Untersuchungen dienen kann. Es wird zwar auf <strong>ein</strong>e angemessene<br />
Leistungsfähigkeit geachtet, diese aber nicht auf Kosten des leichten Verständnisses und<br />
der Erweiterbarkeit des Systems umgesetzt. Auch kann in der beschränkten Zeit nicht auf das<br />
Problem der Sicherheit <strong>ein</strong>gegangen werden, welches bei Spielsystemen vor allem in Hinsicht auf<br />
das Cheaten von Belang ist. Für Schnittstellen an der Peripherie zu anderen Subsystemen <strong>ein</strong>er<br />
Spielearchitektur werden nicht unbedingt Implementierungen geliefert, da dies den Rahmen<br />
sprengen würde.<br />
<strong>Jens</strong> <strong>Pfau</strong> · Stephan Mehlhase 31
Kapitel 5<br />
Bestehende Systeme<br />
The important thing in science is not<br />
so much to obtain new facts as to<br />
discover new ways of thinking about<br />
them.<br />
Sir William Bragg<br />
Nachdem <strong>ein</strong>e Taxonomie aufgestellt und die Anforderungen identifiziert wurden, sollen im Folgenden<br />
bestehende Systeme nach ersterer kategorisiert und gegen zweitere geprüft werden. Dies<br />
dient dazu, <strong>ein</strong>e Bestandsaufnahme der Marktsituation anzudeuten. Da <strong>ein</strong>e Vielzahl sehr spezifischer<br />
Ansätze existiert, kann hier nur auf <strong>ein</strong>e möglichst repräsentative Teilmenge derselben<br />
<strong>ein</strong>gegangen werden. Dazu zählen zum <strong>ein</strong>en Opensource Spiele Frameworks, aber auch Architekturbeschreibungen<br />
kommerzieller Produkte. Gerade letztere erarbeiten allgem<strong>ein</strong>gültigere<br />
Konzepte aus ihren spezifischen Lösungen heraus, mangeln aber an Detail und begleitendem<br />
Quelltext. Denselben bieten Opensource Projekte, die aber häufig kaum Dokumentation zur<br />
Verfügung stellen und lediglich proprietäre Ansätze implementieren, die nur in dem gegebenen<br />
Umfeld brauchbar sind.<br />
5.1 Dungeon Siege<br />
Das folgende, von Scott Bilas entworfene Objektmodell ist Teil des im Jahre 2002 veröffentlichten<br />
Spiels Dungeon Siege [Bilas, 2002]. Dies ist <strong>ein</strong> Fantasy-Rollenspiel mit Action-Elementen,<br />
welches sowohl <strong>ein</strong>en Singleplayer- als auch <strong>ein</strong>en Multiplayer-Modus unterstützt. Mit Hilfe <strong><strong>ein</strong>es</strong><br />
Editors lässt sich das Spiel nach den eigenen Wünschen anpassen, was die <strong>Entwicklung</strong><br />
zahlreicher Modifikationen ermöglicht hat.<br />
5.1.1 Systembeschreibung<br />
Dieses Modell basiert auf sogenannten Komponenten, die <strong>für</strong> sich jeweils <strong>ein</strong> eigenes, abgeschlossenes<br />
Verhalten definieren (Abbildung 5.1). Komponenten werden nach dem Baukastenprinzip<br />
zu Spielobjekten zusammengesetzt, die weiter k<strong>ein</strong>e eigenen Daten oder Verhalten umfassen. Jede<br />
Komponente ist entweder in der in diesem Produkt verwendeten Programmiersprache C++
V. Bestehende Systeme 33<br />
oder <strong>ein</strong>er eigens <strong>für</strong> das Spiel entwickelten Skriptsprache implementiert. Beispielhaft seien<br />
die C++-Komponenten Actor, Conversation, Inventory, Physics sowie die Skriptkomponenten<br />
spell_fire, water_effects und cmd_camera_move genannt. Ein Spielobjekt, das die<br />
Physics-Komponente enthält, würde damit das entsprechende Verhalten annehmen, um von<br />
der Physikengine angesteuert zu werden.<br />
wirdersteltmitHilfevon<br />
Komponente «interface» SkriptKomponente<br />
C+Komponente<br />
Spielobjekt Muster<br />
0.*korespondieren Datenkomponente<br />
spezifiziert 0.*<br />
Tabelenspezifikation basiertauf<br />
enthältInstanzenvon 0.* FeldNameTypStandardwert<br />
Abbildung 5.1: Skizze des Dungeon Siege <strong>Objektmodells</strong><br />
0.*<br />
Über Spezifikationsdateien respektive Metainformationen definieren C++- sowie Skriptkomponenten<br />
ihre Abhängigkeit von anderen Komponenten sowie ihre Datenfelder und deren Typen.<br />
Ein solches Datenschema wird Tabellenspezifikation genannt.<br />
Jedes Spielobjekt ist <strong>ein</strong>e Instanz <strong><strong>ein</strong>es</strong> Musters, aus dem es durch Kopie gewonnen wird. Die<br />
Muster stehen in <strong>ein</strong>er Hierarchie der Einfachvererbung und enthalten jeweils sogenannte Datenkomponenten.<br />
Diese definieren Daten gemäß <strong>ein</strong>er Tabellenspezifikation <strong>für</strong> die korrespondierende<br />
Komponente. Dabei kann <strong>ein</strong> Muster Informationen der Datenkomponenten s<strong><strong>ein</strong>es</strong> Vatermusters<br />
überschreiben, indem es nur s<strong>ein</strong>e Änderungen vorhält, sowie neue Datenkomponenten<br />
hinzufügen. Wenn <strong>ein</strong> Spielobjekt instanziiert werden soll, werden die von ihm und s<strong>ein</strong>em<br />
Muster spezifizierten Komponenten erstellt und durch die Datenkomponenten des Musters mit<br />
<strong>Jens</strong> <strong>Pfau</strong> · Stephan Mehlhase 33
34 V. Bestehende Systeme<br />
Informationen gefüllt, wobei Daten der Instanzdefinition die der Musterdefinition überschreiben.<br />
5.1.2 Einordnung<br />
Bei der Einordnung in die Taxonomie ist festzustellen, dass Spielobjekte k<strong>ein</strong>e eigenen Daten<br />
enthalten, sondern lediglich die Komponenten, mit deren Hilfe das Verhalten modelliert ist,<br />
<strong>ein</strong>en Zustand besitzen. Sowohl die Komponentendefinition als auch die Definition der Typen<br />
als Muster und der Spielobjekte an sich geschieht data-driven in Textdateien nach <strong>ein</strong>em proprietären<br />
Format.<br />
Es wird zwar k<strong>ein</strong>e explizite Aussage darüber getätigt, aber theoretisch ließen sich mit diesem<br />
System Objekte zur Laufzeit aus weiteren Definitionsdateien nachladen. Ebenso können<br />
zur Laufzeit bestehende Skripte geändert sowie neue hinzugefügt werden, die dann von weiteren,<br />
nachzuladenden Objekten als Komponenten enthalten werden können. Da die gesamte<br />
Typhierarchie vorgehalten wird und jeder Typ nur die Änderungen gegenüber s<strong>ein</strong>em Vorgänger<br />
speichert, können zur Laufzeit Typen geändert und weitere geladen werden. Sofern die Spielobjekte<br />
Referenzen auf ihren Typ halten und ebenfalls nur die Differenzen ihrer Daten zu denen<br />
ihres Typs speichern, würde sich damit die Änderung <strong><strong>ein</strong>es</strong> Typs auch auf dessen Instanzen<br />
auswirken.<br />
Zu der Kommunikation oder die Anordnung der Objekte zu<strong>ein</strong>ander betreffenden Fragen wird<br />
k<strong>ein</strong>e Aussage gemacht.<br />
Eine Übersicht über die Einordnung in die Taxonomie erkennt man auf Abbildung 5.2<br />
5.1.3 Evaluation<br />
Dieses Modell zieht s<strong>ein</strong>e Vorteile insbesondere daraus, dass alle Elemente data-driven definiert<br />
werden können. Verhalten wird unter anderem in Skriptdateien abgelegt, Muster und ihre Instanzen<br />
in Textdateien. Dadurch, dass die Definition von Objekttypen nicht durch <strong>ein</strong>e statische<br />
Vererbungshierarchie erfolgt, sondern durch das Kombinieren von unabhängigen Komponenten,<br />
lassen sich beliebig unterschiedliche Spielobjekte anlegen. Leistungskritische Komponenten können<br />
in der verwendeten Programmiersprache entwickelt werden, solche, die häufigen Änderungen<br />
während der <strong>Entwicklung</strong> unterliegen, in der Skriptsprache.<br />
Vor allem zur <strong>Entwicklung</strong> ist es von Vorteil, dass Typen, Spielobjekte und Verhalten, abgesehen<br />
von den C++-Komponenten, zur Laufzeit ohne Neukompilierung des Systems ausgetauscht<br />
werden könnten. Im Fall der Typen hat dies aber den Nachteil <strong>ein</strong>er größeren Speicherbelegung,<br />
weil die inneren Knoten der Hierarchie nicht verworfen werden können. Zudem bedarf es <strong>ein</strong>er<br />
längeren Zeit, <strong>für</strong> <strong>ein</strong> Objekt <strong>ein</strong> Datum festzustellen, wenn dazu der Typ des Objekts und<br />
dessen Vorfahren in der Hierarchie befragt werden müssen.<br />
Mit Hilfe entsprechender <strong>Entwicklung</strong>swerkzeuge, die auf Grund des Baukastenprinzips des<br />
Systems <strong>ein</strong>fach zu entwerfen sind, können somit sowohl Spieldesigner als auch Endanwender<br />
äußerst flexibel Spielinhalt nach ihren Wünschen formen. All dies fördert die Erweiterbarkeit<br />
34 <strong>Jens</strong> <strong>Pfau</strong> · Stephan Mehlhase
V. Bestehende Systeme 35<br />
Taxonomie<br />
Objektstruktur<br />
Daten<br />
Modellierung<br />
Komponentenmodell<br />
Definition<br />
Textdateien<br />
Verhalten<br />
Modellierung<br />
als Komponenten<br />
Definition<br />
Quelltext<br />
Skriptsprache<br />
Typen<br />
Modellierung<br />
Muster<br />
Definition<br />
data-driven<br />
Vererbung<br />
Konzept<br />
Einfach<br />
Gegenstand<br />
Verhalten<br />
Objektordnung<br />
Dynamik zur Laufzeit<br />
Kommunikation<br />
Abbildung 5.2: Einordnung des von Dungeon Siege verwendeten Systems in die Taxonomie<br />
des Systems. Es ist sogar vorstellbar, dass Komponenten, die unabhängig von der Logik des<br />
spezifischen Spiels entworfen wurden, in anderen Produkten gleicher Kategorie wiederverwendet<br />
werden können. Dies betrifft zum Beispiel die Grafikroutinen oder die Physikengine und<br />
Künstliche Intelligenz. Abgesehen davon ist das System an sich auf jeden Fall wiederverwendbar<br />
in anderen Projekten, da alle Spiellogik nur in den Komponenten und nicht dem Kernsystem<br />
definiert ist.<br />
Gemessen an den im vorigen Kapitel gegebenen Anforderungen liegen die Nachteile vor allem<br />
darin, dass sowohl <strong>ein</strong>e eigene Skriptsprache als auch <strong>ein</strong> eigenes Format zur Definition der<br />
Muster und Instanzen verwendet wird. Letzteres bedingt die Implementierung von Formatüberprüfungen<br />
im Quelltext, was beim Einsatz von XML bereits durch vorhandene Bibliotheken<br />
durchgeführt werden könnte.<br />
In der gegebenen Implementierung ist <strong>ein</strong> Austauschen der Skriptsprache nicht möglich, da das<br />
System beim Erstellen der Tabellenspezifikationen <strong>für</strong> die Skriptkomponenten auf entsprechende<br />
Metainformationen aus den Skripten angewiesen ist. Bei <strong>ein</strong>er eigenen Umsetzung des Ansatzes<br />
würde diese Einschränkung aber schon durch das Auslagern der Metainformationen in externe<br />
Dateien aufgehoben werden können.<br />
Der größte Mangel in diesem Kontext sind jedoch die fehlenden Aussagen über die Verwaltung<br />
<strong>Jens</strong> <strong>Pfau</strong> · Stephan Mehlhase 35
36 V. Bestehende Systeme<br />
und Anordnung der Spielobjekte sowie Möglichkeiten, den Zustand des Systems persistent zu<br />
machen. Auch die Kommunikation der Objekte mit weiteren Komponenten des Systems sowie<br />
der Synchronisierung mit anderen Teilnehmern bedarf <strong><strong>ein</strong>es</strong> eigenen Entwurfs.<br />
5.2 Thief und Deus Ex<br />
In den Spielen Thief und Deus Ex [Church, 2002; Duran, 2003; Doherty, 2003] werden die<br />
Spielobjekte mit Hilfe von Komponenten emuliert, wie in Kapitel 3.1.1.1 bereits vorgestellt.<br />
Deshalb soll hier k<strong>ein</strong>e erneute Beschreibung der Datenmodellierung gegeben werden. Es ist<br />
wichtig, dass der Begriff „Komponenten“ hier anders gebraucht wird als in der Beschreibung<br />
von Dungeon Siege (Kapitel 5.1).<br />
5.2.1 Systembeschreibung<br />
Ebenso wie die Daten der Spielobjekte wird deren Verhalten als Komponenten beschrieben. Die<br />
IDs der Objekte, die <strong>ein</strong> bestimmtes Verhalten zeigen können, werden in der entsprechenden<br />
Komponente, die dieses Verhalten implementiert, hinterlegt. Solche Komponenten können dann<br />
zum Beispiel Skriptobjekte, aber auch Objekte der Programmiersprache s<strong>ein</strong>.<br />
Objekttypen werden in der gleichen Struktur gehalten; <strong>ein</strong>e spezifische Instanz oder <strong>ein</strong> weiterer<br />
Typ erbt die Eigenschaften, sowohl Daten als auch Verhalten, ihres Vaters. Das erfordert, dass<br />
in <strong>ein</strong>er weiteren Datenstruktur die Vererbungslinien der Typen und Objekte hinterlegt sind.<br />
Sowohl die Beschreibungen der Daten und Spielobjekte als auch die der Typen werden datadriven<br />
abgelegt. Dies wird vor allem dadurch untersützt, dass Spielobjekte nichts weiteres als<br />
zusammengesteckte Eigenschaften sind.<br />
Spielobjekte können indirekt dadurch gruppiert werden, dass sie mit anderen den Eintrag in<br />
bestimmten Komponenten gem<strong>ein</strong>sam haben. Dies lässt, wie bereits erwähnt, andere Subsysteme<br />
des Spiels nur auf den <strong>für</strong> sie bedeutenden Informationen arbeiten.<br />
Spielobjekte und Typen, die genauso wie Spielobjekte modelliert werden, können zur Laufzeit<br />
nachgeladen werden. Das gleiche trifft <strong>für</strong> die Verhaltenskomponenten zu, die in <strong>ein</strong>er Skriptsprache<br />
definiert sind. Ob in diesem Modell Typen zur Laufzeit änderbar sind, so dass sich auch<br />
deren Kinder und Instanzen ändern, ist aus den Quellen nicht erkennbar. Das hängt zum <strong>ein</strong>en<br />
davon ab, ob vererbte Eigenschaften in dem Kindobjekt gehalten oder weiterhin aus der Vaterklasse<br />
angefordert werden. Zum anderen ist dies in zweitem Fall davon abhängig, ob erfolgreiche<br />
Zugriffe auf Eigenschaften in <strong>ein</strong>em Cache gehalten werden.<br />
5.2.2 Einordnung<br />
Aus den oben gegebenen Informationen und der Beobachtung, dass in den Quellen k<strong>ein</strong>e die<br />
Kommunikation betreffenden Themen behandelt werden, ergibt sich <strong>für</strong> dieses Modell die in<br />
36 <strong>Jens</strong> <strong>Pfau</strong> · Stephan Mehlhase
V. Bestehende Systeme 37<br />
Abbildung 5.3 dargestellte Einordnung in die Taxonomie.<br />
Taxonomie<br />
Objektstruktur<br />
Daten<br />
Modellierung<br />
Komponentenmodell<br />
Definition<br />
Verhalten<br />
Modellierung<br />
extern<br />
Definition<br />
Quelltext<br />
Skriptsprache<br />
Typen<br />
Modellierung<br />
Muster<br />
Definition<br />
data-driven<br />
Vererbung<br />
Konzept<br />
Einfach<br />
Gegenstand<br />
Daten<br />
Verhalten<br />
Objektordnung<br />
Logische Gruppierung<br />
Dynamik zur Laufzeit<br />
Typen nachladbar<br />
Objekte nachladbar<br />
Verhalten nachladbar<br />
Kommunikation<br />
Abbildung 5.3: Einordnung des von Thief und Deus Ex verwendeten Systems in die Taxonomie<br />
5.2.3 Evaluation<br />
Durch die Unterstützung des data-driven Designs mit Hilfe des komponentenbasierten Ansatzes<br />
lässt sich durch die Bereitstellung geeigneter Werkzeuge erreichen, dass Spielinhalt komplett<br />
unabhängig von dem Quelltext des Systems entworfen werden kann. In diesem Fall trifft dies<br />
auf alle Elemente der Objektstruktur zu: Spielobjekte, Typen und Verhalten lassen sich in<br />
externen Quellen definieren.<br />
Komponenten können <strong>für</strong> ihre spezifischen Anforderungen optimiert werden. Subsysteme, die<br />
eben nur auf <strong>ein</strong>e Teilmenge der Daten der aktuell relevanten Spielobjekte zugreifen müssen,<br />
werden sich auf Komponenten stützen, die ihnen eben genau diese und nicht mehr und nicht<br />
weniger bereitstellen. Das hat positive Auswirkungen auf die Leistungsfähigkeit des Systems.<br />
<strong>Jens</strong> <strong>Pfau</strong> · Stephan Mehlhase 37
38 V. Bestehende Systeme<br />
Spielobjekte lassen sich in beliebiger Form aus Daten und Verhalten zusammensetzen. Der<br />
Ansatz erlaubt große Flexibilität und damit den Einsatz in weiteren Projekten. Wegen des Verzichts<br />
auf die objektorientierte Modellierung wird sich <strong>ein</strong>e Erweiterung des Systems allerdings<br />
als schwierig herausstellen. Änderungen am Quelltext <strong>für</strong> die Komponentenmodellierung würden<br />
sich auch auf andere Programmteile auswirken. Zum Beispiel wäre die Frage zu klären, wie<br />
Abhängigkeiten zwischen den Eigenschaften der Spielobjekte implementiert werden könnten.<br />
Hier liegt gerade das Manko dieses Ansatzes. Die Aufgabe der objektorientierten Modellierung<br />
bedeutet die Abkehr von <strong>ein</strong>em wohlverstandenen Prinzip, das gerade <strong>für</strong> die modulare <strong>Entwicklung</strong><br />
<strong>ein</strong>steht. Das wirkt sich auch darin aus, dass k<strong>ein</strong>e Objektstruktur im eigentlichen Sinne<br />
existiert, zu der zum Beispiel über <strong>ein</strong>e <strong>ein</strong>heitliche Schnittstelle neue Objekte hinzugefügt oder<br />
bestehende gelöscht werden können.<br />
Weder über das Erreichen von Persistenz noch die Übermittlung der Daten werden in den Quellen<br />
Aussagen getätigt. Ersteres wird allerdings dadurch gefördert, dass Komponenten nichts<br />
anderes als Vektoren von Werten sind, die folglich direkt mit <strong>ein</strong>er Datenbankrelation korrelieren.<br />
5.3 Worldforge Atlas Middleware<br />
Hinter dem Namen Worldforge verbirgt sich <strong>ein</strong> vielschichtiges Opensource Projekt, das sich<br />
zum Ziel gesetzt hat, Entwicklern freier Spiele Werkzeuge an die Hand zu geben. Atlas, als<br />
Teil dieses Projekts, ist <strong>ein</strong> Kommunikationsprotokoll <strong>für</strong> Prozesse, die auch auf lokal verteilten<br />
Rechnern laufen können. Im Folgenden beziehen wir uns auf die C++-Referenzimplementierung,<br />
die zusätzlich <strong>ein</strong> Objektmodell definiert. Die Dokumentation jedoch ist sehr dürftig und die<br />
exakte Verwendung des Systems geht aus dem Quelltext nicht unmittelbar hervor.<br />
5.3.1 Systembeschreibung<br />
Atlas <strong>für</strong> sich all<strong>ein</strong>e definiert lediglich das Format <strong>für</strong> den Austausch von Objekten zwischen<br />
Prozessen. Die Referenzimplementierung hingegen bietet <strong>ein</strong>iges mehr. Sie gibt vor, wie Spielobjekte,<br />
Typen und Operationen implementiert s<strong>ein</strong> müssen, um mit Hilfe <strong>ein</strong>er gegebenen<br />
Netzwerkschicht versendet werden zu können.<br />
Den Kern des Systems bilden fünf elementare Datentypen, die sowohl von Spielobjekten verwendet<br />
werden als auch selber versendbar sind:<br />
Integer: Eine ganze Zahl.<br />
Float: Eine Gleitzahl.<br />
String: Eine Zeichenkette.<br />
List: Eine geordnete Liste anderer Objekte.<br />
Map: Eine ungeordnete Abbildung von Schlüsseln auf andere Objekte.<br />
38 <strong>Jens</strong> <strong>Pfau</strong> · Stephan Mehlhase
V. Bestehende Systeme 39<br />
Ein Spielobjekt ist nicht mehr als <strong>ein</strong>e Map der oben genannten Elemente, die zwei notwendige<br />
Einträge enthält: Die ID des Objekts sowie <strong>ein</strong>e Liste s<strong>ein</strong>er Väter. Ersteres dient zur <strong>ein</strong>deutigen<br />
Identifikation <strong><strong>ein</strong>es</strong> Spielobjekts, zweiteres kann zu Vererbungszwecken genutzt werden. Typen<br />
sind ebenfalls Objekte nach dem beschriebenen Prinzip, haben aber <strong>ein</strong>en Eintrag in der Map,<br />
der sie als Typen markiert.<br />
Operationen sind ebenfalls als solche Objekte modelliert. Zusätzlich referenzieren sie in ihrer<br />
Map das Objekt, welches die Operation veranlasst hat, das Objekt, auf das sie angewendet werden<br />
sollen, sowie <strong>ein</strong>e Liste von Parametern. In dieser Form werden sie auch zwischen Prozessen<br />
ausgetauscht. Da k<strong>ein</strong>e Möglichkeit existiert, <strong>ein</strong>en Rückgabewert durch <strong>ein</strong>en solchen Methodenaufruf<br />
zu erhalten, sind diese Operationen dem Prinzip nach Remote Procedure Calls 1 . Weder<br />
Atlas noch die Referenzimplementierung definieren jedoch, wie die eigentlichen Methoden<br />
definiert und nach dem Empfang auf ihr Zielobjekt angewendet werden.<br />
Es wäre möglich, alle diese genannten Objekte mit Hilfe <strong>ein</strong>er <strong>ein</strong>zigen Klasse zu implementieren,<br />
die eben lediglich die Map zum Halten der Attribute definiert. Die Implementierung bietet<br />
darüber hinaus aber weitere Klassen, welche von der oben genannten erben und zum Beispiel<br />
spezielle Entitäten oder Operationen darstellen. Diese geben dann <strong>ein</strong>ige Einträge der Map vor<br />
und bieten weitergehende direkte Methoden zum Setzen der zusätzlichen Attribute.<br />
Die Referenzimplementierung definiert Schnittstellen zum Serialisieren der zu versendenden<br />
Objekte sowie <strong>ein</strong>e Anzahl von Filtern, welche die Daten <strong>für</strong> die Netzwerkschicht aufbereiten<br />
beziehungsweise diese beim Empfang aus dem Netzwerkstrom zurückgewinnen. Letztere bieten<br />
zum Beispiel die Möglichkeit der Verschlüsselung oder den Einsatz von Prüfsummen und lassen<br />
sich <strong>für</strong> komplexere Aufgaben an<strong>ein</strong>anderketten.<br />
5.3.2 Einordnung<br />
Die Anordnung der Objekte sowie die Definition der Daten, des Verhaltens und der Typen sind<br />
nicht Teil von Atlas-C++. Nach dem Prinzip der Remote Procedure Calls versendet Atlas zur<br />
Änderungsübermittlung Operationen, auf die der Empfänger reagiert. Auf diesem Weg kann<br />
sich auch <strong>ein</strong> Teilnehmer über Änderungen bei anderen Teilnehmern informieren.<br />
Damit ergibt sich die in Abbildung 5.4 ersichtliche Einordnung in die Taxonomie.<br />
5.3.3 Evaluation<br />
Sowohl Spielobjekte als auch Typen und Aktionen werden lediglich über Daten definiert. Das<br />
erlaubt die flexible Gestaltung <strong><strong>ein</strong>es</strong> Spiels und bietet die Möglichkeit, genannte Entitäten<br />
aus externen Quellen mit Hilfe des Ansatzes des data-driven Designs zu erschaffen. Damit<br />
ist das System in anderen Projekten wiederverwendbar. Der Quelltext – zumindest der des<br />
Atlas-Frameworks – ist unabhängig von dem Spielinhalt. Die objektorientierte <strong>Entwicklung</strong> erlaubt<br />
es, das Modell um zusätzliche Funktionalität zu erweitern. Gerade die Idee der Filter zur<br />
1 http://de.wikipedia.org/wiki/Remote_Procedure_Call<br />
<strong>Jens</strong> <strong>Pfau</strong> · Stephan Mehlhase 39
40 V. Bestehende Systeme<br />
Taxonomie<br />
Objektstruktur<br />
Daten<br />
Modellierung<br />
Dynamische Zustände<br />
Definition<br />
Verhalten<br />
Modellierung<br />
extern<br />
Definition<br />
Typen<br />
Modellierung<br />
Klassen<br />
Definition<br />
Vererbung<br />
Konzept<br />
Mehrfach<br />
Gegenstand<br />
Daten<br />
Objektordnung<br />
Dynamik zur Laufzeit<br />
Kommunikation<br />
Initiative<br />
Polling<br />
Änderungsübermittlung<br />
Kompression<br />
Semantik-unabhängig<br />
Quality of Service<br />
Abbildung 5.4: Einordnung des in Atlas verwendeten Systems in die Taxonomie<br />
Be<strong>ein</strong>flussung des Kommunikationsverkehrs bietet die Anpassung des Systems an individuelle<br />
Bedürfnisse.<br />
Mit Atlas wird <strong>ein</strong> Protokoll zum Abgleich von Daten implementiert, das zumindest innerhalb<br />
des Worldforge Projekts standardisiert und getestet ist.<br />
Atlas-C++ bietet k<strong>ein</strong> vollständiges Objektmodell sondern hauptsächlich <strong>ein</strong>e Kommunikationsschnittstelle.<br />
Es bedarf <strong><strong>ein</strong>es</strong> weiteren Entwurfs zur Anordnung der Objekte, der Anbindung<br />
des Verhaltens an die Objekte und der Erstellung von Objekten.<br />
Zwischen Spielobjekten, Verhalten und Typen besteht k<strong>ein</strong> Unterschied im Quelltext. Das erfordert<br />
weitere Überprüfungen zur Laufzeit, die ansonsten zur Kompilierzeit hätten durchgeführt<br />
werden können.<br />
Mechanismen zur Persistenz werden nicht angeboten, sollten sich aber leicht in das System<br />
integrieren lassen, da alle Entitäten nur aus Daten bestehen.<br />
40 <strong>Jens</strong> <strong>Pfau</strong> · Stephan Mehlhase
V. Bestehende Systeme 41<br />
5.4 Britt L. Hannahs Vorschlag <strong>für</strong> <strong>ein</strong> Objektmodell<br />
In diesem Abschnitt soll <strong>ein</strong> Vorschlag <strong>für</strong> <strong>ein</strong> Objektmodell behandelt werden, der vor allem<br />
auf Wiederverwendbarkeit und damit den Einsatz als universelles Framework abzielt [Hannah,<br />
2004]. Eine Beispielimplementierung ist vorhanden, es mangelt aber noch an <strong>ein</strong>er kompletten<br />
Umsetzung der Idee.<br />
5.4.1 Systembeschreibung<br />
Das System basiert auf der Beobachtung, dass fünf grundlegende Entitäten in jedem Spiel zu<br />
finden sind. Diese manifestieren sich deshalb wiederum als Objekte im Modell und sollen hier<br />
als erstes <strong>ein</strong>geführt werden. Die genaue Wirkungsweise derselben wird im weiteren Verlauf<br />
deutlich werden.<br />
1. Game Entity Object: Das Entitätsobjekt repräsentiert <strong>ein</strong> Spielobjekt, welches alles<br />
von <strong>ein</strong>em Gegenstand bis zu <strong>ein</strong>em Spielercharakter darstellen kann.<br />
2. Game Action Object: Dieses Objekt modelliert die Logik des Spiels und damit das<br />
Verhalten der Spielobjekte. Dabei repräsentiert es Ereignisse wie Springen, Schießen, etc.<br />
Aktionen werden von anderen Aktionen oder durch Eingaben des Benutzers aufgerufen.<br />
3. Game Form Object: Im Gegensatz zu dem Entitätsobjekt stellt dieses Element die<br />
virtuelle Ersch<strong>ein</strong>ung <strong><strong>ein</strong>es</strong> Spielobjekts dar. Dies ist üblicherweise <strong>ein</strong> 3D-Modell. Im<br />
Folgenden bezeichnen wir dieses als Formobjekt.<br />
4. Game State Object: Zustandsobjekte stellen als Teil von Entitäts- und Formobjekten<br />
deren Zustand zu <strong>ein</strong>em bestimmten Zeitpunkt dar. Jedes Zustandsobjekt repräsentiert<br />
dabei <strong>ein</strong>e Zustandsvariable.<br />
5. Game Space Object: Ein Raumobjekt ist <strong>ein</strong>e Menge von anderen Objekten des Systems.<br />
Die Motivation <strong>für</strong> <strong>ein</strong> solches Objekt ist Optimierung, denn auf diesem Weg können<br />
Spielobjekte <strong>für</strong> bestimmte Zwecke und zur Bearbeitung durch andere Subsysteme des<br />
Spiels gruppiert werden.<br />
Diese Objekte stehen in zahlreichen Beziehungen zu<strong>ein</strong>ander, wie aus Abbildung 5.5 ergeht.<br />
Um diese nachvollziehen zu können, sollen jetzt, nachdem <strong>ein</strong> Überblick gegeben wurde, alle<br />
Elemente des Modells im Detail betrachtet werden.<br />
Das Raumobjekt<br />
Ein Raumobjekt enthält Entitäts- und/oder Formobjekte, die in <strong>ein</strong>er dem aktuellen Kontext<br />
entsprechenden Beziehung zu<strong>ein</strong>ander stehen. Natürlich ist dies dynamisch und wird sich während<br />
der Laufzeit ständig ändern.<br />
<strong>Jens</strong> <strong>Pfau</strong> · Stephan Mehlhase 41
42 V. Bestehende Systeme<br />
0.*<br />
Space<br />
target,source 0.* Form Entity Action<br />
0.*<br />
0.*<br />
liestundändert State0.*0.*<br />
Abbildung 5.5: UML Diagramm der von Britt L. Hannah vorgeschlagenen Architektur<br />
Die übliche Verwendung der Raumobjekte ist zum Beispiel die Unterteilung der Menge der<br />
Spielobjekte in <strong>ein</strong>en virtuellen und <strong>ein</strong>en logischen Raum. Während ersterer die Formobjekte<br />
hält, die in der aktuellen Spielsituation visuell dargestellt werden sollen, umfasst zweiterer die<br />
Entitätsobjekte, die die Logik des Spiels bestimmen.<br />
Das Zustandsobjekt<br />
Zustandsobjekte sind Teil von Entitäts- und Formobjekten und sind, wie in Kapitel 3.1.1.1 vorgestellt,<br />
als dynamische Zustände implementiert. Dabei definieren sie den Zustand, in dem sich<br />
<strong>ein</strong> solches Objekt zu <strong>ein</strong>em bestimmten Zeitpunkt befindet. Dies ist zunächst nicht ungewöhnlich,<br />
wird aber bei der Betrachtung der Aktionsobjekte interessant, denn ob <strong>ein</strong>e bestimmte<br />
Aktion auf <strong>ein</strong> Objekt anwendbar ist, bestimmt sich durch die Zustandsvariablen desselben.<br />
Eine Aktion zum Ändern der Lebenspunkte <strong><strong>ein</strong>es</strong> Objekts könnte nur auf solche Spielobjekte<br />
angewendet werden, die eben genau die Zustandsvariable <strong>für</strong> die Lebenspunkte enthalten. Aktionen<br />
ändern bei ihrer Anwendung die Werte der Zustandsobjekte von Objekten und damit<br />
also indirekt auch das Verhalten derselben.<br />
42 <strong>Jens</strong> <strong>Pfau</strong> · Stephan Mehlhase
V. Bestehende Systeme 43<br />
Das Formobjekt<br />
Formobjekte sind die Objekte des Systems, die visuell dargestellt werden. Der Nutzer wird dabei<br />
davon ausgehen, dass das, was er sieht, das Spielobjekt ist. Das ist allerdings falsch, denn dieses<br />
wird systemintern von <strong>ein</strong>em Entitätsobjekt repräsentiert. Lediglich die Informationen in Form<br />
von Zustandsobjekten, die <strong>für</strong> die Anzeige nötig sind, werden im Formobjekt gehalten.<br />
Der Vorteil dieser Auslagerung der visuellen Eigenschaften ist, dass die Anzeige der Spielobjekte<br />
von denselben entkoppelt und somit leicht austauschbar ist und die Formobjekte unabhängig von<br />
den eigentlichen Spielobjekten in <strong>für</strong> die Grafikengine vorteilhaften Datenstrukturen gehalten<br />
werden können.<br />
Um die Anzeige zu steuern, enthält <strong>ein</strong> Formobjekt Zustandsobjekte. Diese bestimmen zum<br />
Beispiel s<strong>ein</strong>e Position und Ausrichtung im Raum. Gleichzeitig wirken sich diese aber auch auf<br />
die Aktionen aus, die auf <strong>ein</strong>em Entitäts- und s<strong>ein</strong>em Formobjekt ausgeführt werden. Denn die<br />
Aktionen können natürlich auch von den oben genannten Parametern abhängen.<br />
Dadurch, dass Formobjekte Raumobjekte enthalten können, können sie transitiv auch andere<br />
Entitätsobjekte b<strong>ein</strong>halten. Das hat vor allem <strong>ein</strong>e Bedeutung in Hinblick auf die Aktionsobjekte,<br />
die in Raumobjekten auf Mengen von Objekten arbeiten. Eine Aktion könnte zum Beispiel<br />
aus dem 3D-Modell <strong><strong>ein</strong>es</strong> Formobjekts Beschränkungen <strong>für</strong> die in diesem enthaltenen Objekte<br />
herleiten. Ein Raum in <strong>ein</strong>em Haus wäre zum Beispiel durch <strong>ein</strong> Formobjekt repräsentiert.<br />
Für die in s<strong>ein</strong>en Raumobjekten enthaltenen Entitätsobjekte würde die Einschränkung gelten,<br />
dass diese sich nicht durch die Wand des Raumes, also über die Abmessungen des Formobjekts<br />
hinaus, bewegen könnten.<br />
Das Aktionsobjekt<br />
Entitätsobjekte werden zum <strong>ein</strong>en durch die enthaltenen Zustandsobjekte als auch durch ihre<br />
Aktionen beschrieben. Letztere beschreiben das Verhalten, welches <strong>ein</strong> Spielobjekt mitbringt.<br />
Aktionen verändern den Zustand von Spielobjekten dadurch, dass sie die Zustandsobjekte der<br />
Entitäts- und Formobjekte modifizieren. Gleichzeitig hängt ihre Logik aber auch von diesen<br />
Zustandsvariablen ab.<br />
Aktionsobjekte halten zum <strong>ein</strong>en <strong>ein</strong>e Referenz auf das Entitätsobjekt, von dem sie ausgehen,<br />
sowie <strong>ein</strong> Zielobjekt oder <strong>ein</strong>e Menge von Zielobjekten. Bei der Ausführung hängt diese Aktion<br />
nur von deren Zustandsobjekten ab und ändert eben auch nur dieselben.<br />
Die Idee ist, dass Aktionen wiederum neue Aktionen hervorrufen und so das Spiel lebendig<br />
wird. Das Beispiel wäre <strong>ein</strong>e Aktion Schießen, die auf <strong>ein</strong>em Spielobjekt wirkt und dessen Lebenspunkte<br />
modifiziert. Zunächst stellt die Aktion fest, ob ihr Ziel das Zustandsobjekt <strong>für</strong> die<br />
Lebenspunkte überhaupt besitzt. Ist das der Fall, wird die Aktion ihre Arbeit machen und danach<br />
überprüfen, ob die Lebenspunkte des Zielobjekts noch größer Null sind. Sollte das nicht<br />
zutreffen, könnte sie auf jenem Objekt zum Beispiel <strong>ein</strong>e Sterben-Aktion aufrufen.<br />
Aktionen sollen über entsprechende Schnittstellen direkt die audiovisuelle Ausgabe ansteuern<br />
<strong>Jens</strong> <strong>Pfau</strong> · Stephan Mehlhase 43
44 V. Bestehende Systeme<br />
beziehungsweise den entsprechenden Subsystemen Aufgaben zuweisen. Es ist wichtig, dass Aktionen<br />
über <strong>ein</strong>en Zeitraum und nicht nur zu <strong>ein</strong>em Zeitpunkt existieren. Dies wird im Folgenden<br />
deutlich.<br />
Das Entitätsobjekt<br />
Die Entitätsobjekte sind die Schaltstelle des Systems, wie aus Abbildung 5.5 zu entnehmen ist.<br />
Sie haben Beziehungen zu allen anderen Objekttypen.<br />
Die Visualisierung <strong><strong>ein</strong>es</strong> Entitätsobjekts wird durch das assoziierte Formobjekt bestimmt. Dieses<br />
ist austauschbar, so dass <strong>ein</strong> Entitätsobjekt leicht unterschiedliche Ersch<strong>ein</strong>ungen annehmen<br />
kann.<br />
Während der Zustand <strong><strong>ein</strong>es</strong> Entitätsobjekts durch s<strong>ein</strong>e Zustandsobjekte definiert ist, wird s<strong>ein</strong><br />
Verhalten durch die ihm zugeordneten Aktionen festgelegt. Diese werden in zwei unterschiedlichen<br />
Datenstrukturen innerhalb des Entitätsobjektes gehalten. Zunächst befinden sich die<br />
Aktionen in der Menge der <strong>für</strong> dieses Objekt gültigen Aktionen. Wird <strong>ein</strong>e Aktion aufgerufen,<br />
wird sie in <strong>ein</strong>e Warteschlange <strong>für</strong> auszuführende Aktionen verschoben. So kann später das Entitätsobjekt<br />
aufgefordert werden, nach<strong>ein</strong>ander die Aktionen dieser Warteschlange auszuführen.<br />
Gleichzeitig wird verhindert, dass <strong>ein</strong>e Aktion zwei Mal ausgeführt wird. Die entsprechende<br />
Aktion kann nun mehrere Iterationen in dieser Warteschlange verharren und wird in jedem<br />
Durchlauf ihre Arbeit verrichten. Sobald die Aktion diese abgeschlossen hat, wird sie sich selber<br />
aus der Warteschlange entfernen und zurück in die Menge der verfügbaren Aktionen des<br />
entsprechenden Entitätsobjekts befördern.<br />
Dadurch, dass <strong>ein</strong> Entitätsobjekt indirekt über s<strong>ein</strong> Formobjekt <strong>ein</strong>e Referenz auf <strong>ein</strong> Raumobjekt<br />
hält, kann es jedes andere Objekt, welches im aktuellen Kontext gültig ist, erreichen. Dies<br />
ist vor allem <strong>für</strong> die Aktionen wichtig, die damit ihre Zielobjekte bestimmen.<br />
5.4.2 Einordnung<br />
Es wird k<strong>ein</strong>e Aussage über die Definition von Daten und Verhalten gemacht, beides wäre jedoch<br />
data-driven umsetzbar, da zum <strong>ein</strong>en die Daten auf dynamischen Attributen basieren und<br />
die Aktionen entkoppelt von den Spielobjekten sind und somit als Skriptobjekte zu implementieren<br />
wären. Selbiges trifft auf die Typisierung zu. Die Spielobjekte sind zum <strong>ein</strong>en dadurch<br />
hierarchisch geordnet, dass Entitätsobjekte mit Formobjekten assoziiert sind, diese wiederum<br />
Raumobjekte halten, die schließlich wieder Entitätsobjekte enthalten können. Zum anderen lassen<br />
sich Spielobjekte mit Hilfe der Raumobjekte gruppieren.<br />
Über die Dynamik zur Laufzeit kann wegen der fehlenden Beschreibung der Art der Objektdefinition<br />
k<strong>ein</strong>e Aussage gemacht werden. Bei <strong>ein</strong>er entsprechenden Umsetzung nach dem datadriven<br />
Design sollte es möglich s<strong>ein</strong>, in diesem Punkt <strong>ein</strong>e größtmögliche Flexibilität zu erreichen.<br />
Die Unterstützung der Kommunikation wird in der Quelle bewusst nicht behandelt, denn das<br />
Modell soll durch s<strong>ein</strong>e Modularität inhärent offen gegenüber derartigen neuen Anforderungen<br />
s<strong>ein</strong>.<br />
44 <strong>Jens</strong> <strong>Pfau</strong> · Stephan Mehlhase
V. Bestehende Systeme 45<br />
Es ergibt sich damit die in Abbildung 5.6 sichtbare Einordnung in die Taxonomie.<br />
Taxonomie<br />
Objektstruktur<br />
Daten<br />
Modellierung<br />
Dynamische Zustände<br />
Definition<br />
Verhalten<br />
Modellierung<br />
als Komponenten<br />
Definition<br />
Typen<br />
Objektordnung<br />
Hierarchische Ordnung<br />
Logische Gruppierung<br />
Dynamik zur Laufzeit<br />
Kommunikation<br />
Abbildung 5.6: Einordnung des von Britt L. Hannah vorgeschlagenen Systems in die Taxonomie<br />
5.4.3 Evaluation<br />
Das vorgestellte System basiert vor allem auf der Austauschbarkeit s<strong>ein</strong>er Bestandteile. Nicht<br />
nur kann die visuelle Ersch<strong>ein</strong>ung der Spielobjekte leicht ausgetauscht werden, sondern auch<br />
das Verhalten, welches in den Aktionen – leichtgewichtigen Komponenten – gekapselt ist. Spielobjekte<br />
können in beliebiger Form aus Zustandsvariablen zusammengesteckt werden, wodurch<br />
gleichzeitig ihr Verhalten gesteuert werden kann.<br />
Dies fördert nicht nur die Wiederverwendbarkeit sondern auch die Erweiterbarkeit des Systems.<br />
Wie beschrieben ist es leicht vorstellbar, das System data-driven zu gestalten und damit die<br />
Unabhängigkeit des Spieldesigns vom Quelltext zu erreichen.<br />
Durch die Gruppierung der Entitäts- und Formobjekte in Raumobjekte lassen sich <strong>für</strong> andere<br />
Subsysteme des Spiels Sichten auf das Objektmodell definieren, die <strong>ein</strong>en effizienten Zugriff <strong>für</strong><br />
die jeweilige Aufgabe erlauben.<br />
Nachteilig in Hinblick auf die gegebenen Anforderungen ist, dass sich die Quelle weder mit<br />
Persistenz noch mit Kommunikation beschäftigt. Es bedarf weiterer Untersuchungen, wie das<br />
Modell diese beiden Subsysteme in ausreichender Weise unterstützen könnte.<br />
Das System definiert k<strong>ein</strong>e <strong>ein</strong>zelne Schnittstelle auf die entstehende Objektstruktur. Anstattdessen<br />
existieren parallel unterschiedliche Typen von Objekten in komplexen Beziehungen und<br />
mit sehr unterschiedlichen Aufgaben, was die Implementierung erschwert und <strong>ein</strong>en <strong>ein</strong>heitlichen<br />
Zugriff auf die Struktur verhindert.<br />
<strong>Jens</strong> <strong>Pfau</strong> · Stephan Mehlhase 45
Kapitel 6<br />
Design und Implementierung<br />
Die Schöpfung ist niemals vollendet.<br />
Sie hat zwar <strong>ein</strong>mal angefangen, aber<br />
sie wird niemals aufhören.<br />
Immanuel Kant<br />
Das Ziel dieser Arbeit ist die Erstellung <strong><strong>ein</strong>es</strong> <strong>flexiblen</strong> <strong>Objektmodells</strong> <strong>für</strong> Spiele. Eine Bestandsaufnahme<br />
der Marktsituation wurde durchgeführt und <strong>ein</strong>e Taxonomie aufgestellt, die<br />
die Problemstellung charakterisiert und kategorisiert. Die Anforderungen an die Arbeit wurden<br />
identifiziert und dargelegt. Diese seien hier zur Erinnerung noch <strong>ein</strong>mal gegeben:<br />
• Flexible Objektstruktur<br />
• Auf den Spielobjekten ausführbare Aktionen<br />
• Unterstützung des data-driven Designs<br />
• Möglichkeit zur Herstellung der Persistenz des Spielzustands<br />
• Ausreichende Performanz<br />
• Erweiterbarkeit und Wiederverwendbarkeit<br />
Aufbauend auf den gewonnenen Erkenntnissen soll im Folgenden <strong>ein</strong> Objektmodell entworfen<br />
werden, das den genannten Anforderungen genügt. Die Taxonomie umfasst die <strong>für</strong> <strong>ein</strong> solches<br />
Projekt zu bedenkenden Aspekte und deren Implementierungsmöglichkeiten. Es liegt daher<br />
nahe, das System anhand dieser zu entwickeln. Wir werden also schrittweise die <strong>ein</strong>zelnen Abschnitte<br />
der Taxonomie betrachten und damit sukzessive <strong>ein</strong> Softwaresystem entwickeln, das in<br />
jedem Schritt um weitere Elemente wie Klassen und Methoden ergänzt wird. Die Implementierung<br />
wird in Java vorgenommen, weil damit die Kompatibilität mit dem zu entwickelnden<br />
Framework erhöht wird, dessen <strong>Entwicklung</strong> in der gleichen Programmiersprache erfolgt. Daher<br />
hat sich auch die Modellierung auf die von Java bereitgestellten Konstrukte zu beschränken.<br />
Zum Beispiel wird die Mehrfachvererbung nicht unterstützt.<br />
Abschließend wird das Gesamtsystem dargestellt und kurz gegen die Anforderungen geprüft.<br />
Im nachfolgenden Kapitel wird dann beschrieben, wie das Objektmodell in <strong>ein</strong>em Spieleprojekt<br />
zu nutzen ist.
VI. Design und Implementierung 47<br />
6.1 Spielobjekte<br />
Zunächst wird die Struktur der Spielobjekte definiert. Dabei sind die Daten, das Verhalten, die<br />
Typisierung und die Anordnung der Objekte unter<strong>ein</strong>ander zu bestimmen. Jeder dieser Aspekte<br />
wird nun <strong>ein</strong>zeln betrachtet, modelliert und die jeweiligen Entscheidungen begründet.<br />
6.1.1 Daten<br />
In Kapitel 3.1.1.1 wurden die Möglichkeiten zur Modellierung der Daten der Spielobjekte vorgestellt.<br />
Die Implementierung als Sprachkonstrukte bedingt <strong>ein</strong>e feste Kodierung der Objektdefinitionen<br />
im Quelltext, was <strong>ein</strong>en <strong>flexiblen</strong> Einsatz und ständige Änderungen zur Laufzeit<br />
unterbindet oder zumindest stark erschwert. Das Komponentenmodell unterstützt das datadriven<br />
Design, induziert aber <strong>ein</strong>e steile Lernkurve bei den Mitarbeitern und verzichtet auf<br />
<strong>ein</strong>e direkte Modellierung mit Hilfe der objektorientierten <strong>Entwicklung</strong> und damit die leichte<br />
Erweiterbarkeit und Wiederverwendbarkeit. Die Modellierung als dynamische Attribute bietet<br />
<strong>ein</strong>e größtmögliche Flexibilität, mangelt aber an <strong>ein</strong>er Typisierung der Attribute, wodurch teure<br />
Typüberprüfungen zur Laufzeit nötig werden.<br />
Es liegt daher nahe, die Daten mit Hilfe dynamischer Zustände zu modellieren, die zum <strong>ein</strong>en<br />
nicht dem Problem der fehlenden Typisierung unterliegen und zum anderen durch die Kombinationen<br />
der Zustände beliebige Spielobjekte erlauben, die auch zur Laufzeit problemlos geändert<br />
werden können. Der zusätzliche Speicherbedarf, der dadurch entsteht, dass alle Daten auf<br />
Instanzebene und nicht auf Klassenebene gehalten werden, kann durch <strong>ein</strong>e eigene Implementierung<br />
von Spielobjekttypen verringert werden. Auf diesem Weg ist es auch möglich, direkt<br />
den Typ <strong><strong>ein</strong>es</strong> Objekts zu bestimmen. Dies wird im späteren Verlauf behandelt. Die zusätzlichen<br />
Laufzeitkosten werden gemäß den Anforderungen zu Gunsten <strong>ein</strong>er guten Erweiterbarkeit,<br />
Wiederverwendbarkeit und Erlernbarkeit vernachlässigt.<br />
Die Zustände sollen in unterschiedlichen Typen vorliegen, die wir auf folgende festgelegt haben:<br />
Double: <strong>ein</strong>e Gleitkommazahl<br />
Integer: <strong>ein</strong>e ganze Zahl<br />
Boolean: <strong>ein</strong> Bool’scher Wert<br />
String: <strong>ein</strong>e Zeichenkette<br />
Map: <strong>ein</strong>e Zuordnung von Zeichenketten auf Zustände<br />
Set: <strong>ein</strong>e ungeordnete Menge von Zuständen<br />
List: <strong>ein</strong>e geordnete Liste von Zuständen<br />
Damit die Instanzen dieser Zustandstypen innerhalb der Spielobjekte in <strong>ein</strong>er <strong>ein</strong>zigen Datenstruktur<br />
gehalten werden können, bedarf es <strong>ein</strong>er gem<strong>ein</strong>samen Oberklasse, die im Folgenden<br />
<strong>Jens</strong> <strong>Pfau</strong> · Stephan Mehlhase 47
48 VI. Design und Implementierung<br />
+setName(name:String) +getName():String State<br />
+getAsInt():int +setAsDouble(double:double) +getAsDouble():double +setAsInt(int:int) 0.* GameObject states:Map +getState(key:String):State +adState(state:State) +removeState(key:String) +hasState(key:String):bol<br />
etali<br />
Abbildung 6.1: UML Diagramm der Zustände<br />
als State bezeichnet wird und <strong>ein</strong> Paar von Zugriffsfunktionen (Getter und Setter) <strong>für</strong> jeden der<br />
oben genannten Typen definiert. Diese Methoden werfen in ihrem Standardverhalten zunächst<br />
noch Ausnahmen. Die konkreten Zustandsklassen überschreiben dann genau die Methoden <strong>für</strong><br />
den Typ, den sie repräsentieren. So ist sichergestellt, dass auf <strong>ein</strong>em konkreten State nur typsichere<br />
Operationen ausgeführt werden können.<br />
Ein Spielobjekt enthält <strong>ein</strong>e Menge von Zuordnungen von Schlüsseln auf State Objekte.<br />
Das ermöglicht es, <strong>ein</strong> Objekt gezielt nach <strong>ein</strong>em bestimmten Zustand zu befragen. Da<strong>für</strong><br />
werden die Zugriffsmethoden hasState(String), getState(String), addState(State) und<br />
removeState(String) angeboten. Mit dem Klassennamen GameObject <strong>für</strong> das Spielobjekt ergibt<br />
sich dann zunächst die in Abbildung 6.1 dargestellte Modellierung.<br />
value:int +setAsInt(int:int) +getAsInt():int IntState DoubleState +setAsDouble(double:double) +getAsDouble():double d:double<br />
Die Definition der Daten basiert lediglich auf den Zustandsklassen und ist damit unabhängig<br />
von der Klasse GameObject. Somit können die Informationen extern nach dem Prinzip des<br />
data-driven Design beschrieben werden. Einsetzbar sind alle in Kapitel 3.1.1.2 vorgestellten<br />
Datenquellen. Von dem System wird k<strong>ein</strong> bestimmter Weg vorgeschrieben, wie später noch<br />
deutlich werden wird.<br />
6.1.2 Verhalten<br />
Da den Spielobjekten flexibel Verhalten zugewiesen werden können soll, ist es nicht sinnvoll,<br />
dieses fest in Form von Methoden an die Klasse GameObject zu binden. Abgesehen davon ist<br />
die Anzahl der Aktionen in <strong>ein</strong>em ausgewachsenen Spiel sehr groß, was <strong>ein</strong>e aufgeblähte Klas-<br />
48 <strong>Jens</strong> <strong>Pfau</strong> · Stephan Mehlhase
VI. Design und Implementierung 49<br />
sendefinition nach sich ziehen würde, von der jede Instanz letztlich nur <strong>ein</strong>en geringen Teil der<br />
Methoden gebrauchen würde. Nun bleibt noch, das Verhalten extern oder als Komponenten zu<br />
modellieren. Ersteres bedarf aufwendiger Logik, um festzusetzen, welches Verhalten <strong>ein</strong> Spielobjekt<br />
annehmen kann. Letzteres hingegen bietet Flexibilität, unterliegt aber in s<strong>ein</strong>er ursprünglichen<br />
Fassung dem Problem, dass das Spezifizieren von Abhängigkeiten zwischen Komponenten<br />
kompliziert ist.<br />
[Hannah, 2004] schafft dem Abhilfe, wie in Kapitel 5.4 vorgestellt. Aktionen sind als Command-Objekte<br />
[Gamma, 1997] und damit als Komponenten modelliert und ihr Wechselspiel<br />
unter<strong>ein</strong>ander basiert in erster Linie auf den Zuständen der Spielobjekte sowie auf der Möglichkeit,<br />
dass Aktionen wiederum andere Aktionen induzieren können. Insbesondere ist das Modell<br />
unabhängig von den eigentlichen Spielobjekten, Verhalten bestimmt sich nur auf Grund der<br />
Zustände von Spielobjekten. Die dadurch entstehende lose Kopplung der Komponenten Spielobjekte<br />
und Verhalten erlaubt die leichte Austauschbarkeit und damit Wiederverwendbarkeit<br />
derselben. Wir werden die Modellierung der Aktionen auf dem gleichen Weg umsetzen. Diese<br />
referenzieren zum <strong>ein</strong>en <strong>ein</strong> Spielobjekt, von dem sie ausgehen (source) und zum anderen <strong>ein</strong><br />
<strong>ein</strong>zelnes Zielobjekt (target) oder <strong>ein</strong>e Menge von Zielobjekten (targetSet). Ist k<strong>ein</strong> target<br />
Objekt gegeben, wird angenommen, dass das source Objekt ebenfalls diese Rolle übernimmt.<br />
Wird die Methode doAction() auf <strong>ein</strong>er solchen Aktion aufgerufen, führt sie ihre Arbeit basierend<br />
auf den Zuständen ihres Quellobjekts und ihrer Zielobjekte aus. Üblicherweise ändert sie<br />
als Ergebnis die Zustände und damit das Verhalten derselben.<br />
Die Klasse GameObject erhält <strong>ein</strong>e Menge möglicher Aktionen (possibleActions), die über<br />
ihren Namen referenziert werden, sowie <strong>ein</strong>e Warteschlange von gerade ausgeführten Aktionen<br />
(currentActions). Mit Hilfe der Methode activateAction(), die als Parameter den Namen<br />
<strong>ein</strong>er Aktion sowie <strong>ein</strong> Zielobjekt oder <strong>ein</strong>e Menge von Zielobjekten erwartet, kann nun <strong>ein</strong>e<br />
Aktion auf <strong>ein</strong>em Spielobjekt, die sich in den possibleActions befindet, initialisiert und aktiviert<br />
werden. Diese wird dann in die currentActions verschoben und in jeder Spieliteration<br />
durch doAction() aufgefordert, ihre Aufgabe zu erfüllen, bis <strong>ein</strong>e externe Komponente oder sie<br />
sich selber mit Hilfe der Methode deactivateAction() <strong>für</strong> beendet erklärt und das Spielobjekt<br />
auffordert, sie zurück in die possibleActions zu verschieben. So wird zum <strong>ein</strong>en das Ausführen<br />
von Aktionen ermöglicht, die länger als <strong>ein</strong>e Spieliteration dauern, zum anderen wird verhindert,<br />
dass <strong>ein</strong>e Aktion gleichzeitig mehrmals von dem gleichen Spielobjekt ausgeführt wird.<br />
Ein Spielobjekt kann über die Methode doActions() aufgefordert werden, die Aktionen s<strong>ein</strong>er<br />
currentActions auszuführen. Dies sollte üblicherweise <strong>ein</strong>mal in jeder Iteration der Spielschleife<br />
geschehen. Mit Hilfe der Methoden sleep() und unsleep() kann erreicht werden, dass <strong>ein</strong><br />
Spielobjekt über <strong>ein</strong>en bestimmten Zeitraum k<strong>ein</strong>e Aktionen ausführt. Diese Methoden werden<br />
später sukzessive mit weiterer Funktionalität versehen, um Spielobjekte komplett in <strong>ein</strong>en<br />
passiven Zustand zu überführen.<br />
Aktionen mit <strong>ein</strong>em konkreten Verhalten erben von der abstrakten Klasse Action, die die oben<br />
genannte Basisfunktionalität bietet. Das Verhalten kann dann sowohl im Quelltext innerhalb<br />
der doAction() Methode als auch extern in <strong>ein</strong>er Skriptsprache definiert s<strong>ein</strong>, denn <strong>ein</strong>e Aktion<br />
kann natürlich in dieser Methode auch <strong>ein</strong> Skriptobjekt aufrufen und diesem Zugriff auf<br />
das Quellobjekt und die Zielobjekte und damit deren Zustände bieten. Dies bietet den Vorteil,<br />
dass Verhalten ohne Neukompilierung des Projekts <strong>ein</strong>gebracht werden kann, was wiederum<br />
das data-driven Design unterstützt. Drei solcher Aktionsklassen sind beispielhaft implemen-<br />
<strong>Jens</strong> <strong>Pfau</strong> · Stephan Mehlhase 49
50 VI. Design und Implementierung<br />
tiert. Eine davon führt Lua Skriptcode aus (LuaScriptAction), <strong>ein</strong>e andere Tcl Skriptcode<br />
(TclScriptAction) und <strong>ein</strong>e dritte definiert ihr Verhalten im Quelltext (IncreaseHPAction).<br />
Jede von Action abgeleitete Klasse korrespondiert mit <strong>ein</strong>er konkreten Implementierung von<br />
ActionFactory. Durch diese wird sie erstellt, da das Instanziieren <strong>ein</strong>er Aktion möglicherweise<br />
<strong>ein</strong>en größeren Aufwand bedeutet. Bei der Erstellung erhält die Action von ihrer ActionFactory<br />
den Namen zugewiesen, über den sie in den Spielobjekten angesprochen wird und der <strong>für</strong> jede<br />
Art von Aktion <strong>ein</strong>deutig ist. Insbesondere kann über diesen Namen mit Hilfe der Methode<br />
hasAction() festgestellt werden, ob <strong>ein</strong> Spielobjekt <strong>ein</strong>e bestimmte Aktion ausführen kann.<br />
Das Prinzip der ActionFactory wird im weiteren Verlauf in Bezug auf die Typisierung von<br />
Spielobjekten interessant. Gleichzeitig bietet es aber auch die Möglichkeit, zur Laufzeit neue<br />
Aktionsinstanzen zu kreieren. Das nun um die oben genannten Klassen und Methoden ergänzte<br />
System ist in Abbildung 6.2 dargestellt.<br />
©posibleActions:Map ©curentActions:List +doActions() +activateAction(name:String,target:GameObject) GameObject<br />
+hasAction(name:String):bolean +slep()+unslep() State +deactivateAction(name:String) 0.*<br />
+source +target liestundändert<br />
©source:GameObject ©target:GameObject ©targetSet:Set +doAction() Action ActionFactory +getName():String 0.* +instantiate(source:GameObject):Action<br />
Abbildung 6.2: UML Diagramm der Aktionen<br />
+doAction() IncreaseHPActionFactory +instantiate(source:GameObject):IncreaseHPAction +getName():String IncreaseHPAction<br />
LuaScriptActionFactory +getName():String LuaScriptAction +doAction() +instantiate(source:GameObject):LuaScriptAction erstelt<br />
erstelt<br />
50 <strong>Jens</strong> <strong>Pfau</strong> · Stephan Mehlhase
VI. Design und Implementierung 51<br />
6.1.3 Typisierung<br />
Um das Speicheraufkommen zu verringern, sollen Spielobjekte typisiert s<strong>ein</strong> und Eigenschaften<br />
nicht komplett auf der Instanz- sondern auch auf der Typebene gehalten werden. Da sowohl<br />
Daten als auch Aktionen entkoppelt von den Spielobjekten modelliert sind, bietet es sich an,<br />
jedem Spielobjektstyp ebenfalls <strong>ein</strong>e Menge dieser Entitäten zuzuweisen, die dann von den<br />
konkreten Instanzen geerbt und möglicherweise überschrieben wird. Damit können Typen als<br />
Muster modelliert werden, was sowohl das data-driven Design und damit die Flexibilität des<br />
Systems unterstützt als auch die Änderung von Typen zur Laufzeit ohne Neukompilierung<br />
des Projekts erlaubt. Ersteres wird bei der Modellierung der Typen als Klassen nur bedingt<br />
ermöglicht, zweiteres hingegen gar nicht unterstützt.<br />
Die Klasse GameObjectType erhält somit analog zu GameObject <strong>ein</strong>e Menge von State Objekten<br />
sowie <strong>ein</strong>e Menge von ActionFactory Objekten, die jeweils über ihren Namen referenziert<br />
werden. An diesem Punkt ist die Beobachtung wichtig, dass GameObject direkt Action Objekte<br />
enthält, GameObjectType hingegen ActionFactory Objekte. Das ist dadurch bedingt, dass Aktionen,<br />
die in dem Typ <strong><strong>ein</strong>es</strong> Spielobjekts vorliegen, erst dann erstellt werden, wenn das entsprechende<br />
Spielobjekt über activateAction() aufgefordert wird, diese zu aktivieren. Eine Aktion<br />
erhält ihren Urheber als source, so dass die Instanzen <strong><strong>ein</strong>es</strong> bestimmten GameObjectType unterschiedliche<br />
Instanzen <strong>ein</strong>er Aktion erhalten müssen. Deshalb wird diese bei Bedarf erstellt.<br />
In Bezug auf die Behandlung der State Objekte ist ebenfalls <strong>ein</strong>e Anpassung nötig, denn die<br />
Bereitstellung derselben durch <strong>ein</strong> Spielobjekt geschieht nun sowohl durch dieses selber als auch<br />
möglicherweise s<strong>ein</strong>en Typ, wenn das Zustandsobjekt nicht direkt im Spielobjekt enthalten ist.<br />
In letzterem Fall ist sicherzustellen, dass bei <strong>ein</strong>er Änderung des Zustandsobjekts dessen neuer<br />
Wert nicht in das Zustandsobjekt des Typs sondern in <strong><strong>ein</strong>es</strong> des Spielobjekts geschrieben wird,<br />
denn ansonsten würden damit alle Instanzen dieses Typs geändert werden. Dieses Problem wird<br />
folgendermaßen gelöst. Kann <strong>ein</strong>e Anfrage nach <strong>ein</strong>em State nicht innerhalb <strong><strong>ein</strong>es</strong> Spielobjekts<br />
aber durch dessen Typ beantwortet werden, wird mit Hilfe der Methode pack der Klasse State<br />
<strong>ein</strong> neues Zustandsobjekt erstellt, welches <strong>ein</strong>e Kopie des durch den Typ zurückgelieferten ist<br />
und <strong>ein</strong>en Verweis auf dieses erhält (internalState). Das neue Objekt wird in die Liste der<br />
Zustände des Spielobjekts <strong>ein</strong>getragen. Solange nur Lesend auf dieses zugegriffen wird, liefert es<br />
den Wert von internalState. Bei der ersten Modifzierung wird der Verweis auf den internen<br />
Zustand gelöscht, so dass das State Objekt eigenständig wird.<br />
Um diese Funktionalität bereitstellen zu können, erhält die Klasse GameObjectType <strong>ein</strong>e Methode<br />
getStateForGameObject, die <strong>ein</strong> solches gepacktes Zustandsobjekt zurückliefert.<br />
GameObject referenziert s<strong>ein</strong>en Typ und reicht jede Anfrage an State oder Action Objekte,<br />
die es nicht selber auflösen kann, an diesen weiter. Um das System noch effizienter zu gestalten,<br />
können GameObjectType Objekte <strong>ein</strong>e Referenz auf <strong>ein</strong> weiteres GameObjectType Objekt halten,<br />
um so <strong>ein</strong>e Vererbungshierarchie aufzubauen. Andersherum erhält jedes dieser Objekte <strong>ein</strong>en<br />
Verweis auf die von ihm erbenden Typen. Letzteres wird später bei der Verwaltung der Hierarchie<br />
benötigt. Die eben genannten Anfragen an den Typ <strong><strong>ein</strong>es</strong> Spielobjekts werden die Hierarchie<br />
hochpropagiert, bis sie erfüllt werden können oder am Wurzelknoten die Unerfüllbarkeit derselben<br />
festgestellt wird. Die Ergebnisse dieser Anfragen werden nicht zwischengespeichert, sondern<br />
im Falle der State Objekte mit der oben beschriebenen Methode der gepackten Zustandsobjekte<br />
gelöst. Dadurch wirken sich auch noch zur Laufzeit Änderungen der Typen auf deren Instanzen<br />
<strong>Jens</strong> <strong>Pfau</strong> · Stephan Mehlhase 51
52 VI. Design und Implementierung<br />
aus. Insbesondere können zur Laufzeit die Typhierarchie modifiziert und die Typen <strong>ein</strong>zelner<br />
Spielobjekte ausgetauscht werden. Um das zu ermöglichen, wird auch bei der Anfrage nach <strong>ein</strong>em<br />
Zustandsobjekt an <strong>ein</strong>en Typ das Ergebnis gepackt und in der Liste der Zustandsobjekte<br />
des befragten Typs abgelegt.<br />
Natürlich ist dennoch die Möglichkeit gegeben, nur <strong>ein</strong>e <strong>ein</strong>stufige Hierarchie zu erstellen, die<br />
die nötigen zusätzlichen Laufzeitkosten, die durch das erwähnte Propagieren der Anfragen entstehen,<br />
eliminiert.<br />
Da die Mehrfachvererbung <strong>ein</strong>en größeren Verwaltungsaufwand mit sich bringt, wird in diesem<br />
System nur die Einfachvererbung umgesetzt.<br />
Ebenso wie Spielobjekte lassen sich Typen mit Hilfe des data-driven Design in externen Datenquellen<br />
auf sehr ähnliche Weise definieren, denn Typen sind kaum etwas anderes als Spielobjekte.<br />
Die aus dieser <strong>Entwicklung</strong> entstehenden Klassen ergehen aus Abbildung 6.3. +parent<br />
6.1.4 Anordnung<br />
parent:GameObjectType children:List +setParent(type:GameObjectType) +getParent():GameObjectType GameObjectType<br />
+removeChild(child:GameObjectType) +getState(name:String):State +getStateForGameObject(name:String):State +adChild(child:GameObjectType)<br />
+type<br />
type:GameObjectType +setType(type:GameObjectType) GameObject +getType():GameObjectType<br />
ActionFactory 0.*<br />
+pack():State internalState:State State<br />
Abbildung 6.3: UML Diagramm der Typen<br />
0.*0.*+internalState<br />
Unter den in Kapitel 3.1.4 vorgestellten Methoden zur Anordnung der Spielobjekte ist die der<br />
hierarchischen Ordnung die flexibelste, denn mit Hilfe dieser ließe sich auch <strong>ein</strong>e logische Gruppierung<br />
umsetzen und natürlich auch <strong>ein</strong>e nichtgeordnete Menge von Spielobjekten simulieren.<br />
Deswegen sollen in diesem System die Objekte in <strong>ein</strong>er Baumstruktur angeordnet werden, die<br />
52 <strong>Jens</strong> <strong>Pfau</strong> · Stephan Mehlhase
VI. Design und Implementierung 53<br />
<strong>ein</strong>e hierarchische Ordnung definiert. Der Verwaltungsaufwand <strong>für</strong> <strong>ein</strong>en Graphen, gerade was<br />
das Behandeln von Zyklen betrifft, ist hoch. Daher wurde diese Option nicht weiter betrachtet.<br />
Zunächst wird die Klasse GameObject, deren Instanzen also wiederum mehrere Objekte des<br />
gleichen Typs enthalten können sollen, mit Methoden ausgestattet, die das Anlegen <strong>ein</strong>er solchen<br />
Baumstruktur erlauben. Dies sind die folgenden:<br />
• addChild(GameObject)<br />
• addChild(GameObject, Integer)<br />
• removeChild(GameObject)<br />
• setParent(GameObject)<br />
• getParent(GameObject)<br />
addChild kann neben dem hinzuzufügenden Spielobjekt <strong>ein</strong>en weiteren Integer-Parameter<br />
entgegennehmen, der bestimmt, an welcher Stelle in der Liste der Kinder des Vaterobjekts<br />
dieses Spielobjekt <strong>ein</strong>gefügt werden soll, falls die Reihenfolge von Belang ist. Die<br />
setParent(GameObject) Methode führt k<strong>ein</strong>e Überprüfung nach Inkonsistenzen wie entstehende<br />
Zyklen durch, so dass diese Aufgabe aussenstehenden Entitäten zufällt.<br />
Damit <strong>ein</strong> <strong>ein</strong>deutiger Zugriff auf Spielobjekte möglich ist, erhält GameObject <strong>ein</strong>e ID sowie<br />
die zugehörige Zugriffsmethode getId(). Die ID kann entweder bei der Konstruktion <strong><strong>ein</strong>es</strong> Objekts<br />
gesetzt werden oder wird automatisch mit Hilfe der statischen Methode getUniqueId()<br />
bestimmt. Letztere gewährt den Zugriff auf <strong>ein</strong>en fortlaufenden Zähler <strong>für</strong> die Objekte, die<br />
ohne vom Entwickler vorgegebene ID instanziiert wurden. Die ebenfalls statische Methode<br />
setUniqueId(long) setzt den Zähler auf das Maximum des übergebenen Parameters und des<br />
aktuellen Zählerwerts.<br />
Diese Methode könnte von <strong>ein</strong>er Komponente genutzt werden, die den Zähler über mehrere<br />
Objektbäume hinweg synchronisiert und somit die systemweite Eindeutigkeit der IDs gewährleistet.<br />
Dabei könnte diese zum Beispiel auf Multicastalgorithmen zurückgreifen, die eben dieses<br />
<strong>für</strong> die IDs zu versendender Nachrichten in <strong>ein</strong>em Netzwerk sicherstellen [Mühlhäuser, 2005].<br />
Ebenso wird jedem Spielobjekttyp bei der Konstruktion <strong>ein</strong> <strong>ein</strong>deutiger Bezeichner zugewiesen,<br />
GameObjectType implementiert daher die gleichen Methoden und Mechanismen.<br />
Daraus folgend ergibt sich die in Abbildung 6.4 gezeigte Modellierung.<br />
Der Baum der Spielobjekte und die Hierarchie der Typen sollen über <strong>ein</strong>e gem<strong>ein</strong>same Schnittstelle<br />
verwaltet werden. Diese wird durch das Interface TreeManager definiert. Zunächst bietet<br />
dieses die Methoden get() und getType(), um Zugriff auf das durch die ID referenzierte<br />
Spielobjekt oder den Typ zu erhalten. Desweiteren deklariert das Interface Methoden<br />
zum Einfügen (insert(String, GameObject) und insertType(String, GameObjectType))<br />
und Löschen (remove(String) und removeType(String)) von Entitäten sowie zum Ersetzen<br />
(replace(String, GameObject)) <strong><strong>ein</strong>es</strong> Teilbaums der Spielobjektstruktur.<br />
Das Spielobjekt wird informiert, wenn es aus dem Baum entfernt wird. Dies erlaubt es, zu diesem<br />
Zeitpunkt <strong>ein</strong>e mit Hilfe der Methode setDeleteAction() definierte Aktion auszuführen. Das<br />
<strong>Jens</strong> <strong>Pfau</strong> · Stephan Mehlhase 53
54 VI. Design und Implementierung<br />
id:String uniqueId:long+getId():String +setUniqueId(uniqueId:long) +getUniqueId():long GameObjectType<br />
id:String +parent type<br />
GameObject<br />
Abbildung 6.4: UML Diagramm der Anordnung von Objekten<br />
parent:GameObject children:List type:GameObjectType +getId():String +setUniqueId(uniqueId:long) uniqueId:long<br />
+adChild(child:GameObject) +adChild(child:GameObject,position:int) +removeChild(child:GameObject) +setParent(parent:GameObject) +getParent():GameObject +getUniqueId():long<br />
ermöglicht dem Spieldesigner, auf das Entfernen <strong><strong>ein</strong>es</strong> Objektes zu reagieren. Diese Aktion wird<br />
allerdings nur <strong>ein</strong>mal angestoßen und existiert nicht über mehrere Spieliterationen hinweg.<br />
Zur Verbesserung der Leistung der genannten Mechanismen – denn das Suchen nach <strong>ein</strong>em<br />
Objekt kann hohe Laufzeitkosten bedeuten – wird <strong>ein</strong>e Schnittstelle <strong>für</strong> <strong>ein</strong>en Zwischenspeicher<br />
(TreeCache) <strong>für</strong> Spielobjekte definiert, dessen konkrete Implementierungen von Realisierungen<br />
des TreeManager Interfaces genutzt werden können. Dieses definiert Methoden, um Spielobjekte<br />
in den Cache zu legen (put(GameObject)), solche zu entfernen (remove(String)) und auf<br />
diese zuzugreifen (get(String) und has(String)). Diese Komponente bietet die Möglichkeit,<br />
auf häufig frequentierte Spielobjekte schneller zuzugreifen, da das Durchsuchen des gesamten<br />
Baums teilweise erspart bleibt.<br />
Beispielimplementierungen dieser Interfaces werden in Kapitel 7.1 vorgestellt. Die nächste Stufe<br />
der Modellierung lässt sich aus Abbildung 6.5 entnehmen.<br />
Dadurch, dass sowohl die Baumverwaltung als auch der Cache als Interfaces definiert sind, lassen<br />
diese sich beliebig austauschen und das System somit flexibel erweitern.<br />
6.2 Dynamik zur Laufzeit<br />
Wie wir in den vorherigen Sektionen gesehen haben, können Spielobjekte, Aktionen und Typen<br />
data-driven definiert s<strong>ein</strong>. Somit lassen sich diese zur Laufzeit nachladen, indem <strong>ein</strong>fach<br />
Instanzen der entsprechenden Klassen erstellt werden. Für diese Funktionalität bedarf es k<strong>ein</strong>er<br />
weiteren Implementierung.<br />
54 <strong>Jens</strong> <strong>Pfau</strong> · Stephan Mehlhase
VI. Design und Implementierung 55<br />
TreManager +get(id:String):GameObject +getType(id:String):GameObjectType «interface»<br />
+insert(parent:String,obj:GameObject)<br />
TreCache +get(id:String):GameObject +put(obj:GameObject) verwendet «interface»<br />
+remove(id:String) +has(id:String):bolean +insert(parent:String,obj:GameObject,position:int) +insertType(parent:String,type:GameObjectType) +remove(id:String) +removeType(id:String) +replace(what:String,with:GameObject)<br />
0.* 0.* GameObjectType +setDeleteAction(action:Action) GameObject<br />
Abbildung 6.5: UML Diagramm des TreeManager 0.*<br />
In der vorgestellten Modellierung werden Abfragen in der Vererbungshierarchie der Typen nicht<br />
zwischengespeichert. Das heißt, Typen von Spielobjekten lassen sich zur Laufzeit austauschen<br />
sowie ändern, so dass sich damit auch die Eigenschaften der Spielobjekte <strong>für</strong> diese Typen ändern.<br />
Nach der Taxonomie bietet das System in diesem Punkt also die größtmögliche Flexibilität.<br />
6.3 Kommunikation<br />
In den folgenden Sektionen wird zum <strong>ein</strong>en die Möglichkeit entwickelt, mehrere Instanzen des<br />
Systems, die in unterschiedlichen Prozessen laufen, zu synchronisieren, zum anderen werden<br />
weiteren Subsystemen <strong><strong>ein</strong>es</strong> Spiels Schnittstellen zur Kommunikation mit dem Objektmodell<br />
angeboten.<br />
6.3.1 Initiative<br />
In Kapitel 3.3.1 wurden zwei Wege <strong>ein</strong>geführt, mit denen modelliert werden kann, bei welchem<br />
Kommunikationsteilnehmer die Initiative zum Austausch von Änderungen liegt. Zum <strong>ein</strong>en ist<br />
dies das Prinzip des Polling, zum anderen die Publish/Subscribe Methode. Letztere reduziert<br />
den Nachrichtenverkehr auf das Nötige und bietet somit vor allem in Hinblick auf die aufgestellten<br />
Anforderungen zur Netzwerkkommunikation, die <strong>ein</strong>e geringe Auslastung der Bandbreite<br />
fordern, die geeignetere Umsetzung.<br />
Die Träger von Informationen, die <strong>für</strong> andere Subsysteme oder Netzwerkteilnehmer von Interesse<br />
ersch<strong>ein</strong>en, sind in diesem System zum <strong>ein</strong>en natürlich die Spielobjekte, zum anderen deren<br />
<strong>Jens</strong> <strong>Pfau</strong> · Stephan Mehlhase 55
56 VI. Design und Implementierung<br />
Typen. Dabei können grundsätzlich jeweils drei unterschiedliche Nachrichten <strong>für</strong> jede Hierarchie<br />
auftreten: Das Hinzufügen, Löschen sowie Modifizieren <strong><strong>ein</strong>es</strong> Objekts. Weiterhin könnten<br />
Komponenten an der Ausführung von Aktionen interessiert s<strong>ein</strong>. Es können also genau diese<br />
Nachrichten empfangen werden. Hierzu wird die Schnittstelle GameObserver festgelegt, welche<br />
von Interessenten implementiert werden muss und die genannten Methoden definiert, die jeweils<br />
als Parameter die betroffenen Objekte entgegennehmen:<br />
• onAdd(GameObject)<br />
• onChange(GameObject)<br />
• onDelete(GameObject)<br />
• onAdd(GameObjectType)<br />
• onChange(GameObjectType)<br />
• onDelete(GameObjectType)<br />
• onActionApplied(GameObject, Action)<br />
Die Frage, die zu klären bleibt, ist, wie die Objekte der Klassen, die GameObserver implementieren,<br />
ihr Interesse an Änderungen anmelden und wann und wie solche Änderungen propagiert<br />
werden.<br />
Sowohl innerhalb der Objekte des GameObject als auch des GameObjectType werden an Änderungen<br />
des spezifischen Objekts interessierte Entitäten in <strong>ein</strong>er Liste gehalten, die mit Hilfe<br />
der Methoden addObserver(GameObserver) und removeObserver(GameObserver) manipuliert<br />
werden kann.<br />
Für die Spielobjekte gilt, dass erstere den übergebenen GameObserver nicht nur in die Liste des<br />
aufgerufenen Spielobjekts sondern auch die s<strong>ein</strong>er Kinder <strong>ein</strong>fügt. Analog entfernt zweitere den<br />
Beobachter aus der eigenen Liste und denen der Kinder. Wenn explizit nur die Liste des angesprochenen<br />
Objekts angepasst werden soll, können die Methoden addObserverOnlyThisNode()<br />
und removeObserverOnlyThisNode() genutzt werden.<br />
In der Klasse GameObject werden folgende Elemente auf Änderungen überwacht:<br />
• Die Liste der Kinder<br />
• Die Liste der ausführbaren Aktionen<br />
• Die Liste der zur Zeit ausgeführten Aktionen<br />
• Die Liste Zustandsobjekte und deren Werte<br />
• Der Verweis auf den Vaterknoten<br />
• Der Verweis auf den Typ<br />
56 <strong>Jens</strong> <strong>Pfau</strong> · Stephan Mehlhase
VI. Design und Implementierung 57<br />
Jede Modifikation der genannten Entitäten hat <strong>ein</strong>en oder mehrere Aufrufe der internen Methoden<br />
objectAdded(), objectDeleted(), objectChanged() und actionApplied() zur Folge.<br />
Letztere wird ausgeführt, wenn <strong>ein</strong>e Aktion aus der Liste der ausführbaren in die Liste der ausgeführten<br />
Aktionen verschoben wird.<br />
Diese Methoden wiederum rufen ihren Gegenpart in dem GameObserver Interface ihrer Beobachter<br />
auf. Das Element, welches geändert wurde, wird mit Hilfe <strong><strong>ein</strong>es</strong> sogenannten Dirty Flags<br />
markiert.<br />
Über die Änderungen an den Werten s<strong>ein</strong>er Zustandsobjekte wiederum wird <strong>ein</strong> Spielobjekt mit<br />
Hilfe des Java-eigenen Observer-Mechanismus informiert. Dazu implementiert GameObject das<br />
Interface Observer und State erbt von der abstrakten Klasse Observable.<br />
Mit Hilfe der in Kapitel 6.1.2 vorgestellten Methode sleep() kann verhindert werden, dass <strong>ein</strong><br />
Spielobjekt Nachrichten an s<strong>ein</strong>e Beobachter sendet. Dieser Zustand wird durch <strong>ein</strong>en Aufruf<br />
von unsleep() wieder aufgehoben.<br />
Analog dazu in Bezug auf die Verwaltung der Beobachter verhält sich GameObjectType, das<br />
hingegen nur folgende s<strong>ein</strong>er Elemente auf Modifikationen zu beobachten hat:<br />
• Den Verweis auf den Vaterknoten in der Vererbungshierarchie<br />
• Die Liste der Kinder<br />
• Die Liste der Zustandsobjekte und deren Werte<br />
• Die Liste der ausführbaren Aktionen<br />
Basierend auf den Annahmen, dass Typen nur selten geändert werden und <strong>ein</strong> Beobachter nur<br />
an der gesamten Typhierarchie interessiert ist, wird gegenüber der Beobachtung des Spielobjektbaums<br />
<strong>ein</strong>e Ver<strong>ein</strong>fachung vorgenommen. Bei der Änderung <strong><strong>ein</strong>es</strong> Typs wird dies intern bis zum<br />
Wurzelknoten hochpropagiert, wobei der geänderte Typ mitgereicht wird. So braucht <strong>ein</strong> interessierter<br />
Beobachter lediglich den Wurzelknoten zu beobachten und die Verwaltung der Beobachter<br />
in dieser Hierarchie ist weniger aufwändig. Eine Unterscheidung zwischen der bloßen Beobachtung<br />
<strong><strong>ein</strong>es</strong> Einzelknotens und der Beobachtung <strong><strong>ein</strong>es</strong> Knotens und s<strong>ein</strong>er Kinder, wie sie <strong>für</strong> die<br />
Spielobjekthierarchie umgesetzt ist, wird hier überflüssig. Damit enfällt <strong>für</strong> GameObjectType<br />
die Unterscheidung zwischen dem Hinzufügen <strong><strong>ein</strong>es</strong> Beobachters zu <strong>ein</strong>em <strong>ein</strong>zelnen Knoten<br />
der Hierarchie oder <strong>ein</strong>em ganzen Teilbaum. Aufrufe von addObserver(GameObserver) oder<br />
removeObserver(GameObserver) werden bis zum Wurzelknoten durchgereicht und greifen nur<br />
auf dessen Beobachterliste zu.<br />
Obwohl die Klassen GameObject und GameObjectType damit <strong>ein</strong>e sch<strong>ein</strong>bar nahezu identische<br />
Funktionalität implementieren, wird auf die Auslagerung in <strong>ein</strong>e gem<strong>ein</strong>same Oberklasse verzichtet,<br />
denn das Verhalten der Methoden zum Hinzufügen und Entfernen von Objekten zu den<br />
Beobachterlisten zwischen den beiden Klassen ist zu unterschiedlich, als dass <strong>ein</strong>e gem<strong>ein</strong>same<br />
Basis gefunden werden könnte.<br />
Zudem hätte die Auslagerung dieser Funktionalität in <strong>ein</strong>e Oberklasse die Folge, dass die Methoden<br />
des Interfaces GameObserver nicht mehr Objekte der exakten Laufzeitklasse empfangen<br />
könnten, sondern nur Objekte des Typs dieser Oberklasse. Damit würden zur Laufzeit teure<br />
<strong>Jens</strong> <strong>Pfau</strong> · Stephan Mehlhase 57
58 VI. Design und Implementierung<br />
Typüberprüfungen nötig werden. Eine Möglichkeit, dies zu umgehen, wäre der Einsatz des Visitor<br />
Entwurfmusters [Gamma, 1997], so dass <strong>ein</strong>e Implementierung des GameObserver zunächst<br />
nur die Typen der Oberklasse entgegennimmt, dann aber die gegebenen Objekte besucht und<br />
somit die Laufzeitklasse derselben erfährt. Auch diese Umsetzung wird nicht verfolgt, weil damit<br />
GameObserver konkretes Verhalten implementieren müsste und nicht mehr als Interface sondern<br />
nur noch als abstrakte Klasse definiert werden könnte, was die Integration in bestehende Projekte<br />
erschwert. Auch hier bestünde weiterhin das Problem <strong>ein</strong>er fehlenden Schnittmenge gleichen<br />
gem<strong>ein</strong>samen Verhaltens der beiden Klassen. Infolgedessen wird auch auf <strong>ein</strong>e Implementierung<br />
mit Hilfe des Java-eigenen Observer-Mechanimus verzichtet.<br />
Damit ergibt sich die in Abbildung 6.6 dargestellte Modellierung des Observermechanismus.<br />
«interface»<br />
GameObserver +onAd(obj:GameObject) +onChange(obj:GameObject) +onDelete(obj:GameObject) +onAd(type:GameObjectType) +onChange(type:GameObjectType) +onDelete(type:GameObjectType) +onActionAplied(obj:GameObject,action:Action)<br />
0.* benachrichtigt 0.* 0.* +update(o:Observable,obj:Object) «interface»Observer<br />
Observable<br />
observers:List +removeObserver(o:GameObserver) +adObserverOnlyThisNode(o:GameObserver) GameObject<br />
+removeObserverOnlyThisNode(o:GameObserver) +adObserver(o:GameObserver)<br />
GameObjectType +removeObserver(o:GameObserver) observers:List +adObserver(o:Observer) +removeObserver(o:Observer) benachrichtigt<br />
+adObserver(o:GameObserver) benachrichtigt<br />
benachrichtigt State<br />
Abbildung 6.6: UML Diagramm des Observermechanismus<br />
58 <strong>Jens</strong> <strong>Pfau</strong> · Stephan Mehlhase
VI. Design und Implementierung 59<br />
6.3.2 Änderungsübermittlung<br />
Um den <strong>flexiblen</strong> Einsatz des Systems zu gewährleisten, soll dieses beide der in Kapitel 3.3.2<br />
vorgestellten Methoden zur Übermittlung von Änderungen implementieren. Die Grundlage hier<strong>für</strong><br />
wurde bereits in der vorherigen Sektion gelegt. Das Interface GameObserver beschreibt <strong>ein</strong>e<br />
Komponente, die zum <strong>ein</strong>en Nachrichten über die Änderung von Spielobjekten und deren Typen<br />
sowie über die Ausführung von Aktionen empfängt. Der nächste Schritt ist, diese <strong>für</strong> den Versand<br />
zu anderen Prozessen aufzubereiten. Dazu sollen zunächst nur die Daten herausgefiltert werden,<br />
die <strong>ein</strong>er Synchronisierung bedürfen, um Ressourcen wie vor allem die Netzwerkbrandbreite zu<br />
schonen.<br />
Basierend auf den vorgestellten Dirty Flags definieren die beiden Klassen GameObject und<br />
GameObjectType die Methoden isDirty() und getDelta(). Während erstere überprüft, ob innerhalb<br />
<strong><strong>ein</strong>es</strong> Objekts Änderungen vorliegen, gibt zweitere <strong>ein</strong>e Instanz der Klasse DeltaObject<br />
beziehungsweise DeltaType zurück, die genau diese Änderungen b<strong>ein</strong>haltet. Nachdem <strong>ein</strong> solches<br />
Objekt, das die Differenz zweier Zustände <strong><strong>ein</strong>es</strong> Spielobjekts oder Typs beschreibt, extrahiert<br />
wurde, können die Dirty Flags des Ausgangsobjekts mit Hilfe der Methode makeClean()<br />
zurückgesetzt werden. DeltaObject enthält die folgenden Felder:<br />
• Die ID des Objekts<br />
• Die zur Zeit größte <strong>für</strong> Spielobjekte automatisch vergebene ID<br />
• Die Liste der Kinder<br />
• Die Liste der ausführbaren Aktionen<br />
• Die Liste der zur Zeit ausgeführten Aktionen<br />
• Die Liste der Zustandsobjekte<br />
• Den Verweis auf den Typ des Objekts<br />
• Den Verweis auf den Vaterknoten in der Spielobjekthierarchie<br />
DeltaObjectType enthält die folgenden Felder:<br />
• Die ID des Typs<br />
• Die zur Zeit größte <strong>für</strong> Typen automatisch vergebene ID<br />
• Den Verweis auf den Vaterknoten in der Vererbungshierarchie<br />
• Die Liste der Kinder in der Vererbungshierarchie<br />
• Die Liste der Zustandsobjekte<br />
• Die Liste der ausführbaren Aktionen<br />
<strong>Jens</strong> <strong>Pfau</strong> · Stephan Mehlhase 59
60 VI. Design und Implementierung<br />
+isDirty():bolean +getDelta():DeltaObject GameObject<br />
GameObjectType +getDelta():DeltaType +makeClean() +isDirty():bolean<br />
erstelt erstelt parentparent +makeClean() parent type<br />
children:List actions:List DeltaType id:String DeltaObject uniqueId:long<br />
+setId(id:String) +getUniqueId():long states:List id:String uniqueId:long children:List posibleActions:List curentActions:List states:List parent:GameObject type:GameObjectType +getId():String +setId(id:String) parent:GameObjectType +getId():String<br />
+setUniqueId(uniqueId:long) +getUniqueId():long +setUniqueId(uniqueId:long) etali etali<br />
Abbildung 6.7: UML Diagramm der Deltaobjekte<br />
Damit ergibt sich die in Abbildung 6.7 gezeigte Klassenmodellierung.<br />
Bevor Instanzen der Klassen GameObject, GameObjectType, DeltaObject und DeltaType sowie<br />
Aktionen mit anderen Subsystemen oder sogar Prozessen ausgetauscht werden können, bedarf<br />
es der Überführung in <strong>ein</strong>e geeignete Repräsentation und der Rückgewinnung der Daten aus<br />
derselben. Während der Versand über das Netzwerk auf <strong>ein</strong>en Bytestrom angewiesen ist, wird<br />
<strong>ein</strong>e Datenbank üblicherweise mit SQL-Befehlen angesprochen. Oft ist <strong>ein</strong>e direkte Umsetzung<br />
von der objektorientierten Darstellung in <strong>ein</strong>e andere nicht möglich und es benötigt mehrerer<br />
Zwischenschritte <strong>für</strong> diese Aufgabe. Der in Kapitel 5.3 angedeutete Ansatz der Filter soll von<br />
dem System in <strong>ein</strong>er flexibleren Form umgesetzt werden. Dazu wird <strong>ein</strong>e Reihe von Schnittstellen<br />
definiert, die sich in die Kategorien Filter und Transformatoren unterteilen lassen. Erstere<br />
markieren Klassen, die Daten in <strong>ein</strong>er bestimmten Repräsentation entgegennehmen, zweitere<br />
Klassen, die Daten in <strong>ein</strong>e bestimmte Repräsentation überführen. Zum Beispiel wird <strong>ein</strong><br />
StringFilter definiert, der <strong>ein</strong>e Methode filter(String) anbietet, sowie <strong>ein</strong> Transformator<br />
StringTransformator, der die Methode set(StringFilter) deklariert. Während ersterer also<br />
<strong>ein</strong>e Zeichenkette entgegennimmt, wird zweiter <strong>ein</strong>e Zeichenkette liefern und an <strong>ein</strong>en weiteren<br />
passenden Filter übergeben. Weitere vorgegebene Filter und Transformatoren sind zum Beispiel:<br />
ByteFilter: Übernimmt <strong>ein</strong>en Bytestrom.<br />
60 <strong>Jens</strong> <strong>Pfau</strong> · Stephan Mehlhase
VI. Design und Implementierung 61<br />
DeltaFilter: Übernimmt DeltaOject und DeltaType Objekte in add(), change() und<br />
delete() Methoden sowie Aktionen. Durch die Unterscheidung der Methoden lässt sich<br />
die Art der Nachricht in der neuen Repräsentation konservieren.<br />
GameObjectFilter: Übernimmt GameObject und GameObjectType Objekte.<br />
ByteTransformator: Liefert <strong>ein</strong>en Bytestrom und damit den Eingang <strong>für</strong> <strong>ein</strong>en ByteFilter.<br />
DeltaTransformator: Liefert den Eingang <strong>für</strong> <strong>ein</strong>en DeltaFilter.<br />
GameObjectTransformator: Liefert den Eingang <strong>für</strong> <strong>ein</strong>en GameObjectFilter.<br />
Dabei ist auch explizit vorgesehen, dass <strong>ein</strong>e konkrete Implementierung sowohl <strong>ein</strong>en Filter<br />
als auch <strong>ein</strong>en Transformator implementieren kann. Somit ließen sich Ketten bilden, die aus<br />
der objektorientierten Repräsentation der Daten <strong>ein</strong>e andere gewinnen und umgekehrt. Durch<br />
das Definieren der Schnittstellen lassen sich Filter und Transformatoren beliebig mit<strong>ein</strong>ander<br />
verketten, so dass deren leichte Austauschbarkeit und die Erweiterbarkeit bestehender Filterund<br />
Transformatorketten gewährleistet ist. Soll das System hingegen auf die beste Leistung<br />
ausgelegt s<strong>ein</strong>, ließen sich solche Ketten natürlich auch auf <strong>ein</strong> <strong>ein</strong>ziges Glied reduzieren.<br />
Die Verwendung der Filter <strong>für</strong> unterschiedliche Zwecke wie die Netzwerkkommunikation oder<br />
Persistenz wird in Kapitel 7.2 dargestellt.<br />
6.3.3 Kompression und Quality of Service<br />
Da die beiden hier zu behandelnden Aspekte nicht direkt Teil <strong><strong>ein</strong>es</strong> <strong>Objektmodells</strong> sind, lassen<br />
sie sich nur indirekt unterstützen. Eine Implementierung derselben kann nicht gegeben werden.<br />
Dennoch liefern beide hilfreiche Ansätze zur Steuerung des Netzwerkverkehrs zu Gunsten der<br />
Bandbreite. Das System soll deshalb dem Entwickler die Möglichkeit bieten, zum <strong>ein</strong>en Daten,<br />
die versendet werden sollen, mit <strong>für</strong> die Kommunikation wichtigen Informationen zu versehen,<br />
und zum anderen, diese Informationen an geeigneter Stelle auszuwerten und zum Beispiel <strong>für</strong><br />
die Priorisierung oder Komprimierung von Nachrichten zu verwenden.<br />
Die in der vorigen Sektion dargestellten Filter und Transformatoren bezeichnen unter anderem<br />
die Schnittstelle zwischen Objektmodell und Netzwerkschicht. Dort kann entschieden werden,<br />
wann oder wie der Versand von Daten stattfindet.<br />
Die Steuerung dieser Entscheidungen soll über Eigenschaften erfolgen, die den zu versendenden<br />
Instanzen der Klassen GameObject, GameObjectType, State und Action gegeben werden.<br />
Es wird daher ähnlich zu der Klasse State <strong>ein</strong>e abstrakte Klasse Property implementiert,<br />
die Paare von Zugriffsmethoden <strong>für</strong> unterschiedliche Datentypen definiert, die standardmäßig<br />
Ausnahmen werfen und deren Instanzen von dem Container Properties b<strong>ein</strong>haltet werden.<br />
Konkrete Implementierungen von Property überschreiben die Methoden, die mit dem von ihnen<br />
realisierten Datentyp korrespondieren.<br />
Die Klassen GameObject, GameObjectType und Action erben von der Klasse Properties, um<br />
deren Funktionalität anzunehmen. State enthält <strong>ein</strong>e Instanz dieser Klasse, da es bereits von<br />
Observable erbt.<br />
Ebenso wie die Zustandsobjekte lassen sich die Eigenschaften data-driven definieren aber auch<br />
zur Laufzeit modifizieren. Folgende Datentypen werden unterstützt:<br />
<strong>Jens</strong> <strong>Pfau</strong> · Stephan Mehlhase 61
62 VI. Design und Implementierung<br />
Integer: <strong>ein</strong> ganzzahliger Wert<br />
Double: <strong>ein</strong>e Gleitkommazahl<br />
Boolean: <strong>ein</strong> Bool’scher Wert<br />
String: <strong>ein</strong>e Zeichenkette<br />
Es ist explizit vorgesehen, dass diese Eigenschaften nur lokal existieren und nicht wie die State<br />
Objekte über Prozesse hinweg synchronisiert werden. So kann jeder Teilnehmer s<strong>ein</strong>e eigene<br />
Netzwerksteuerung implementieren. Zum Beispiel könnten Aktionen mit <strong>ein</strong>er Integer-Property<br />
versehen werden, die deren Priorität bei der Versendung über das Netzwerk bestimmt. Ein<br />
entsprechender Filter könnte dann da<strong>für</strong> Sorge tragen, dass zunächst dringliche Aktionen zum<br />
Beispiel vor Chatnachrichten versendet werden. Damit ergibt sich die in Abbildung 6.8 gezeigte<br />
Modellierung. Properties 0.* Property<br />
6.4 Überblick<br />
values:Map<br />
+setAsInt(value:int) +setAsDouble(value:double)etali +setProperty(name:String,p:Property) +getProperty(name:String):Property +getAsInt():int +getAsDouble():double<br />
Action GameObject GameObjectType<br />
+setProperty(name:String,p:Property) +getProperty(name:String):Property props:Properties State<br />
IntProperty<br />
+props<br />
value:int+getAsInt():int<br />
value:double +setAsInt(value:int)<br />
+getAsDouble():double DoubleProperty<br />
Abbildung 6.8: UML Diagramm der Property Objekte +setAsDouble(value:double)<br />
In diesem Kapitel wurde <strong>ein</strong> Objektmodell entworfen, das auf <strong>ein</strong>er Baumstruktur der Spielobjekte<br />
basiert. Diese lässt sich über <strong>ein</strong>e <strong>ein</strong>zelne Schnittstelle verwalten. Spielobjekte können<br />
typisiert s<strong>ein</strong> und durch als Komponenten modellierte Aktionen mit Verhalten versehen werden.<br />
Diese drei Entitäten sind komplett in externen Datenquellen definierbar, so dass Spieldesigner<br />
unabhängig von den Programmierern arbeiten können.<br />
Mit Hilfe der Filter und Transformatoren kann sowohl <strong>ein</strong>e Persistenzschnittstelle implementiert<br />
62 <strong>Jens</strong> <strong>Pfau</strong> · Stephan Mehlhase
VI. Design und Implementierung 63<br />
als auch <strong>ein</strong>e Brücke zu der Netzwerkschicht geschlagen werden. Es ist vorstellbar, dass mit Hilfe<br />
dieses Prinzips auch weitere Subsysteme <strong><strong>ein</strong>es</strong> Spiels mit dem Objektmodell kommunizieren.<br />
Änderungen des Systems werden durch das Observer Entwurfsmuster propagiert, welches nur<br />
die nötigen Nachrichten verschickt und damit den Overhead reduziert.<br />
Zur Steigerung der Leistung werden unterschiedliche Mechanismen angeboten. Hier seien beispielhaft<br />
noch <strong>ein</strong>mal der TreeCache und die Properties zur Steuerung des Netzwerkverkehrs<br />
genannt.<br />
Durch das Implementieren gegen Schnittstellen lassen sich weite Teile des Systems austauschen.<br />
Damit wird die Erweiterbarkeit und Wiederverwendbarkeit desselben unterstützt.<br />
Abbildung 6.9 liefert <strong>ein</strong>en Überblick des Gesamtsystems, aus Abbildung 6.10 ergeht die Einordnung<br />
in die Taxonomie.<br />
<strong>Jens</strong> <strong>Pfau</strong> · Stephan Mehlhase 63
64 VI. Design und Implementierung<br />
TreCache TreManager<br />
State liestundändert<br />
Action erbtvon GameObjectType GameObject<br />
erstelt erstelt benachrichtigen wird<strong>ein</strong>gepflegtin wird<strong>ein</strong>gepflegtin<br />
DeltaType DeltaObject GameObserver<br />
Abbildung 6.9: Überblick über die entwickelte Architektur<br />
werden<strong>ein</strong>gespeistin erstelt<br />
*Transformator *Filter<br />
64 <strong>Jens</strong> <strong>Pfau</strong> · Stephan Mehlhase
VI. Design und Implementierung 65<br />
Taxonomie<br />
Objektstruktur<br />
Daten<br />
Modellierung<br />
Dynamische Zustände<br />
Definition<br />
Quelltext<br />
Textdateien<br />
Binärdateien<br />
XML Dateien<br />
Skriptsprache<br />
Datenbanken<br />
Verhalten<br />
Modellierung<br />
als Komponenten<br />
Definition<br />
Quelltext<br />
Skriptsprache<br />
Typen<br />
Modellierung<br />
Muster<br />
Definition<br />
Quelltext<br />
data-driven<br />
Vererbung<br />
Konzept<br />
Einfach<br />
Gegenstand<br />
Daten<br />
Verhalten<br />
Objektordnung<br />
Hierarchische Ordnung<br />
Dynamik zur Laufzeit<br />
Typen nachladbar<br />
Objekte nachladbar<br />
Verhalten nachladbar<br />
Typen veränderbar<br />
Kommunikation<br />
Initiative<br />
Publish/Subscribe<br />
Änderungsübermittlung<br />
Delta Updates<br />
Aktionen<br />
Kompression<br />
Quality of Service<br />
Abbildung 6.10: Einordnung des von uns entwickelten Systems in die Taxonomie<br />
<strong>Jens</strong> <strong>Pfau</strong> · Stephan Mehlhase 65
Kapitel 7<br />
Integration<br />
I just invent, then wait until man<br />
comes around to needing what I’ve<br />
invented.<br />
R. Buckminster Fuller<br />
Im letzten Kapitel wurde das Design sowie die Implementierung beschrieben. In diesem soll<br />
nun die Frage geklärt werden, wie das entwickelte Objektmodell in <strong>ein</strong> bestehendes Projekt zu<br />
integrieren ist.<br />
Um das zu erreichen, sollte zunächst evaluiert werden, ob die gegebenen Referenzimplementierungen<br />
der Baumverwaltungsklassen ausreichend sind oder ob diese an abweichende Anforderungen<br />
angepasst werden sollten. Wie die dazugehörigen Schnittstellen derzeit implementiert<br />
sind und was zu beachten gilt, ist im Abschnitt 7.1 beschrieben. Dort wird ebenfalls erläutert,<br />
wie die Komponente, die <strong>für</strong> die Visualisierung der <strong>ein</strong>zelnen Spielobjekte zuständig ist, mit<br />
dem System interagieren kann.<br />
Nachfolgend wird dargelegt, wie mehrere Instanzen des <strong>Objektmodells</strong> mit<strong>ein</strong>ander kommunizieren<br />
können und welche Möglichkeiten sich bieten, den aktuellen Zustand des Systems persistent<br />
zu speichern sowie mit Hilfe externer Quellen die Daten des Systems zu instanziieren. Wie dies<br />
mit Hilfe der Filter zu realisieren ist, wird im Abschnitt 7.2 geklärt.<br />
Abschließend wird dann die Implementierung der Spiellogik – also das Verhalten der Spielobjekte<br />
– beschrieben. Diese arbeitet, wie im vorigen Kapitel beschrieben, nur mit Aktionen, um<br />
Spielobjekte zu verändern. Wie das im Detail geschieht, wird im Abschnitt 7.3 gezeigt.<br />
7.1 Objekthierachien<br />
Wie kann <strong>ein</strong>e Implementierung des TreeManager Interfaces aussehen?<br />
Eine Referenzimplementierung dieser Schnittstelle ist die Klasse DefaultTreeManager. Diese<br />
b<strong>ein</strong>haltet den Wurzelknoten der Objekt- als auch den der Typhierarchie sowie <strong>ein</strong>en Cache,<br />
der die Schnittstelle TreeCache implementiert. In der Annahme, dass die Spielobjekte <strong>ein</strong>er
VII. Integration 67<br />
sehr viel höheren Dynamik unterliegen als die Typen, werden in diesem nur erstere gehalten.<br />
Die DefaultTreeManager Klasse unterstützt lediglich <strong>ein</strong>e Objekt- sowie <strong>ein</strong>e Typhierarchie pro<br />
TreeManager Instanz.<br />
Die Methode zum Erhalten von Spielobjekten prüft zunächst, ob das angefragte Objekt im<br />
Cache enthalten ist. Trifft dies nicht zu, wird es mit Hilfe <strong>ein</strong>er Breitensuche gesucht. Nachdem<br />
das gewünschte Objekt gefunden wurde, wird es dem Cache hinzugefügt. Die Knoten der Objekthierarchie,<br />
die bei der Breitensuche besucht werden, aber nicht dem gesuchten entsprechen,<br />
werden nicht hinzugefügt; dies kann bei der Implementierung des Caches von Bedeutung s<strong>ein</strong>.<br />
Für Spielobjekttypen funktioniert dies analog, mit der Einschränkung, dass k<strong>ein</strong> Cache genutzt<br />
wird und somit auch k<strong>ein</strong>e Anfrage an <strong>ein</strong>en solche erfolgen muss.<br />
Beim Hinzufügen zu dem Baum muss, unabhängig davon, ob es sich um die Objekt- oder<br />
Typhierarchie handelt, sichergestellt werden, dass der <strong>ein</strong>zufügende Knoten nicht schon darin<br />
existiert. Wäre dies der Fall, würde das abermalige Hinzufügen zu Inkonsistenzen führen, da<br />
dieser dann zeitgleich in den Listen der Kinder zweier unterschiedlicher Knoten stehen würde.<br />
Deswegen wird, bevor der Knoten <strong>ein</strong>gefügt wird, kontrolliert, ob dieser oder <strong><strong>ein</strong>es</strong> s<strong>ein</strong>er<br />
Kinder bereits vorhanden ist. Diese Überprüfung läuft rekursiv, so dass alle Knoten des hinzuzufügenden<br />
Teilbaums untersucht werden. Da diese Implementierung mit Hilfe von get(String)<br />
<strong>ein</strong> Ergebnis sucht, zeigt sich, dass <strong>ein</strong>e effiziente Implementierung dieser Methode auch die<br />
Geschwindigkeit der Einfügeoperationen signifikant verbessern kann.<br />
Beim Löschen <strong><strong>ein</strong>es</strong> Knotens muss sichergestellt werden, dass nicht nur dieser entfernt wird,<br />
sondern auch dessen Kinder. Wenn <strong>ein</strong> Knoten entfernt wird, wird auf dessen Elternknoten die<br />
removeChild(GameObject) Methode ausgeführt. Durch diesen Aufruf werden die Nachrichten<br />
<strong>für</strong> die Observer generiert sowie die Verbindung zwischen Vater- und Kindknoten vollständig<br />
aufgelöst. Dieser Aufruf wird rekursiv durch den gesamten Teilbaum propagiert und löst alle<br />
Referenzen zwischen den Knoten zu<strong>ein</strong>ander.<br />
Wie kann <strong>ein</strong>e Implementierung des TreeCache Interface aussehen?<br />
Eine Referenzimplementierung dieser Schnittstelle ist die Klasse DefaultTreeCache. Sie setzt<br />
den Zwischenspeicher mit Hilfe der Java-eigenen HashMap um. Alle Objekte, die mit Hilfe<br />
der Einfügeoperation put(GameObject) zum TreeManager hinzugefügt werden, werden auch<br />
in dieser Datenstruktur abgelegt. Eine Suche mittels get(String) sowie das Löschen mittels<br />
remove(String) lassen sich dann an die Methoden der HashMap delegieren. Diese ist sehr effizient<br />
implementiert, wovon auch der Zwischenspeicher profitiert.<br />
Problematisch wird es allerdings beim Speicherbedarf. Da <strong>ein</strong> Spiel aus sehr vielen Spielobjekten<br />
bestehen kann, ist dieser <strong>ein</strong> nicht zu vernachlässigender Punkt. Eine Möglichkeit, hier<br />
Abhilfe zu schaffen und dabei trotzdem die Effizienz der HashMap zu erhalten, wäre, nur die am<br />
häufigsten abgefragten Objekte zu speichern.<br />
<strong>Jens</strong> <strong>Pfau</strong> · Stephan Mehlhase 67
68 VII. Integration<br />
Wie kann der Lebenszyklus <strong><strong>ein</strong>es</strong> Objekts modelliert werden?<br />
Wichtig <strong>für</strong> den Lebenszyklus sind in der Praxis zumeist drei Fragestellungen:<br />
• Wie kann <strong>ein</strong>e Aktion direkt nach der Erstellung <strong><strong>ein</strong>es</strong> Objekts ausgeführt werden?<br />
• Wie kann <strong>ein</strong>e Aktion direkt vor der Löschung <strong><strong>ein</strong>es</strong> Objekts ausgeführt werden?<br />
• Wie kann <strong>ein</strong> Objekt schlafen gelegt werden, so dass k<strong>ein</strong>e Aktionen mehr ausgeführt<br />
werden?<br />
Die erste Anforderung kann dadurch erfüllt werden, dass <strong>ein</strong>e Aktion vor der Einfügung des<br />
Spielobjekts in den Baum zu currentActions hinzufügt wird. Dadurch wird diese nach dem<br />
Hinzufügen durch <strong>ein</strong>en Aufruf von doActions auf dem Spielobjekt ausgeführt, was üblicherweise<br />
in der ersten nachfolgenden Iteration des Spiels geschehen würde.<br />
Die zweite Problemstellung wird von GameObject direkt unterstützt. Die mit der Methode<br />
setDeleteAction(Action) gesetzte Aktion wird beim Entfernen des Objekts aus dem Baum<br />
ausgeführt. Diese wird allerdings nur noch genau <strong>ein</strong>mal angestoßen.<br />
Auch das letzte Problem wird direkt durch die sleep() Funktion unterstützt. Sobald das Objekt<br />
in den Schlaf versetzt wurde, werden weder Aktionen ausgeführt noch irgendwelche Observernachrichten<br />
verschickt. Um das Objekt wieder zu aktivieren, wird unsleep() angeboten.<br />
Wie kann <strong>ein</strong> Spielobjekt direkt mit der Anzeigekomponente kommunizieren?<br />
Die Spielobjekte enthalten nur Daten und prinzipiell k<strong>ein</strong>erlei Informationen darüber, ob und<br />
wie sie dargestellt werden sollen. Um <strong>ein</strong> Objekt mit <strong>ein</strong>er Anzeigekomponente zu verbinden,<br />
existiert die Schnittstelle Form, die das Interface GameObserver erweitert und k<strong>ein</strong>e weiteren<br />
zusätzlichen Methoden definiert. Es handelt sich also zunächst um <strong>ein</strong> r<strong><strong>ein</strong>es</strong> Marker Interface 1 .<br />
Das GameObject bietet zwei Zugriffsmethoden, um so <strong>ein</strong> Form-Objekt mit <strong>ein</strong>em Spielobjekt<br />
zu assoziieren oder aus dem Spielobjekt zu erhalten. Beim Hinzufügen wird das Form Objekt<br />
ebenfalls als Beobachter des Spielobjekts <strong>ein</strong>getragen, um angemessen auf etwaige Änderungen<br />
dessen Zustandes reagieren zu können.<br />
Die Funktionalität der Darstellung ist stark abhängig von dem Projekt, weswegen die Schnittstelle<br />
zunächst leer ist. Wenn das Objektmodell verwendet wird, sollte dieses Interface mit<br />
weiteren Methoden gefüllt werden. Beispielsweise könnte <strong>ein</strong>e Methode render() hinzugefügt<br />
werden, die in jeder Iteration der Spielschleife auf den Form-Objekten, die aus den in der Nähe<br />
des Spielers befindlichen Spielobjekten gewonnen wurden, aufgerufen wird, um diese darzustellen.<br />
1 http://en.wikipedia.org/wiki/Marker_interface_pattern<br />
68 <strong>Jens</strong> <strong>Pfau</strong> · Stephan Mehlhase
VII. Integration 69<br />
7.2 Filter<br />
Wie kann <strong>ein</strong> Filter/Transformator implementiert werden?<br />
Ein Filter muss <strong>ein</strong>e der Filterschnittstellen implementieren. Je nach Typ hat das Interface<br />
<strong>ein</strong>e oder mehrere Funktionen, die Objekte des entsprechenden Typs entgegennehmen. Sollen<br />
die Daten, die übergeben werden, an andere Filter weitergegeben werden, sollte zusätzlich <strong>ein</strong><br />
Transformator Interface implementiert werden. Über dieses kann <strong>ein</strong> Filter erhalten werden, an<br />
den die Daten weitergereicht werden, nachdem sie bearbeitet wurden.<br />
Wie so etwas implementiert werden könnte, zeigt Listing 7.1, in dem <strong>ein</strong> Filter implementiert ist,<br />
der <strong>ein</strong>e Zeichenkette mit ROT13 2 kodiert und an den in der Variable nextFilter gespeicherten<br />
Filter weiterreicht.<br />
1 public class ROT13Filter implements StringFilter ,<br />
StringTransformer {<br />
2 private StringFilter nextFilter ;<br />
3<br />
4 public void filter ( String s) {<br />
5 StringBuffer buf = new StringBuffer (s. length ());<br />
6 for (char c : s. toCharArray ()) {<br />
7 buf.append (((c + 13) % 256));<br />
8 }<br />
9 if ( nextFilter != null) {<br />
10 nextFilter . filter (buf.toString ());<br />
11 }<br />
12 }<br />
13<br />
14 public void set( StringFilter sf) {<br />
15 nextFilter = sf;<br />
16 }<br />
17 }<br />
Listing 7.1: ROT13 Filter<br />
Wie können mehrere Filter zu <strong>ein</strong>er Filterkette verbunden werden?<br />
Wie im vorigen Abschnitt beschrieben, kann dies über die Transformatoren und deren Methode<br />
set() geschehen. Wird diese mit <strong>ein</strong>em Filter als Parameter aufgerufen, wird dieser intern<br />
gespeichert. Nachdem die eigene Filteraktion durchgeführt wurde, werden die Daten an diesen<br />
weitergegeben. Den Startpunkt der Filterkette bildet <strong>ein</strong> Filter, der nur die Transformatorschnittstelle<br />
implementiert. Den Endpunkt hingegen bildet <strong>ein</strong> Filter, der die Transformatorschnittstelle<br />
eben genau nicht implementiert.<br />
Beispielsweise müsste <strong>ein</strong> Objekt, das die über das Netzwerk empfangenen Daten <strong><strong>ein</strong>es</strong> anderen<br />
2 http://de.wikipedia.org/wiki/ROT13<br />
<strong>Jens</strong> <strong>Pfau</strong> · Stephan Mehlhase 69
70 VII. Integration<br />
Netzwerkteilnehmers an <strong>ein</strong>en Filter übergibt, nur den Transformator implementieren. Von dort<br />
aus werden die Daten durch die Filterkette gereicht, bis sie am Ende der Kette <strong>ein</strong>e Komponente<br />
erreichen, die die Daten in den Objektbaum <strong>ein</strong>pflegen könnte. Diese bräuchte nur <strong>ein</strong><br />
Filterinterface zu implementieren.<br />
Wie kann <strong>ein</strong>e Filterkette zur persistenten Speicherung,<br />
Netzwerkkommunikation und zum Laden von Spielobjekten genutzt werden?<br />
Das Beispiel im vorigen Abschnitt deutet dies bereits an. Zur Netzwerkkommunikation werden<br />
zwei Filterketten definiert, <strong>ein</strong>e <strong>für</strong> <strong>ein</strong>gehende und <strong>ein</strong>e <strong>für</strong> ausgehende Verbindungen. Bei<br />
ersterer bildet <strong>ein</strong> Empfänger den Startpunkt der Transformatorkette. Der gelesene Bytestrom<br />
wird durch die Transformatoren nach und nach erst in Strings und dann in Objekte transformiert.<br />
Diese gelangen dann zum Ende der Kette, an dem sie <strong>ein</strong> Filter in die Objekthierarchien<br />
<strong>ein</strong>pflegt.<br />
In die andere Richtung sieht es ganz ähnlich aus. Eine Änderung an der Hierarchie oder an<br />
bestimmten Teilen davon erfährt <strong>ein</strong> GameObserver, der gleichzeitig <strong>ein</strong>en Filter b<strong>ein</strong>haltet. An<br />
diesen übergibt er die Delta Updates aus dem Baum, die nun durch mehrere Transformationen<br />
in <strong>ein</strong>en Bytestrom umgewandelt werden. Das Ende der Kette bildet <strong>ein</strong>e Komponente,<br />
die in das Netzwerk sendet. Die im <strong>ein</strong>zelnen dazwischen <strong>ein</strong>gesetzten Filter sind dabei stark<br />
vom jeweiligen Projekt abhängig. Beispiele wären Filter, die den Bytestrom mit Hilfe von GZIP<br />
oder BZIP2 komprimieren, weitere, die die Daten verschlüsseln, und wieder andere, welche die<br />
Objekte in <strong>ein</strong>e XML Form oder <strong>ein</strong>e beliebige anderen Definitionssprache transformieren.<br />
Es ist denkbar, <strong>ein</strong>e Filterkette zu implementieren, die nicht Delta Updates sondern komplette<br />
Spielobjekte entgegennimmt, diese serialisiert und als Bytestrom auf <strong>ein</strong>en persistenten Speicher<br />
schreibt. Hier könnten weitere Ideen entstehen; basiert die persistente Speicherung beispielsweise<br />
auf <strong>ein</strong>em Datenbanksystem, muss der Bytestrom gar nicht erstellt werden. Es könnte <strong>ein</strong> SQL<br />
Befehl in <strong>ein</strong>em StringFilter erstellt werden, welcher direkt an die Datenbank übergeben<br />
werden könnte. Mit <strong>ein</strong>er umgekehrten Filterkette, die die Daten aus dem persistenten Speicher<br />
holt und in Spielobjekte umwandelt, ergibt sich automatisch <strong>ein</strong> Mechanismus, der das initiale<br />
Laden der Spielobjekte zu Spielbeginn erlaubt.<br />
7.3 Aktionen<br />
Wie können Aktionen <strong>ein</strong> Spielobjekt verändern?<br />
Ein Spielobjekt wird unter anderem dadurch modifiziert, dass s<strong>ein</strong>e Zustände geändert werden.<br />
Die Klasse GameObject enthält <strong>ein</strong>e Möglichkeit, um zu überprüfen, ob <strong>ein</strong> State Objekt in<br />
<strong>ein</strong>em Spielobjekt existent ist, sowie Methoden, dieses Zustandsobjekt zu ändern, zu entfernen<br />
oder <strong>ein</strong> neues hinzuzufügen. Eine Aktion beispielsweise, die die Lebenspunkte <strong><strong>ein</strong>es</strong> Charakters<br />
um 25 erhöht, inkrementiert – genauer ausgedrückt – <strong>ein</strong>en Zustand, der <strong>ein</strong>en Integer Wert enthält,<br />
um 25. Wenn dieser in unserem kl<strong>ein</strong>en Beispiel „hitpoints“ hieße, müsste zunächst geprüft<br />
70 <strong>Jens</strong> <strong>Pfau</strong> · Stephan Mehlhase
VII. Integration 71<br />
werden, ob <strong>ein</strong> State Objekt mit diesem Namen in dem Spielobjekt vorhanden ist. Falls dies<br />
gegeben ist, wird dieser aus dem Spielobjekt angefordert und s<strong>ein</strong> Wert angepasst. Dazu wird<br />
dieser mit Hilfe der passenden Zugriffsmethode extrahiert, verändert und wieder gespeichert.<br />
Da auf Objekte in Java über Referenzen verwiesen und somit direkt auf das Zustandsobjekt des<br />
Spielobjekts zugegriffen wird, ist <strong>ein</strong> erneutes explizites Setzen des Zustands nicht nötig. Das<br />
Objekt wird über den in Kapitel 6.1.1 beschriebenen Mechanismus über die Änderung s<strong><strong>ein</strong>es</strong><br />
Zustands informiert. Wie <strong>ein</strong>e Methode in <strong>ein</strong>er Aktion zum Ändern <strong><strong>ein</strong>es</strong> Zustands aussehen<br />
könnte, zeigt Listing 7.2. Diese Methode wird aus doAction() aufgerufen und erhält <strong>ein</strong> Zielobjekt<br />
als Parameter. Sollte das Ziel <strong>ein</strong>e Menge von Objekten s<strong>ein</strong>, wird diese Methode auf jedes<br />
Objekt <strong>ein</strong>zeln angewandt.<br />
1 public void doAction () {<br />
2 GameObject s = getTarget ();<br />
3 if (s == null) {<br />
4 for ( GameObject t : getTargetSet ()) {<br />
5 increase (t);<br />
6 }<br />
7 } else {<br />
8 increase (s);<br />
9 }<br />
10 GameObject d = getSource ();<br />
11 d. deactivateAction (this.getName ());<br />
12 }<br />
13<br />
14 private void increase ( GameObject s) {<br />
15 if (s != null) {<br />
16 if (s. hasState (" hitpoints ")) {<br />
17 try {<br />
18 int hp = s. getState (" hitpoints "). getAsInt ();<br />
19 hp += 25;<br />
20 s. getState (" hitpoints "). setInt (hp);<br />
21 } catch ( StateTypeException e) {<br />
22 return ;<br />
23 } catch ( OutOfBoundException e) {<br />
24 return ;<br />
25 }<br />
26 }<br />
27 }<br />
28 }<br />
Listing 7.2: Die IncreaseHP Aktion<br />
<strong>Jens</strong> <strong>Pfau</strong> · Stephan Mehlhase 71
72 VII. Integration<br />
Welche Möglichkeiten existieren, um Aktionen zu implementieren, die mehrere<br />
Ticks dauern?<br />
Ein Tick ist <strong>ein</strong>e Iteration der Hauptschleife des Spiels. Eine Aktion kann mehrere dieser Ticks<br />
in der currentActions <strong><strong>ein</strong>es</strong> Spielobjekts verbleiben. Während jedes Schleifendurchlaufs wird<br />
doAction() aufgerufen. Die Aktion könnte jedesmal dasselbe tun, oder, ähnlich <strong>ein</strong>em Zustandsautomat,<br />
mehrere interne Zustände durchlaufen und dabei die Aktionslogik ändern. Für<br />
das im vorigen Abschnitt gegebene Beispiel würde das erste Verfahren dazu führen, dass mit<br />
jedem Tick der Zustand um 25 erhöht würde. Genausogut könnte die Methode aber auch so<br />
implementiert werden, dass in den ersten Aufrufen 25 Lebenspunkte hinzugefügt und danach<br />
langsam die Menge der hinzugefügten Lebenspunkte mit jeder weiteren Iteration dekrementiert<br />
würde, was <strong>ein</strong> Beispiel <strong>für</strong> das zweite Verfahren wäre.<br />
Wie kann <strong>ein</strong>e Aktion <strong>ein</strong>e andere starten oder beenden?<br />
Zum Aktivieren <strong>ein</strong>er Aktion muss auf dem Spielobjekt, auf dem sie ausgeführt werden soll, die<br />
Methode activateAction() aufgerufen werden, die den Namen der Aktion sowie die Zielobjekte<br />
als Parameter erhält. Um a priori zu überprüfen, ob <strong>ein</strong>e Aktion auf <strong>ein</strong>em Spielobjekt<br />
ausführbar ist, kann hasAction(String) verwendet werden. Das Beenden verläuft analog mit<br />
der Methode deactivateAction(String). Damit sich <strong>ein</strong>e Aktion selber beendet, muss sie sich<br />
auf dem Quellobjekt deaktivieren. Listing 7.3 zeigt, wie das realisierbar ist.<br />
1 protected void removeFromCurrentActionQueue () {<br />
2 GameObject g = getSource ();<br />
3 g. deactivateAction (this.getName ());<br />
4 }<br />
Listing 7.3: Methode zum Beenden <strong>ein</strong>er Aktion<br />
Wie kann <strong>ein</strong>e Skriptsprache zur Definition der Aktionslogik verwendet werden?<br />
Eine Aktion kann beliebige Logik enthalten, demzufolge könnte dort auch <strong>ein</strong> Interpreter <strong>für</strong><br />
<strong>ein</strong>e Skriptsprache geladen werden. Eine Beispielimplementierung existiert <strong>für</strong> Lua und Tcl. Die<br />
LuaScriptAction baut auf das luajava 3 Paket, die Tcl-Variante wurde mit Hilfe des Tcl/Java 4<br />
Projekts umgesetzt.<br />
Der Einfachheit halber existiert <strong>ein</strong>e abstrakte Klasse ScriptAction, die von Action erbt und<br />
zusätzlich weitere Methoden zur Verfügung stellt. Da Spieldesigner, die das Verhalten von Aktionen<br />
in der Skriptsprache definieren, nicht umfassende Kenntnisse im Programmieren haben<br />
müssen, sind in diesen Methoden häufig genutzte Arbeitsschritte ausgelagert, die dann aus den<br />
Skriptobjekten heraus aufgerufen werden könnten. Die im letzten Abschnitt vorgestellte Methode<br />
zum Beenden der Aktion ist dort beispielsweise definiert.<br />
3 http://www.keplerproject.org/luajava/<br />
4 http://tcljava.sourceforge.net/<br />
72 <strong>Jens</strong> <strong>Pfau</strong> · Stephan Mehlhase
VII. Integration 73<br />
Listing 7.4 zeigt, wie die doAction() Methode der Klasse LuaScriptAction implementiert<br />
ist. Vor dem Aufruf des eigentlichen Lua Skripts werden <strong>ein</strong>ige Objekte auf den Stack des<br />
Lua-Interpreters gelegt. Die Variable luaCode enthält den auszuführenden Quelltext, der im<br />
Konstruktor von der LuaActionFactory gesetzt wird. Die übrige Logik der Aktion wird in Lua<br />
ausgeführt.<br />
1 public void doAction () {<br />
2 LuaState luaState = LuaStateFactory . newLuaState<br />
();<br />
3 luaState . openBasicLibraries ();<br />
4<br />
5 try {<br />
6 // Add this action to the Lua script so that<br />
methods can be<br />
7 // accessed .<br />
8 luaState . pushString (" thisAction ");<br />
9 luaState . pushObjectValue (this);<br />
10<br />
11 // Add the source of this action .<br />
12 luaState . pushString (" sourceObject ");<br />
13 luaState . pushObjectValue ( getSource ());<br />
14<br />
15 // Add the target of this action .<br />
16 luaState . pushString (" target ");<br />
17 luaState . pushObjectValue ( getTarget ());<br />
18<br />
19 // Add the targetSet of this action .<br />
20 luaState . pushString (" targetSet ");<br />
21 luaState . pushObjectValue ( getTargetSet ());<br />
22 } catch ( LuaException e) {<br />
23 luaState . close ();<br />
24 return ;<br />
25 }<br />
26<br />
27 luaState . doString ( luaCode );<br />
28 luaState . close ();<br />
29 }<br />
Listing 7.4: Ausführung <strong>ein</strong>er in Lua geschriebenen Aktion<br />
<strong>Jens</strong> <strong>Pfau</strong> · Stephan Mehlhase 73
Kapitel 8<br />
Zusammenfassung und Ausblick<br />
Vollkommenheit entsteht<br />
offensichtlich nicht dann, wenn man<br />
nichts mehr hinzuzufügen hat,<br />
sondern wenn man nichts mehr<br />
wegnehmen kann.<br />
Antoine de Saint-Exupéry<br />
Das Ziel dieser Arbeit war, <strong>ein</strong> Objektmodell <strong>für</strong> Spiele zu entwickeln, das sich flexibel in<br />
unterschiedlichen Umgebungen <strong>ein</strong>setzen lässt. Als primäre Anforderungen wurden daher die<br />
Erweiter- und Wiederverwendbarkeit identifiziert. Besondere Rücksicht wurde auch darauf genommen,<br />
dass die Integration in <strong>ein</strong> Framework <strong>für</strong> Multiplayerspiele geplant ist.<br />
Um die relevanten Aspekte und deren Implementierungsmöglichkeiten zu bestimmen, wurde auf<br />
Basis bestehender Systeme und mit Hilfe von Literatur, die sich mit diesem Thema beschäftigt,<br />
<strong>ein</strong>e Taxonomie erarbeitet. Diese erfasst das Themengebiet mehrdimensional und kann somit<br />
auch verwandten Arbeiten dienlich s<strong>ein</strong>.<br />
Die Taxonomie sowie die aufgestellten Anforderungen bildeten die Basis zur <strong>Entwicklung</strong> der<br />
Architektur des Systems. Dieses zeichnet sich vor allem durch die leichte Austauschbarkeit s<strong>ein</strong>er<br />
Komponenten aus, wodurch zum Beispiel die Kommunikation des <strong>Objektmodells</strong> mit anderen<br />
Subsystemen oder anderen Instanzen desselben flexibel an verschiedene Umgebungen anpassbar<br />
ist. Die Elemente, die den Spielinhalt bilden, lassen sich über vorgestellte Methoden in externen<br />
Datenquellen definieren, so dass die <strong>Entwicklung</strong> derselben unabhängig von dem Quelltext des<br />
Projekts erfolgen kann. Dies erleichtert zum <strong>ein</strong>en den Spieldesignern ihre Arbeit und ermöglicht<br />
zum anderen die parallele Umsetzung des Systems und des Spielinhalts. Dadurch lassen<br />
sich sowohl Zeit als auch Kosten sparen.<br />
Es sollte weiterhin deutlich geworden s<strong>ein</strong>, dass bei der Verwendung <strong><strong>ein</strong>es</strong> flexibel <strong>ein</strong>setzbaren<br />
<strong>Objektmodells</strong> die wiederholte Neuentwicklung <strong><strong>ein</strong>es</strong> solchen <strong>für</strong> verschiedene Projekte nicht<br />
nötig ist.<br />
8.1 Kritikpunkte<br />
Die größten Probleme dieser Arbeit ergaben sich dadurch, dass immer wieder zwischen der<br />
Anforderung nach ausreichender Performanz auf der <strong>ein</strong>en Seite und ordentlichem objektorien-
VIII. Zusammenfassung und Ausblick 75<br />
tierten Design auf der anderen abgewogen werden musste. Zwar wurde in den Anforderungen<br />
das Augenmerk vor allem auf zweiteres gelegt, <strong>ein</strong>e Ignoranz des ersteren hätte aber wohl die<br />
Praxistauglichkeit des Systems zunichte gemacht. Diese Problematik spiegelt sich deutlich in<br />
<strong>ein</strong>igen Komponenten wieder.<br />
Zum <strong>ein</strong>en wurden GameObject sowie GameObjectType nicht gegen Schnittstellen implementiert,<br />
was deren Austauschbarkeit erschwert, aber teure Typüberprüfungen zur Laufzeit bei<br />
dem Übergang zu angrenzenden Systemen wie den Filtern unnötig macht. Das gleiche trifft auf<br />
die Klassen DeltaObject und DeltaType zu, welche zudem noch <strong>ein</strong>e starke Kopplung gegenüber<br />
ihren Äquivalenten in den Objekthierarchien aufweisen.<br />
Zum anderen wurde an vielen Stellen auf das Werfen von Ausnahmen verzichtet, welche in der<br />
heutigen Softwareentwicklung <strong>ein</strong> wichtiges Werkzeug zur Fehlerbehandlung darstellen. Denn<br />
<strong>ein</strong> solcher Prozess geht auch immer mit <strong>ein</strong>er Objekterstellung <strong>ein</strong>her, die zusätzliche Kosten<br />
zur Laufzeit verursacht. Dieser Kompromiss wurde vor allem an voraussichtlich häufig frequentierten<br />
Stellen wie der Methode zur Löschung <strong><strong>ein</strong>es</strong> Spielobjekts aus der Baumhierarchie <strong>ein</strong>gegangen.<br />
Sollte hier zum Beispiel das zu löschende Objekt gar nicht vorhanden s<strong>ein</strong>, wird k<strong>ein</strong>e<br />
Ausnahme zur Signalisierung dieses Zustands geworfen.<br />
Bei Veränderungen der Baumstruktur sowie dem Anwenden von Aktionen auf ihr Quellobjekt<br />
und die Zielobjekte werden innerhalb des Systems möglicherweise sehr viele Nachrichten an<br />
die Beobachter dieser Objekte versendet, da das Modell ohne Kenntnis der Semantik k<strong>ein</strong>e<br />
Möglichkeit hat, zu entscheiden, welche dieser Nachrichten tatsächlich redundant sind. Diese<br />
Aufgabe fällt damit vorrangig den Filtern zu.<br />
Diesen bleibt es ebenfalls überlassen, bei der Synchronisierung der Listen der Aktionen und<br />
Zustandsobjekte – zum Beispiel innerhalb der GameObject Objekte – zu entscheiden, wie das<br />
Löschen von Einträgen in diesen Datencontainern auf <strong>ein</strong>em Netzwerkknoten mit weiteren abgeglichen<br />
wird. Das System bietet k<strong>ein</strong>en Mechanismus, solche aus <strong>ein</strong>er Liste entfernten Objekte<br />
solange als gelöscht markiert vorzuhalten, bis alle interessierten Teilnehmer <strong>ein</strong>en entsprechenden<br />
Abgleich durchgeführt haben. Dies soll auf Grund der Komplexität dieser Aufgabe eben<br />
mit Hilfe der Filter gelöst werden.<br />
8.2 Ausblick<br />
Im Anschluss an diese Arbeit ist von Interesse, ob das System tatsächlich den gestellten Anforderungen<br />
standhält und praktikabel <strong>ein</strong>zusetzen ist. Das kann jedoch nur durch dessen Verwendung<br />
in <strong>ein</strong>em Spieleprojekt festgestellt werden, durch welche möglicherweise weitere Anpassungen<br />
nötig werden. Allerdings sollte die Fokussierung auf Erweiter- und Wiederverwendbarkeit<br />
bei der <strong>Entwicklung</strong> des Systems Adaptionen in vernünftiger Art und Weise unterstützen. Im<br />
Folgenden werden <strong>ein</strong>ige Anregungen <strong>für</strong> solche Handgriffe gegeben.<br />
• Es ist zu untersuchen, ob die Liste der aktuell ausgeführten Aktionen in der Klasse<br />
GameObject nicht in <strong>ein</strong>em Singleton[Gamma, 1997] oder <strong>ein</strong>er statischen Variable gehalten<br />
werden könnte, damit <strong>ein</strong> Aufrufen aller Aktionen nicht das Iterieren über den<br />
<strong>Jens</strong> <strong>Pfau</strong> · Stephan Mehlhase 75
76 VIII. Zusammenfassung und Ausblick<br />
gesamten Spielobjektbaum erfordert, in dem viele Objekte möglicherweise gerade gar k<strong>ein</strong>e<br />
Aktionen auszuführen haben.<br />
• Zur Verbesserung der Leistung des Systems könnte die Vererbungshierarchie der Typen<br />
auf ihre Blätter reduziert werden, indem die Eigenschaften der Typen von der Wurzel an in<br />
die Kinder kopiert werden. Dies könnte <strong>für</strong> den Einsatz zur Laufzeit interessant s<strong>ein</strong>, wenn<br />
<strong>ein</strong> dynamisches Austauschen der Typen nicht mehr von Interesse ist. Dadurch würden<br />
Anfragen nach Eigenschaften von Spielobjekten direkt von deren Typ behandelt werden<br />
können und müssten nicht durch die gesamte Vererbungshierarchie geprüft werden.<br />
• Möglicherweise wären weitere State Klassen von Interesse, zum Beispiel solche, die Konstanten<br />
repräsentieren, deren Wert sich nach der Initialisierung nicht mehr ändern.<br />
• Es ist nötig, die Anforderungen der Grafikengine an das Form Interface zu evaluieren, um<br />
dieses mit entsprechenden Methoden zu füllen.<br />
• Um die Eindeutigkeit der von dem System vergebenen IDs zu gewährleisten, bedarf es <strong><strong>ein</strong>es</strong><br />
geeigneten Mechanismus, der zum Beispiel auf Prinzipien beruht, die auch ihre Verwendung<br />
in Algorithmen <strong>für</strong> Multicasts 1 finden und es mehreren Netzwerkknoten erlauben,<br />
sich auf <strong>ein</strong>e <strong>ein</strong>deutige ID <strong>für</strong> jede zu verschickende Nachricht zu <strong>ein</strong>igen.<br />
• Zur Definition der Objekte in dem System könnte <strong>ein</strong> XML Schema und <strong>ein</strong> passender<br />
Parser entworfen werden, der mit Hilfe korrespondierender Filter die Objekthierarchien<br />
mit Leben füllt. Ebenso könnte <strong>ein</strong>e Datenbank als Quelle genutzt werden.<br />
• Werden zur Laufzeit Typen, von denen bereits Instanzen mit überschreibenden State<br />
Objekten bestehen, aus der Hierarchie entfernt, kann es zu undefinierbarem Verhalten<br />
kommen. Das ist genauer zu untersuchen und möglicherweise mit geeigneten Methoden<br />
zu verhindern.<br />
• Es könnte <strong>ein</strong> Netzwerkprotokoll entwickelt werden, welches zwischen zwei Netzwerkknoten<br />
vermittelt, welche Filter <strong>ein</strong>gesetzt werden, oder welches den Filtern erlaubt, bei Bedarf<br />
selber Objekte nachzufordern.<br />
Über diese Arbeit hinaus bedarf die Spieleentwicklung weiterer Erforschung von Methoden und<br />
der <strong>Entwicklung</strong> von Handwerkszeug, zu dem auch <strong>ein</strong> solches Objektmodell gehören sollte. Die<br />
Schwierigkeit besteht natürlich in der Interdisziplinarität des Themengebiets, in dem eben nicht<br />
nur Informatiker beschäftigt sind. So müssen unterschiedliche Anforderungen, Begrifflichkeiten<br />
und Vorkenntnisse der Mitwirkenden zusammengebracht werden. Gerade an den Schnittstellen<br />
zwischen Fachgebieten lassen sich Optimierungen vornehmen, wie wir am Beispiel des datadriven<br />
Designs gesehen haben. Darüber hinaus ist es wünschenswert, dass Methoden der Softwareentwicklung,<br />
die schon länger ihre Tauglichkeit bewiesen haben, ihren Weg in die Spiel<strong>ein</strong>dustrie<br />
finden und Prozesse somit standardisiert werden können. All dies fördert nicht nur<br />
Kosten- und Zeitersparnis sondern in erster Linie auch die Kommunikation innerhalb der Entwicklerteams.<br />
1 http://de.wikipedia.org/wiki/Multicast<br />
76 <strong>Jens</strong> <strong>Pfau</strong> · Stephan Mehlhase
Literaturverzeichnis<br />
Bilas 2002<br />
Bilas, Scott: A Data-Driven Game Object System. In: Game Developers Conference<br />
Proceedings GDC, Game Developers Conference, 2002<br />
Church 2002<br />
Church, Doug: Object Systems: Methods for Attaching Data to Objects and Connecting<br />
Behaviour. In: Game Developers Conference Proceedings GDC, Game Developers Conference,<br />
2002<br />
Doherty 2003<br />
Doherty, Michael: A Software Architecture for Games. In: University of the Pacific<br />
Department of Computer Science Research and Project Journal (RAPJ) 1 (2003), Nr. 1<br />
Duran 2003<br />
Duran, Alex: Building Object-Systems: Features, Tradeoffs, and Pitfalls. In: Game Developers<br />
Conference Proceedings GDC, Game Developers Conference, 2003<br />
Gamma 1997<br />
Gamma, Erich: Design Patterns. Elements of Reusable Object-Oriented Software. Addison-<br />
Wesley Professional, 1997. – ISBN 0201633612<br />
Griwodz 2002<br />
Griwodz, Carsten: State replication for multiplayer games. In: Proceedings of NetGames<br />
2002, 2002, S. 29–35<br />
Hannah 2004<br />
Hannah, Britt L.: Object-Oriented Game Design. A modular and logical method of designing<br />
games. (2004), August. http://www.devmaster.net/articles/oo-game-design/<br />
Ludwig 2005<br />
Ludwig, Joe: Real-World MMO Object Sharing. (2005), S. 209 – 221. ISBN 1584503904<br />
Mühlhäuser 2005<br />
Mühlhäuser, Max: Lecture Notes on TK1: Distributed Systems. 2005<br />
Rabin 2000<br />
Rabin, Steve: The Magic of Data-Driven Design. (2000), S. 3–7. ISBN 1584500492<br />
Riley 2003<br />
Riley, Sean: Data-Driven Systems for MMP Games. (2003), S. 385–396. ISBN 1584502436
II Literaturverzeichnis<br />
Stroustrup 2000<br />
Kapitel 15. In: Stroustrup, Bjarne: Die C++ Programmiersprache. 4. Addison-Wesley,<br />
2000. – ISBN 382731660, S. 417–418<br />
Tanenbaum 2002<br />
Tanenbaum, Andrew S.: Computer Networks. 4. Prentice Hall PTR, 2002. – ISBN<br />
0130661023<br />
Tanenbaum u. van Steen 2002<br />
Tanenbaum, Andrew S. ; Steen, Maarten van: Distributed Systems: Principles and Paradigms.<br />
1. Prentice Hall, 2002. – ISBN 0130888931<br />
II <strong>Jens</strong> <strong>Pfau</strong> · Stephan Mehlhase