14.09.2013 Aufrufe

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

MEHR ANZEIGEN
WENIGER ANZEIGEN

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

Hurra! Ihre Datei wurde hochgeladen und ist bereit für die Veröffentlichung.

Erfolgreich gespeichert!

Leider ist etwas schief gelaufen!