Entwicklung einer isometrischen Graphik
Transcription
Entwicklung einer isometrischen Graphik
Entwicklung einer isometrischen Graphik-Engine in Java Diplomarbeit Vorgelegt von Thomas Schuster Institut für Computervisualistik Arbeitsgruppe Computergraphik Betreuer und Prüfer: Prof. Dr.-Ing. Stefan Müller August 2004 (Platzhalter für Aufgabenstellung) Hiermit erkläre ich an Eides statt, dass die vorliegende Arbeit selbständig verfasst wurde und keine anderen als die angegebenen Quellen und Hilfsmittel verwendet wurden. . . . . . . . . . . . . . . . . . . , den . . . . . . . . . . . . .................................... (Ort, Datum) (Unterschrift) Inhaltsverzeichnis 1 Einleitung 1.1 Begriffsdefinition . . . . . . . . . . . . . . . . . . . . . . . . . 2 Bestehende Systeme und Verfahren 2.1 Isometrie in Computerspielen . . . 2.2 Verfahren . . . . . . . . . . . . . . 2.2.1 Tile-basierende Verfahren . 2.2.2 Modell-basierende Verfahren 2.2.3 Hybride Verfahren . . . . . 6 6 . . . . . 8 8 10 10 13 13 3 Anforderungsanalyse 3.1 Allgemein . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3.2 Graphik . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3.3 Eingabe . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 14 14 15 17 4 Konzeption 4.1 Überblick . . . . . . . . . . . . . . . . . 4.2 Design . . . . . . . . . . . . . . . . . . . 4.3 Die APIs im Detail . . . . . . . . . . . . 4.3.1 System . . . . . . . . . . . . . . . 4.3.2 Utilities . . . . . . . . . . . . . . 4.3.3 OpenGL-Direktaufrufe (skizziert) 4.3.4 Texturen . . . . . . . . . . . . . . 4.3.5 Sprites . . . . . . . . . . . . . . . 4.3.6 Terrains . . . . . . . . . . . . . . 4.3.7 Eingabe . . . . . . . . . . . . . . 18 18 21 21 24 25 25 27 28 32 35 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5 Prototypische Umsetzung 38 5.1 Technologien . . . . . . . . . . . . . . . . . . . . . . . . . . . 38 5.1.1 JOGL (Java bindings for OpenGL) . . . . . . . . . . . 38 5.1.2 LWJGL (Lightweight Java Game Library) . . . . . . . 39 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 39 40 40 40 41 6 Implementierung des Demospiels 6.1 Design . . . . . . . . . . . . . . . 6.1.1 Allgemein . . . . . . . . . 6.1.2 Anforderungsanalyse . . . 6.2 Implementierung . . . . . . . . . 6.3 Screenshots . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 50 50 50 51 54 55 5.2 5.1.3 Vergleich . . . 5.1.4 Entscheidung Implementierung . . 5.2.1 Allgemein . . 5.2.2 Terrain . . . . . . . . . . . . . . . . . . . . . . . . 7 Auswertung der Ergebnisse 7.1 Erreichte Ziele . . . . . . . . 7.1.1 Allgemein . . . . . . 7.1.2 Graphik . . . . . . . 7.1.3 Eingabe . . . . . . . 7.2 Effizienzanalyse . . . . . . . 7.2.1 Aufbau des Tests . . 7.2.2 Ergebnisse in Zahlen 7.2.3 Schlussfolgerung . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 62 62 62 63 65 65 65 66 70 8 Fazit und Ausblick 8.1 Erreichte Ziele . . . . . . . . . . . . 8.2 Erweiterungen und Verbesserungen 8.2.1 Shader . . . . . . . . . . . . 8.2.2 Innenbereiche . . . . . . . . 8.2.3 Verschiedenes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 71 71 72 72 72 73 . . . . . . . . . . . . . . . . . . . . . . . . Kapitel 1 Einleitung Geht man heute in einen typischen Hollywood“-Film, hat man häufig das ” Gefühl, daß die eigentliche Handlung den Unmengen von Spezialeffekten zum Opfer gefallen ist. Bei aktuellen Computerspielen verhält es sich oft ähnlich. Denn durch den rasanten Fortschritt der Graphikhardware präsentieren sich mittlerweile nahezu sämtliche Spiele in opulenter 3D-Graphik. Aber nicht alles, was technisch in 3D umsetzbar ist, sollte auch tatsächlich so umgesetzt werden. Gerade wenn Übersicht gefragt ist, also beispielsweise komplexe Welten aus einiger Entfernung dargestellt werden, scheinen Massen von aufwendigen 3D-Modellen oft fehl am Platz zu sein. Denn der Aufwand, sie zu rendern, übersteigt häufig ihren effektiven Nutzen. In dieser Arbeit wird versucht, bewährte Graphikverfahren zur Darstellung isometrischer Szenen sinnvoll mit Fähigkeiten moderner Graphikhardware zu verbinden. Auf diese Weise soll eine Graphikengine entstehen, die auch bei weniger leistungsstarker Hardware in der Lage ist, komplexe Welten ansprechend und effizient aus einer isometrischen Perspektive darzustellen. Dabei wird auf die plattform-unabhängigen Standards Java und OpenGL zurückgegriffen, um eine möglichst große Kompatibilität zu gewährleisten. Die angestrebte Umsetzung in Java kann gleichzeitig als eine Art proof of concept angesehen werden. Denn speziell im Bereich der Computergraphik hat Java, im Hinblick auf seine Performanz, einen nicht gerade guten Ruf. 1.1 Begriffsdefinition Axonometrie Bei Axonometrie handelt es sich um eine orthographische Parallelprojektion, d. h. die Sehstrahlen treffen senkrecht auf die Projektionsebene. In der Foto- 6 KAPITEL 1. EINLEITUNG 7 graphie entspricht dies einer Aufnahme eines sehr weit entfernten Gegenstandes mit einem starken Teleobjektiv. Diese Art der Darstellung wird aufgrund ihrer Einfachheit hauptsächlich in technischen Zeichnungen verwendet. Die wichtigsten Merkmale der Axonometrie sind: • Die Projektionsebene ist nicht parallel zu einer der Koordinatenebenen. • Die perspektivische Verkürzung ist unabhängig von der Entfernung zum Betrachter. • Parallelen bleiben erhalten, Winkel nicht. Isometrie Die häufigste axonometrische Projektion ist die isometrische Projektion, bei der die Bildebene alle Koordinatenachsen des Modellkoordinatensystems in gleicher Distanz schneidet. Dadurch sind die Winkel zwischen ihnen in der Projektion gleich (120◦ ). Weiterhin stehen alle Abmessungen innerhalb des Bildes im gleichen Verhältnis zueinander, da die Koordinatenachsen den gleichen Verkürzungsfaktor besitzen (s. Abbildung 1.1). Andere Formen der Axonometrie sind Dimetrie (nur zwei Achsen besitzen den gleichen Verkürzungsfaktor) und Trimetrie (jede Achse hat einen eigenen Verkürzungsfaktor). Abbildung 1.1: Isometrie Kapitel 2 Bestehende Systeme und Verfahren Bei den betrachteten Systemen mit isometrischer Graphik handelt es sich, wie nicht anders zu erwarten, um Spiele. Möglicherweise existierende Anwendungen anderer Art werden an dieser Stelle nicht berücksichtigt. 2.1 Isometrie in Computerspielen Die Ära isometrischer Spiele wurde Anfang der 80er Jahre durch Automatenspiele wie Zaxxon und Marble Madness eingeläutet. Die Faszination dieser damals neuartigen Spielegraphik beruht auf dem gleichen Phänomen wie bei technischen Zeichnungen. Mit einfachen Mitteln wird eine ansprechende Ansicht geschaffen, die zwar keine natürliche Perspektive wie die des menschlichen Auges besitzt, aber dennoch Spielfiguren, Objekte und Hintergründe dreidimensional darstellt. Gerade diese Möglichkeit, Dreidimensionalität mit einer Hardware zu erreichen, die üblicherweise nur zweidimensionale Ansichten darstellen konnte, bedeutete den Durchbruch der Isometrie in der Computerspiele-Industrie. Bis heute finden sich auch für moderne 3D-Hardware immer wieder Spiele mit isometrischer Ansicht, denn für manche Spiele oder sogar Genres bietet sie gegenüber der Zentralprojektion entscheidende Vorteile. Diese werden umso deutlicher, je weiter die Kamera vom eigentlichen Spielgeschehen entfernt und somit Übersichtlichkeit gefragt ist. Dann nämlich sieht die isometrische Perspektive meist gleichförmiger aus, da das Erscheinungsbild eines Objektes nicht von seiner Entfernung zum Blickpunkt abhängt. An dieser Stelle soll eine repräsentative Auswahl von Computerspielen mit isometrischer Graphik gegeben werden (s. Tabelle 2.1). Dieser Überblick 8 KAPITEL 2. BESTEHENDE SYSTEME UND VERFAHREN 9 Abbildung 2.1: Zaxxon & Marble Madness umfasst zahlreiche Genres und erstreckt sich über die letzten 20 Jahre. Zum besseren Verständnis der Tabelle werden einige der Spalten im Folgenden kurz erläutert: Multiplayer: Gibt an, ob das Spiel mehrere gleichzeitige Spieler unterstützt. max. Spieler (Multiplayer): Die Anzahl der gleichzeitig unterstützten Spieler (falls zutreffend). Echtzeit / Rundenbasiert: Läuft das Spiel in Echtzeit ab, oder ist das Voranschreiten der Spielzeit von den Eingaben des Benutzers abhängig? max. Einheiten pro Spieler: Die Anzahl der zum Spieler gehörenden graphischen Objekte. Diese kann von einem (meist eine Repräsentation des Spielers selbst) bis zu mehreren hundert (Armee-Einheiten und dergleichen) reichen. Größe der Welt / einzelner Level: Eine ungefähre Einschätzung der Weltbzw. Levelgröße in Relation zum Sichtfeld. Größe des Sichtfelds: Wie groß ist der Ausschnitt aus der Welt, der im Sichtfeld dargestellt wird, in Relation zu den Spielfiguren? Interaktion des Spielers mit der Welt: Wieviel Einfluss kann der Spieler auf das Aussehen der Welt nehmen? Dies schließt die Manipulation von Landschaften und Objekten als auch die Interaktion mit NichtSpieler-Charakteren (engl. Non-Player Characters, kurz NPC ) ein. KAPITEL 2. BESTEHENDE SYSTEME UND VERFAHREN 10 Interaktion der Welt mit dem Spieler: Wie lebendig“ ist die Darstel” lung der Welt, d. h. wie hoch ist der Grad der Interaktion zwischen der Welt und dem Spieler? Berücksichtigt werden Elemente wie die Dynamik von Landschaften, Objekten und NPCs. Aufgrund der aufgelisteten Eigenschaften läßt sich u. a. der graphischen Aufwand eines jeweiligen Spiels ansatzweise einschätzen. 2.2 Verfahren In diesem Abschnitt werden Verfahren zur isometrischen Darstellung beschrieben, welche vom traditionellen Vorgehen mit 2D-Hardware bis zu neuen Möglichkeiten moderner 3D-Hardware reichen. Bedauerlicherweise gibt es jedoch wenig Veröffentlichungen zum Thema Isometrische Graphik“ allge” mein und speziell zu Spielen. Denn die Hersteller von Spielen mit isometrischer Graphik haben erwartungsgemäß wenig Interesse daran, ihre verwendete Technik der Öffentlichkeit und damit der Konkurrenz zugänglich zu machen. Abgesehen von Isometric Game Programming with DirectX ” 7.0“ [4] finden sich daher kaum Publikationen. Hinzu kommen vereinzelte Internetquellen wie Artikel, Newsgroup- oder Mailinglisteneinträge, die aufgrund ihrer Fragmentiertheit an dieser Stelle nicht zitiert werden. Aufgrund langjähriger Beschäftigung mit Computerspielen im Allgemeinen und mit isometrischen Spielen im Besonderen fließen zusätzlich eigene Beobachtungen und Erfahrungen in diese Arbeit ein. 2.2.1 Tile-basierende Verfahren Ein Tile ist eine Graphik, die einen Ausschnitt des Hintergrunds darstellt. Mehrere Tiles aneinander gefügt ergeben eine nahtlose isometrische Ansicht, möglicherweise mit mehreren Ebenen, Beleuchtungseffekten und Transparenzen. Ein Tile stellt damit einen speziellen Sprite dar. Bei Sprites handelt es sich um vorberechnete oder gezeichnete Graphiken beliebiger Größe, meist mit bitweiser Transparenz oder vollwertigem Alphakanal versehen. Hinzu kommen weitere Sprites zur Darstellung von (Spiel-)Figuren, Objekten und Effekten (z. B. Partikelsysteme). Nach eigenen Einschätzungen nutzen die meisten Spiele aus Tabelle 2.1 dieses Verfahren. Beispiele sind Civilization 3“, Diablo 2“ und Ultima VII: ” ” ” The Black Gate“. Jahr 1997 2002 2000 1993 2001 2000 2000 2002 1999 1986 1999 1989 2004 1993 2000 1998 1993 1992 1998 1994 Echtzeit / Rundenbasiert Echtzeit Echtzeit Echtzeit Echtzeit Rundenbasiert Echtzeit Echtzeit Rundenbasiert Rundenbasiert Echtzeit Echtzeit Echtzeit Echtzeit Echtzeit Echtzeit Echtzeit Echtzeit Echtzeit Rundenbasiert Rundenbasiert Spiel Age of Empires Anno 1503 Baldur's Gate II: Shadows of Amn Cannon Fodder Civilization 3 Command & Conquer: Red Alert 2 Diablo II Heroes of Might and Magic IV Jagged Alliance 2 Marble Madness Planescape: Torment Populous Sacred SimCity 2000 Sims, The StarCraft Syndicate Ultima VII: The Black Gate Warhammer 40,000: Chaos Gate X-COM: UFO Defense Spiel Age of Empires Anno 1503 Baldur's Gate II: Shadows of Amn Cannon Fodder Civilization 3 Command & Conquer: Red Alert 2 Diablo II Heroes of Might and Magic IV Jagged Alliance 2 Marble Madness Planescape: Torment Populous Sacred SimCity 2000 Sims, The StarCraft Syndicate Ultima VII: The Black Gate Warhammer 40,000: Chaos Gate X-COM: UFO Defense max. Einheiten pro Größe der Welt / Spieler einzelner Level > 100 Groß > 100 Groß 6 Sehr groß 15 Gering > 100 Sehr groß > 100 Groß > 10 Sehr groß > 10 Groß 6 Mittel 1 Gering 6 Sehr groß > 100 Groß >5 Sehr groß > 100 Groß > 100 Groß > 100 Groß 4 Groß 8 Sehr groß 5 Gering 10 Gering Präsentation Publisher Microsoft Electronic Arts Interplay Virgin Infogrames Electronic Arts Blizzard Entertainment New World Computing Talonsoft Electronic Arts Interplay Bullfrog Productions Ltd. Electronic Arts Ascaron Take2 Interactive Maxis Software Inc. Maxis Software Inc. Maxis Software Inc. Electronic Arts Blizzard Entertainment Blizzard Entertainment Bullfrog Productions Ltd. Electronic Arts Origin Systems Inc. Origin Systems Inc. Random Games Strategic Simulations Inc. Mythos Games, MicroProse MicroProse Entwickler Ensemble Studios Sunflowers BioWare Corporation Sensible Software Firaxis Games Westwood Studios Blizzard Entertainment 3DO Sir-tech Software Inc. Atari Games Black Isle Studios Multiplayer Ja Nein Ja Nein Ja Ja Ja Ja Nein Ja Nein Ja Ja Nein Nein Ja Nein Nein Ja Nein max. Spieler (Multiplayer) 8 6 8 8 8 6 2 2 16 8 4 - I. der Welt mit dem Spieler Mittel Hoch Sehr hoch Gering Gering Mittel Mittel Hoch Mittel Gering Hoch Gering Hoch Hoch Hoch Gering Mittel Sehr hoch Gering Gering Interaktion I. des Spielers mit Größe des Sichtfelds der Welt Groß Mittel Mittel Mittel Gering Gering Mittel Gering Groß Mittel Groß Mittel Gering Gering Mittel Gering Mittel Mittel Mittel Gering Gering Gering Groß Sehr hoch Gering Gering Groß Sehr hoch Gering Sehr hoch Mittel Mittel Mittel Mittel Gering Hoch Gering Mittel Gering Hoch Genre Strategie Strategie Rollenspiel Action Strategie Strategie Action / Rollenspiel Strategie Strategie / Rollenspiel Action Rollenspiel Strategie Action / Rollenspiel Simulation / Strategie Simulation / Strategie Strategie Strategie / Rollenspiel Rollenspiel Strategie Strategie Allgemein KAPITEL 2. BESTEHENDE SYSTEME UND VERFAHREN Tabelle 2.1: Populäre Spiele mit isometrischer Graphik 11 KAPITEL 2. BESTEHENDE SYSTEME UND VERFAHREN 12 Umsetzung mit 2D-Hardware Ohne die Hilfe texturierter Polygone, die einem heutige Hardware sehr effizient zur Verfügung stellt, muss man eine darzustellende isometrische Fläche oder Struktur aus einzelnen Graphik-Bausteinen zusammensetzen, ähnlich einem Puzzle. Die gängigen Darstellungsalgorithmen (vgl. [4]) verwenden dazu Tiles, die einer festen Form und Größe entsprechen. Es handelt sich dabei um Rhomben, d. h. spezielle Parallelogramme mit gleich langen Seiten. Um eine Ansicht wie in Abbildung 1.1 zu erhalten, müssten die Innenwinkel der Rhomben 60◦ bzw. 120◦ betragen. Da bei der Rasterisierung Linien in diskreten Stufen dargestellt werden, sind diese Winkel jedoch problematisch, denn sie erzeugen ein zu unregelmäßiges Pixelmuster. Daher wird in den meisten Spielen ∼26,6◦ (genauer: arctan(0.5)) statt 30◦ verwendet, um für ein regelmäßiges Muster zu sorgen. Abbildung 2.2: Pixelmuster verschiedener Winkel Umsetzung mit 3D-Hardware Prinzipiell liegt jeder Umsetzung eines tilebasierenden Verfahrens mit 3DHardware das gleiche Prinzip zugrunde: sämtliche Graphik wird mit Hilfe texturierter Polygone dargestellt. Intuitiv bleibt man also im Wesentlichen bei der oben vorgestellten 2D-Methode, nur dass die Möglichkeiten der 3D-Hardware zur Beschleunigung genutzt werden. Dies kann schon in der zwingenden Verwendung texturierter Polygone für Tiles und Sprites enden, wodurch die Darstellung bereits sehr effizient wird. Zusätzlich bieten sich natürlich viele weitere Features aktueller Hardware an, sei es um die allgemeine Performance zu erhöhen, die visuelle Qualität zu verbessern oder Spezialeffekte zu realisieren. Dies trifft natürlich auch auf die folgenden Verfahren zu. KAPITEL 2. BESTEHENDE SYSTEME UND VERFAHREN 2.2.2 13 Modell-basierende Verfahren Mit 3D-Hardware können sämtliche Graphikelemente natürlich auch unter Verwendung von 3D-Modellen realisiert werden, d. h. auf Tiles und Sprites im eigentlichen Sinne wird verzichtet. Eine entsprechende orthographische Projektion vorausgesetzt, ist die Erzeugung isometrischer Szenen also auch auf diese Weise möglich. Nicht selten stellen Sprites ja auch vorgerenderte 3D-Modelle dar, die sich nun eben direkt als Modelle in der Welt befinden. Nach eigenen Einschätzungen nutzen beispielsweise Sacred“ und The ” ” Sims“ aus Tabelle 2.1 dieses Verfahren. 2.2.3 Hybride Verfahren Der Nachteil der komplett modell-basierenden Verfahren liegt in der erforderlichen Komplexität der einzelnen Modelle. Um beispielsweise einen Wald darzustellen, müsste jeder Baum als texturiertes und beleuchtetes Modell gerendert werden. Dies ist sehr aufwendig und kann durch die Verwendung von Sprites oder Billboards zur Darstellung der Bäume massiv beschleunigt werden. Für den Hintergrund der Szene sind 3D-Modelle aufgrund ihrer vergleichsweise geringen Komplexität jedoch meist die bessere Wahl und somit Tiles bzw. Sprites vorzuziehen. Prinzipiell lassen sich großflächige Hintergründe also effizienter mit 3D-Modellen realisieren und einzelne Objekte mit Sprites. Nach eigenen Einschätzungen nutzen beispielsweise Anno 1503“ und ” Heroes of Might and Magic IV“ aus Tabelle 2.1 dieses Verfahren. ” Kapitel 3 Anforderungsanalyse Um die Lesbarkeit der folgenden Kapitel zu erhöhen soll das zu entwickelnde Framework an dieser Stelle einen Namen erhalten. Es wird auf Mithril 1 getauft, in Anlehnung an das große Ziel, es für riesigen Mehrbenutzer-Welten einsetzen zu können, und an das Hauptgenre dieser speziellen Computerspiele: Fantasy-Rollenspiele. Die isometrische Darstellung in Mithril soll mittels eines hybriden Verfahrens (s. Abschnitt 2.2.3) realisiert werden, da dies als eleganter Kompromiss zwischen Geschwindigkeit und visueller Qualität angesehen wird. Außerdem kommt dies der in der Aufgabenstellung geforderten Unterstützung von weniger leistungstarker Graphikhardware nach. Daher orientieren sich die in diesem Kapitel formulierten Anforderungen u. a. an bekannten Vertretern hybrider Verfahren. Aber auch Beobachtungen aus anderen Spielen sowie eigene Ideen fließen in die folgenden Abschnitte ein. 3.1 Allgemein Folgende allgemeine Anforderungen werden an Mithril gestellt: Effizienz: Die Engine muss schnell sein und mit Ressourcen sparsam umgehen. Dies ist besonders wichtig, weil sie ja nur einen von vielen Teilen eines Spiels darstellt. Visuelle Qualität: Um eine hohe visuelle Qualität zu erreichen, sollen die Fähigkeiten der Graphikhardware möglichst gut ausgenutzt werden. 1 Mithril, auch Moria-Silber genannt. Ein phantastisches Metall aus den Büchern von J.R.R. Tolkien. Es wurde in Moria von den Zwergen geschürft und verarbeitet. Mithril war sehr hart und dennoch leicht zu bearbeiten. 14 KAPITEL 3. ANFORDERUNGSANALYSE 15 Dabei sollte Mithril adaptiv vorgehen und sich in seinem Vorgehen der jeweiligen Hardware anpassen. Flexibilität: Dem Benutzer soll ein hohes Maß an Freiheit geboten werden, die Engine an seine Bedürfnisse anzupassen. Ebenso sollen eigene Erweiterungen durch wohldefinierte Schnittstellen möglich sein. Abstraktheit: Da Mithril ein High-Level-Framework darstellt, soll die tatsächlich verwendete Anbindung an OpenGL vor dem Benutzer verborgen werden. Zusätzlich soll es trotzdem möglich sein, über eine Schnittstelle Low-Level-OpenGL-Befehle aufzurufen. Plattform-Unabhängigkeit: Mithril soll auf unterschiedlichen Betriebssystemen laufen und sich dabei möglichst gleich verhalten. Anvisiert werden hauptsächlich Windows und Linux. Kompatibilität: Es werden Mindestanforderungen an die Graphikhardware festgelegt. Solange diese erfüllt sind, soll jede beliebige Hardware unterstützt werden. Stabilität: Die Engine soll robust und fehlertolerant sein. Transparenz ist in diesem Zusammenhang besonders wichtig, so dass der Benutzer auf eventuelle Fehler gezielt reagieren kann. Außerdem soll das System mittels detailiertem Logging leicht zu debuggen sein. 3.2 Graphik Die Anforderungen im Bereich der Graphik, welche den Schwerpunkt der Engine ausmacht, lassen sich in folgende Kategorien unterteilen: Landschaften: In Form und Aussehen möglichst realistische Gelände mit folgenden Features: • Großer Umfang • Stufenloses Höhenprofil • Natürlich wirkender Übergang zwischen verschiedenen Oberflächen, wie Vegetation, Gestein und Schnee • Animierbare Wasserflächen, wie Meere, Seen und Flüsse • Wege und Straßen • Persistente Veränderungen zur Laufzeit, wie Verformungen und Verfärbungen KAPITEL 3. ANFORDERUNGSANALYSE 16 Innenbereiche: • Großer Umfang • Nahtlose Symbiose mit Landschaften, z. B. ein begehbares Haus an einem See • Line of sight, d. h. Ausblenden nicht sichtbarer Bereiche • Mehrere Ebenen • Stufenloses Bodenprofil, z. B. bei einem Höhlenboden • Animierbare Elemente, wie Türen und Aufzüge • Persistente Veränderungen zur Laufzeit, wie Verformungen und Verfärbungen Figuren und Objekte: Folgenden Elemente sollen am Boden, zu Wasser und in der Luft animiert darstellbar sein: • Einzelobjekte, wie Charaktere, Fahrzeuge, Vegetation und Gebäude • Fortlaufende Objekte, wie Zäune, Mauern und Stromleitungen Effekte: Animierbare Spezialeffekte, wie Explosionen, Feuer und Magie. Animationen: Unterstützt werden sollen verschiedene Modi: • Präsentation: Einzelereignis, Ereignissequenz und fortlaufendes Ereignis • Steuerung: zeit- und ereignisbasierend Verhalten bei verschiedenen Auflösungen: Je nach Spieldesign muss sich der Entwickler (sofern er verschiedene Auflösungen unterstützen möchte) entscheiden, wie sich die Anzeige des Spielinhalts der jeweiligen Auflösung anpassen soll. Mithril sollte folgende Fälle unterstützen: • Skalierung des Sichtbereichs → Keine Skalierung der Graphikelemente • Keine Skalierung des Sichtbereichs → Skalierung der Graphikelemente Selektion: Es müssen vielseitige Selektionsmöglichkeiten zur Verfügung stehen, um Bildschirmkoordinaten (beispielsweise die Position des Mauszeigers) auf Objekte (d. h. Sprites) und Objektkoordinaten innerhalb von Terrains und Innenbereichen abbilden zu können. KAPITEL 3. ANFORDERUNGSANALYSE 3.3 17 Eingabe Da die Eingabebehandlung über den Fensterkontext des Betriebssystems geschieht, muss Mithril ebenfalls komfortable Schnittstellen zu Tastatur, Maus und anderen Eingabegeräten bereitstellen. Kapitel 4 Konzeption Dieses Kapitel gliedert sich in eine allgemeine Übersicht der Engine, grundsätzliche Überlegungen zum Design des zu implementierenden Modells sowie die detailierte Betrachtung seiner einzelnen Komponenten. Im Hinblick auf die spätere Umsetzung in Java werden an dieser Stelle bereits Begriffe wie Klasse, Schnittstelle, Objekt und Methode verwendet. 4.1 Überblick Aus der Anforderungsanaylse lässt sich eine Aufteilungen des Frameworks in drei Hauptkategorien vornehmen: Kern, Graphik und Eingabe. Die Aufgaben des Kerns umfassen dabei alles, was sich den beiden anderen Bereichen nicht intuitiv zuordnen lässt. Die Hauptkategorien werden ihrerseits logisch in einzelne Schnittstellen (engl. Interface oder Application Programming Interface, kurz API ) untergliedert. Diese werden in Abbildung 4.1 schematisch dargestellt und im Folgenden in ihrer Funktion kurz umrissen. Kern-APIs System: Fundamentale Bestandteile des Frameworks. Utilities: Hilfreiche, jedoch nicht essentielle Klassen und Methoden. 18 KAPITEL 4. KONZEPTION Abbildung 4.1: Die Schnittstellen im Überblick 19 KAPITEL 4. KONZEPTION 20 Graphik-APIs OpenGL-Direktaufrufe: Ein möglichst großer OpenGL-Befehlsumfang soll über Wrapperklassen1 direkt erreichbar sein. Diese low level calls kann der versierte Benutzer frei nutzen, z. B. für eigene Erweiterungen der Engine. Texturen: Die Erzeugung von Texturen ist durch Einladen der Daten aus einer Bilddatei oder aus einem Bild im Arbeitsspeicher möglich. Texturen bilden die Grundlage für Sprites und Hintergrundbilder und werden als Oberflächen für Terrains und Innenbereiche verwendet. Sprites: Zweidimensionale Graphiken beliebiger Größe im Format RGBA (s. Abschnitt 2.2.1). Sie werden aus Texturen heraus erzeugt. Ihre Darstellung kann über zahlreiche Parameter gesteuert werden. Sprites können Einzelbilder sein oder aus Einzelbildern bestehende Animationen. Terrains: Gelände basierend auf Höhen-, Oberflächen- und Beleuchtungsinformationen. Diese Daten sind zur Laufzeit seperat manipulierbar. Zusätzlich können Sprites zur automatischen Anzeige auf ihnen registriert werden. Innenbereiche: Sie entsprechen prinzipiell den Terrains, werden allerdings um die Unterstützung mehrerer Ebenen, line of sight sowie animierbare Elemente wie Türen und Aufzüge erweitert. Außerdem können Innenbereiche innerhalb eines Terrains platziert werden. Shader: Die Verwendung von pixel shader - und vertex shader -Technologie dient der Verbesserung der visuellen Qualität und erhöht die Flexibilität des Frameworks. Der Einsatz der Shader geschieht über spezielle Schnittstellen innerhalb der anderen Graphik-Komponenten. Eingabe-APIs Tastatur: Tastatureingaben sollen komfortabel abgefragt werden können. Maus: Die Aktionen der Maustasten sollen abgefragt werden können, sowie die relative Bewegung (einschliesslich des Wheels) und die absolute Position der Maus. 1 Eine Wrapperklasse hüllt etwas ein, was eigentlich nicht objektorientiert ist. In diesem Fall Teile der OpenGL-API. KAPITEL 4. KONZEPTION 4.2 21 Design Dem zu entwickelnden Framework ist durch die Verwendung der freien, plattform-unabhängigen Technologien OpenGL und Java bereits ein hohes Maß an Flexibilität in die Wiege gelegt worden. Um diese noch weiter auszubauen, wird Mithril zwei wesentlichen design patterns unterstellt: 1. Abstraktheit: Der Anwender soll gegen Interfaces programmieren, nicht gegen Implementierungen.2 Dieser Regel folgend existieren für nahezu jeden Aspekt in Mithril ein oder mehrere Interfaces, deren tatsächliche Implementierungen vor dem Benutzer verborgen werden. Davon ausgenommen sind lediglich einige wenige finale Klassen geringen Umfangs, wie Color oder Viewport (s. Abschnitt 4.3.1). Durch die konsequente Einhaltung dieses Prinzips ist es möglich, ganze Teile der Engine unter der Haube“ auszutauschen, beispielsweise um ande” re Klassen zur OpenGL-Anbindung einzusetzen. Der Anwender würde davon nichts bemerken, solange er nur gegen die Interfaces programmiert und keine Klasse explizit instanziiert hat. Ebenso sind größere strukturelle Änderungen innerhalb der Implementierung denkbar, bei denen die vorhandenen Interfaces Adapterfunktionen übernehmen, um diese Änderungen vor dem Benutzer zu verbergen. 2. Verwendung von factories: Durch die aus der geforderten Abstraktheit resultierende Schnittstellen-Fassade werden direkte ObjektInstanziierungen erschwert oder sogar verhindert. Um dies aufzufangen existieren spezielle factory-Klassen. Auf diesen kann der Anwender Methoden aufrufen, die die eigentliche Erzeugung vornehmen und eine neue Instanz des gewünschten Typs zurückgeben (siehe beispielsweise die Verwendung von FontFactory und Font in Abschnitt 4.3.1). Diese factories werden ebenfalls über Interfaces angesprochen; ihre konkreten Instanzen werden vom Framework erzeugt und als singletons 3 an den Benutzer weitergegeben. 4.3 Die APIs im Detail Die meisten APIs werden an dieser Stelle durch UML-Klassendiagramme repräsentiert, die jedoch keinen Anspruch auf Vollständigkeit erheben. Viel2 Dieses Vorgehen ist in der objekt-orientierten Programmierung generell ratsam, inbesondere bei größeren Projekten. 3 Unter einem singleton versteht man eine Instanz (einer Klasse), von der es nur genau eine geben darf. KAPITEL 4. KONZEPTION 22 mehr sollen die wesentlichen Konzepte visualisiert werden. Folgende drei APIs werden allerdings nicht im Detail beschrieben: Shader, Innenbereiche und OpenGL-Direktaufrufe. Eine detailiertere Konzeption dieser Bereiche hätte den Umfang der vorliegenden Arbeit gesprengt. Sie werden daher in den anderen APIs auch nicht berücksichtigt. Auf eine Auflistung der Attribute wird verzichtet, da sich diese entweder aus den dargestellten Assoziationen ergeben oder trivialer Natur sind (d. h. sie lassen sich beispielsweise aus den get-Methoden der entsprechenden Klassen ableiten). Abgesehen davon sind ohnehin die meisten der modellierten Klassen Interfaces und verfügen daher nicht über Attribute. Die Auflistung der Operationen ist exemplarisch anzusehen. Dabei werden der Name und der Rückgabewert der Operation berücksichtigt, auf die Parameter wird verzichtet. Dies geschieht einerseits, um die feinkörnige Modellierung von Überladungen zu vermeiden, die gerade bei den factories in hohem Maße nötig wäre. Weiterhin fördert diese Art der Darstellung den abstrakten Charakter des Modells, da lediglich der Zweck einer Operation deutlich werden soll. Die Operationen werden daher im Folgenden auch nur vereinzelt näher beschrieben. An Beziehungen werden Vererbung, Realisation, Aggregation, Assoziation und Abhängigkeit4 verwendet. Eine Beschreibung jedes Diagramms erfolgt innerhalb des entsprechenden Abschnitts. Abbildung 4.2 zeigt stark vereinfacht den generellen Programmablauf der Engine. 4 Diese jedoch ausschließlich mit dem instantiate-Stereotyp, um die Funktionsweise der factories hervorzuheben. KAPITEL 4. KONZEPTION Abbildung 4.2: Vereinfachter Programmfluss 23 KAPITEL 4. KONZEPTION 4.3.1 System Visualisiert in Abbildung 4.3. Mithril Das Haupt-Interface des gleichnamigen Frameworks und das Kontrollzentrum für den Anwender. Intern übernimmt es von Initialisierung über Verwaltung und Fehlermanagement bis zur eigentlichen main loop eine Vielzahl wichtiger Aufgaben, wobei der Schwerpunkt natürlich auf der Graphik liegt. Über dieses Interface hat der Benutzer Zugang zu den singletons der Eingabeklassen Keyboard und Mouse, sowie zu denen der factories FontFactory, SpriteFactory, TerrainFactory und TextureFactory. Über setRenderer() kann eine Klasse, die das RendererInterface (s. u. ) implementiert registriert werden. Renderer Überträgt dem Anwender die Renderkontrolle. Er muss dazu das Interface in einer eigenen Klasse realisieren und diese im MithrilInterface registrieren. Die Methode init() wird zum Zeitpunkt der Initialisierung der Engine bzw. der Registrierung des Renderers genau einmal aufgerufen, dispose() entsprechend bei der Beendigung des Frameworks. checkInput() und render() werden in jedem Durchlauf der main loop aufgerufen und bieten die Möglichkeit, eigene Eingabebehandlungen und Graphikoperationen durchzuführen. Viewport Eine finale Klasse, die ein Darstellungsfeld in Position und Dimension beschreibt. Color Eine finale Klasse, die eine Farbe im Format RGBA repräsentiert. Light Eine generische Lichtquelle, die über ambientes, diffuses und spekulares Licht verfügt. 24 KAPITEL 4. KONZEPTION 25 AmbientLight, PointLight, DirectionalLight und SpotLight Finale Klassen, die das Light-Interface realisieren und dabei jeweils unterschiedliche Arten von Lichtquellen repräsentieren. FontFactory Factory zur Erzeugung von Font-Objekten. Font Repräsentiert einen renderbaren Font. Dieser basiert auf einem TextureGrid (s. Abschnitt 4.3.4). 4.3.2 Utilities Visualisiert in Abbildung 4.4. Diese Komponente ist inhaltlich nur skizziert, da eine solche Werkzeugsammlung natürlich erst mit der Zeit und der tatsächlichen Anwendung des Frameworks wächst. HeightmapGenerator Dient zum Erstellen von zufalls-basierten Höhendaten. Ebenfalls denkbar sind Methoden zum Einlesen von Dateien, sowie das Speichern einer Heightmap in eine Datei. SurfaceMapGenerator Generiert aus einer gegebenen Heightmap eine SurfaceMap. Dies ist über zahlreiche Parameter wie Meeresspiegel-Höhe und Landschaftstypen steuerbar. Außerdem kann aus einer vorhandenen SurfaceMap eine Textur generiert werden, die die Oberflächendaten visualisiert. 4.3.3 OpenGL-Direktaufrufe (skizziert) Oft ist es wünschenswert, über den Funktionsumfangs einer Graphik-Engine hinaus eigene low level calls in die zugrundeliegende Graphik-API auszuführen. Je weiter die Engine jedoch von dieser API entfernt ist, umso schwieriger wird es, diese Möglichkeit dem Benutzer anzubieten. Ein Beispiel sind KAPITEL 4. KONZEPTION 26 Runnable + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + «interface» Mithril areFPSDisplayed() : boolean dispose() : void getBackgroundColor() : Color getBackgroundTexture() : ScreenTexture getBitsPerPixel() : int getFontFactory() : FontFactory getFOV() : float getHeight() : int getKeyboard() : Keyboard getMax2DTextureSize() : int getMax3DTextureSize() : int getMaxTextureUnits() : int getMouse() : Mouse getProperties() : Properties getRenderer() : Renderer getRefreshRate() : int getSpriteFactory() : SpriteDataFactory getTerrainFactory() : TerrainFactory getTextureFactory() : TextureFactory getTitle() : String getViewport() : Viewport getVSync() : boolean getWidth() : int isDisposing() : boolean isFullscreen() : boolean isMouseGrabbed() : boolean isRunning() : boolean isWindowModeSwitchAllowed() : boolean saveScreenshot() : void setBackground() : void setFOV() : void setBackground() : void setFPSDisplayed() : void setFullscreen() : void setMouseGrabbed() : void setRenderer() : void setScreenshotDirectory() : void setScreenshotPrefix() : void setView() : void setViewport() : void setVSync() : void 0..1 + + + + «interface» Renderer checkInput() : void dispose() : void init() : void render() : void 1 Color + + + + + + + + + + getAlpha() : float getBlue() : float getGreen() : float getRed() : float invertRGB() : void set() : void setAlpha() : void setBlue() : void setGreen() : void setRed() : void Viewport + + + + + + + «interface» Light calcLighting() : void getAmbient() : Color getDiffuse() : Color getDirection() : Vector3f getPosition() : Vector3f getSpecular() : Color toOpenGLPosition() : void 3 1 AmbientLight DirectionalLight PointLight 1 + «interface» FontFactory createFont() : Font 1 «instantiate» + + + «interface» Font getHeight() : int getWidth() : int render() : void Abbildung 4.3: UML-Klassendiagramm: System SpotLight KAPITEL 4. KONZEPTION 27 SurfaceMapGenerator + + generate() : SurfaceMap createMapTexture() : Texture2D HeightmapGenerator + generateDiamondCross() : Heightmap Abbildung 4.4: UML-Klassendiagramm: Utilities Szenengraph-Systeme wie Java3D (vgl. [7]), die zwar auf OpenGL basieren, aber aufgrund ihrer Komplexität keine Direktaufrufe einzelner OpenGLBefehle durch den Anwender unterstützen. Da Mithril in seiner Struktur wesentlich flacher und transparenter ist, sind solche Aufrufe jedoch denkbar und sollten durch Wrapperklassen zur Verfügung gestellt werden. Dabei wird ein möglichst großer Teil der OpenGL-Befehle nach außen sichtbar gemacht und kann auf eigene Gefahr vom Anwendungsprogrammierer genutzt werden. Denn obwohl die Engine dadurch viel flexibler wird, kann man sich auf diese Weise auch leicht Fehler und Seiteneffekte einbauen. Diese sind unter Umständen nur schwer nachvollziehbar, da OpenGL eine state machine ist: das Umsetzen eines Status hat oft weitreichende Konsequenzen für nachfolgende OpenGL-Befehle. Also auch für solche, die von Mithril ausgeführt werden. 4.3.4 Texturen Visualisiert in Abbildung 4.5. Texture Repräsentiert allgemeine Textur-Informationen und wird von Texture2D, Texture3D und ScreenTexture spezifisch erweitert. Konkrete TextureInstanzen werden über das TextureFactory-Interface erstellt. Texture2D Erweitert Texture um die Belange zwei-dimensionaler Texturen. Ihre Daten basieren auf RGBA-Bitmaps oder Teilen daraus. Optional werden mip maps unterstützt. Weiterhin übernimmt dieses Interface eine Art factory-Funktion, indem es die Erzeugung von KAPITEL 4. KONZEPTION TextureGrid-Objekten aus Texture2D-Objekten heraus anbietet. Dies ist der einzige Weg ein solches Grid zu instanziieren. Texture3D Erweitert Texture um eine Tiefeneigenschaft. Wie Texture2D basiert auch Texture3D auf RGBA-Bitmaps. ScreenTexture Eine spezielle Meta-Textur, die aus einer oder mehreren Texture2Ds besteht. Sie repräsentiert ein renderbares Bild in der Größe des Bildschirms oder eines beliebigen anderen Viewports. TextureFactory Erzeugt Instanzen von Texture2D, Texture3D und ScreenTexture. Dies kann aus Dateien oder Bildern im Hauptspeicher geschehen. TextureGrid Kapselt ein regelmäßiges Texelgitter. Die zugrundeliegende zweidimensionale Textur wird dadurch in Zellen und ihre Zwischenräume aufgeteilt. Die Zellen dienen als Grundlage für einen oder mehrere Fonts oder Sprites. Im Falle des Fonts repräsentiert jede Zelle eine Glyphe des Schriftsatzes. Bei den Sprites kann eine Zelle für einen einzelnen Sprite stehen oder für ein Einzelbild innerhalb eines animierten Sprites. 4.3.5 Sprites Visualisiert in Abbildung 4.6. SpriteData Repräsentiert die Grunddaten einer ganzen Klasse von Sprites und stellt damit eine Art Vorlage dar. Diese Daten umfassen Dimension, Rotation, Skalierung, Opazität, Blendingeigenschaften sowie Angaben zu Schwerpunkt und optionaler Zentrierung. Mittels createSprite() werden Sprite-Instanzen erzeugt, die auf einer Kopie dieser Daten basieren. 28 KAPITEL 4. KONZEPTION + + + + + + «interface» Texture getHeight() : int getImage() : BufferedImage getImageResource() : String getMagFilter() : int getMinFilter() : int getWidth() : int + 29 «interface» Texture3D getDepth() : int + + + «interface» TextureFactory createScreenTexture() : ScreenTexture createTexture2D() : Texture2D createTexture3D() : Texture3D «instantiate» «instantiate» + + + + + + + + + + + + + + + «interface» TextureGrid getCellHeight() : int getCells() : int getCellSpacing() : int getCellWidth() : int getCols() : int getRows() : int getSMax() : float getSMin() : float getSPerCell() : float getSPerSpacing() : float getTexture() : Texture2D getTMax() : float getTMin() : float getTPerCell() : float getTPerSpacing() : float «instantiate» + + + + + + 1 + + + + + «instantiate» «interface» Texture2D createTextureGrid() : TextureGrid createTextureGrid() : TextureGrid getImageHeight() : int getImageWidth() : int getSMax() : float getSubImageHeight() : int getSubImageWidth() : int getSubImageX() : int getSubImageY() : int getTMax() : float isMipMapped() : boolean «interface» ScreenTexture + render() : void 1..* Abbildung 4.5: UML-Klassendiagramm: Texturen KAPITEL 4. KONZEPTION StillSpriteData Ein leeres flag interface zur exakten Klassifizierung von Objekten der Klasse SpriteData in Einzelbild-Sprites (im Folgenden auch einfache“ Sprites genannt). ” AnimatedSpriteData Erweitert SpriteData um die Länge einer Animation und die Anzahl ihrer Wiederholungen. Sprite Ein renderbarer Sprite auf Basis eines SpriteData-Objekts. Da Sprites in ihrem Lebenszyklus von ihren initialen Daten abweichen können, lassen sich diese Daten mit Hilfe der reset-Methoden teilweise oder auch komplett auf ihre Grunddaten zurück setzen. Sprites werden explizit gerendert oder implizit innerhalb eines Terrains, auf dem sie registriert sind. StillSprite Ein leeres flag interface zur exakten Klassifizierung einfacher Sprites. AnimatedSprite Erweitert Sprite um die Belange animierter Sprites. Sie basieren analog auf AnimatedSpriteData-Objekten und enthalten Steuerungsmöglichkeiten wie start(), stop(), pause() und resume(). SpriteDataFactory Factory zur Erzeugung von Instanzen der StillSpriteData- und AnimatedSpriteData-Klassen. Die entsprechenden Methoden sind in hohem Maße überladen um alle sinnvollen Variantionen von Quellformat und Grunddaten abzudecken. 30 KAPITEL 4. KONZEPTION + + + + + + + + + + + + + + + + + + + + + + + + + + + + «interface» Sprite getBlendingColor() : Color getBlendingStrength() : float getData() : SpriteData getOpacity() : float getRotation() : float getScaling() : float getTerrainLocation() : TerrainLocation getUserTag() : Object isCentered() : boolean isOverblended() : boolean render() : void resetAll() : void resetBlendingColor() : void resetBlendingStrength() : void resetCentered() : void resetOpacity() : void resetOverblended() : void resetRotation() : void resetScaling() : void setBlendingColor() : void setBlendingStrength() : void setCentered() : void setOpacity() : void setOverblended() : void setRotation() : void setScaling() : void setTerrainLocation() : void setUserTag() : void 31 + + + + «instantiate» + + + + 1 + + «interface» StillSpriteData + + «instantiate» + + + + + + + + + + + + + «interface» AnimatedSprite getDuration() : long getLoops() : int isFinished() : boolean isPaused() : boolean isRunning() : boolean pause() : void resetDuration() : void resetLoops() : void restart() : void resume() : void start() : void stop() : void stopAfterLoop() : void «interface» SpriteData createSprite() : Sprite getDefaultBlendingColor() : Color getDefaultBlendingStrength() : float getDefaultOpacity() : float getDefaultRotation() : float getDefaultScaling() : float getHeight() : int getWidth() : int isDefaultCentered() : boolean isDefaultOverblended() : boolean «interface» AnimatedSpriteData getDefaultDuration() : long getDefaultLoops() : int «instantiate» «interface» SpriteDataFactory + + createAnimatedSpriteData() : AnimatedSpriteData createStillSpriteData() : StillSpriteData «interface» StillSprite Abbildung 4.6: UML-Klassendiagramm: Sprites KAPITEL 4. KONZEPTION 4.3.6 32 Terrains Terrains bestehen grundsätzlich aus einem regelmäßigen Gitter von Knotenpunkten oder Nodes, von denen jeweils vier ein Tile aufspannen. Jede dieser Nodes trägt Informationen zu Landhöhe, -typ und -normale sowie Wasserhöhe, -typ und -normale. Dadurch ist es möglich, Wasser als eine zweite Ebene mit variabler Transparenz über dem normalen Land darzustellen. Diese Trennung in Land- und Wasserflächen ermöglicht beispielsweise die Darstellung seichter Bäche (hohe Transparenz, naher Untergrund), tiefer Seen (geringe Transparenz, entfernter Untergrund) und noch tieferer Gewässer (keine Transparenz). Obwohl weniger naheliegend sind auch Vereisung, Sumpf oder Lava mittels Wasserflächen realisierbar. Zwischen den verschiedenen Land- bzw. Wassertypen sind fließende Übergänge vorgesehen. Benachbarte Nodes mit unterschiedlichen Typen lösen also einen ansprechenden Verlauf dieser Typen innerhalb der Tiles zwischen ihnen aus (s. Abbildung 4.7). Auf diese Weise muss ein Tile im Extremfall vier verschiedene Typen ineinander mischen. Abbildung 4.7: Verlauf von Land- bzw. Wassertypen Das Rendern der Terrains muss aus einer isometrischen Perspektive wie in Abschnitt 1.1 beschrieben stattfinden. Abbildung 4.8 stellt ein beispielhaftes Gelände aus 16x16 Nodes schematisch dar und zeigt zusätzlich, wie es durch eine Umsetzung des Frameworks gerendert aussehen könnte. Weiterhin können auf einem Terrain Sprites registriert werden, die beim Rendern des Terrains ebenfalls gerendert werden. Diese Sprites unterhalten je eine TerrainLocation als Position in der Welt. Es werden dabei zwei Arten von Sprites unterschieden: statische und dynamische (nicht zu KAPITEL 4. KONZEPTION Abbildung 4.8: Terrain schematisch und gerendet 33 KAPITEL 4. KONZEPTION 34 verwechseln mit einfachen und animierten). Unter statischen Sprites werden solche verstanden, die ihre Position innerhalb des Terrains (d. h. ihre TerrainLocation) nicht verändern. Unter dieser Annahme läßt sich das Rendern des Geländes beschleunigen. Solche Optimierungen sind bei dynamischen Sprites hingegen schwierig, denn sie bieten den Vorteil, ihre Position beliebig verändern zu dürfen. Abbildung 4.9 repräsentiert schließlich das UML-Klassendiagramm der Terrain-API. ReadableHeightmap Repräsentiert den lesenden Zugriff auf Höhendaten und Normalen. WritableHeightmap Repräsentiert den schreibenden Zugriff auf Höhendaten und Normalen. Heightmap Vereint die Funktionalitäten der Interfaces ReadableHeightmap und WritableHeightmap in einem Interface. DefaultHeightmap Standard-Implementierung des Heightmap-Interfaces. ReadableSurfaceMap Repräsentiert den lesenden Zugriff auf Land- und Wassertypen. WritableSurfaceMap Repräsentiert den schreibenden Zugriff auf Land- und Wassertypen. SurfaceMap Vereint die Funktionalitäten der Interfaces ReadableSurfaceMap und WritableSurfaceMap in einem Interface. KAPITEL 4. KONZEPTION DefaultSurfaceMap Standard-Implementierung des SurfaceMap-Interfaces. TerrainModel Stellt das grundlegende Modell eines Terrains dar. Verwaltet die dazu nötige Heightmap und SurfaceMap. DefaultTerrainModel Standard-Implementierung des TerrainModel-Interfaces. Terrain Ein renderbares Terrain. Es basiert auf einem TerrainModel und unterhält Listen der statischen und dynamischen Sprites, die auf ihm registriert sind. Außerdem ist das Setzen einer Lichtquelle und eines Viewports möglich, welche das Rendern beeinflussen. Weiterhin können Bildschirm-Koordinaten in das Gelände zurückgerechnet werden, um beispielsweise die Position des Mauszeigers auf einen Punkt innerhalb des Geländes abzubilden. Umgekehrt ist es möglich, eine solche TerrainLocation auf die Bildebene zu projezieren. Mit dieser Technik werden beispielsweise beim Rendern die Pixelpositionen der auf dem Terrain registrierten Sprites ermittelt. TerrainFactory Erzeugt Terrain-Instanzen. TerrainLocation Eine Position innerhalb eines Geländes. Sie besteht aus einer XYKoordinate, gemessen anhand der Tiles, sowie einem Z-Wert, der der Höhe innerhalb des Terrains entspricht. 4.3.7 Eingabe Visualisiert in Abbildung 4.10. 35 KAPITEL 4. KONZEPTION + + + + + + + + + + + + + + «interface» ReadableHeightmap getMaxSurfaceHeight() : float getMaxTotalHeight() : float getMaxWaterHeight() : float getMinSurfaceHeight() : float getMinTotalHeight() : float getMinWaterHeight() : float getNodesX() : int getNodesY() : int getSurfaceHeight() : float getSurfaceNormal() : Vector3f getTilesX() : int getTilesY() : int getWaterHeight() : float getWaterNormal() : Vector3f «interface» Heightmap + + + + 1 + + + + + + 36 «interface» WritableHeightmap baseSurfaceOnZero() : void calcSurfaceNormals() : void calcWaterNormals() : void scale() : void setSurfaceHeight() : void setWaterHeight() : void «interface» ReadableSurfaceMap + getNodesX() : int + getNodesY() : int + getSurface() : byte + getSurfaces() : byte + getTilesX() : int + getTilesY() : int + getWater() : byte + getWaters() : byte «interface» TerrainModel getHeightmap() : ReadableHeightmap getSurfaceMap() : ReadableSurfaceMap setHeightmap() : void setSurfaceMap() : void «interface» WritableSurfaceMap + setSurface() : void + setWater() : void «interface» SurfaceMap 1 1 DefaultHeightmap + «interface» Terrain DefaultTerrainModel «interface» TerrainFactory createTerrain() : Terrain «instantiate» + + + + + + + + + + + + + + + addDynamicSprite() : void addStaticSprite() : void clearDynamicSprites() : void clearStaticSprites() : void getDynamicSprites() : Iterator getLight() : Light getModel() : TerrainModel getStaticSprites() : Iterator getViewport() : Viewport projectSurface() : Point3i removeDynamicSprite() : void removeStaticSprite() : void render() : void setViewport() : void unprojectSurface() : boolean DefaultSurfaceMap TerrainLocation + + + + + + + + + + + + Abbildung 4.9: UML-Klassendiagramm: Terrains add() : void distance2D() : float distance3D() : float getX() : float getY() : float getZ() : float reset() : void set() : void setX() : void setY() : void setZ() : void toPoint() : Point3f KAPITEL 4. KONZEPTION 37 Keyboard Die Ereignisse der Tasten, also Drücken und Loslassen, werden gepuffert und können mittels next() durchlaufen werden. Ungepufferte Statusabfragen sind ebenfalls möglich. Insgesamt stehen drei verschiedene Möglichkeiten der Abfrage zur Verfügung: 1. Gepuffert: Das Ereignis einer Taste 2. Ungepuffert: Die Position einer Taste (gedrückt oder nicht) 3. Ungepuffert: Der Status von Caps-Lock, Num-Lock und ScrollLock Außerdem können die Namen der Tasten abgefragt werden. Mouse Wie bei der Tastatur werden auch bei der Maus die Ereignisse der Tasten, also Drücken und Loslassen, gepuffert und können mittels next() durchlaufen werden. Auch hier sind ungepufferte Statusabfragen möglich. Die Abfragen verlaufen dabei analog wie bei der Tastatur. Zusätzlich bietet dieses Interface die Möglichkeit, relative Bewegungen der Maus oder des Wheels (sofern vorhanden), sowie die absolute Position des Mauszeigers abzufragen. Die Anzahl der Tasten sowie das Vorhandensein eines Wheels können ebenfalls ermittelt werden. + + + + + + + + + «interface» Keyboard getEventCharacter() : char getEventKey() : int getKeyName() : String getKeyState() : int isEventKeyPressed() : boolean isKeyDown() : boolean keyPressed() : boolean keyReleased() : boolean next() : boolean + + + + + + + + + + + + + + + «interface» Mouse getButtonCount() : int getButtonIndex() : int getButtonName() : String getDWheel() : int getDX() : int getDY() : int getEventButton() : int getSensitivity() : float[] getX() : int getY() : int hasWheel() : boolean isButtonDown() : boolean isEventButtonPressed() : boolean next() : boolean setSensitivity() : void Abbildung 4.10: UML-Klassendiagramm: Eingabe Kapitel 5 Prototypische Umsetzung 5.1 Technologien Wie bereits in der Aufgabenstellung festgelegt basiert Mithril auf Java und OpenGL, um möglichst plattform-unabhängig zu sein. Die Anbindung an OpenGL steht in Java durch die Standard-Bibliotheken jedoch nicht zur Verfügung. Zu diesem Zweck gab und gibt es einige freie Entwicklungen als Open Source, von denen sich jedoch nur die beiden folgenden durchgesetzt haben: 5.1.1 JOGL (Java bindings for OpenGL) Beschreibung: The JOGL Project hosts a reference implementation of the Java bindings for OpenGL API, and is designed to provide hardwaresupported 3D graphics to applications written in Java. It is part of a suite of open-source technologies initiated by the Game Technology Group at Sun Microsystems. JOGL provides full access to the APIs in the OpenGL 1.5 specification as well as nearly all vendor extensions, and integrates with the AWT and Swing widget sets.[6] Lizenz: BSD (Berkeley Software Distribution License) Verwendung: JOGL stellt dem Benutzer eine Zeichenflächen-Komponente zur Verfügung, die er in eine AWT- oder Swing-Umgebung einbetten muss. Über diese Komponente hat er Zugang zur eigentlichen OpenGLAPI, realisiert über einen speziellen Kontext. Alle Aufrufe der dort bereitgestellten Methoden müssen im selben Thread geschehen wie die 38 KAPITEL 5. PROTOTYPISCHE UMSETZUNG 39 Anzeige der Zeichenfläche. JOGL unterstützt mehrere dieser Kontexte, und damit auch mehrere Zeichenflächen, innerhalb einer Anwendung. 5.1.2 LWJGL (Lightweight Java Game Library) Beschreibung: The Lightweight Java Game Library (LWJGL) is a solution aimed directly at professional and amateur Java programmers alike to enable commercial quality games to be written in Java. LWJGL provides developers access to high performance crossplatform libraries such as OpenGL (Open Graphics Library) and OpenAL (Open Audio Library) allowing for state of the art 3D games and 3D sound. Additionally LWJGL provides access to controllers such as Gamepads, Steering wheel and Joysticks. All in a simple and straight forward API.[3] Lizenz: GPL (GNU General Public License) Verwendung: LWJGL basiert auf einer eigenen Fenster-Klasse, d. h. es ist unabhängig von AWT oder Swing. Das Fenster ist hauptsächlich für den Einsatz im exklusiven Vollbild-Modus vorgesehen, kann jedoch mit einigen Einschränkungen auch im Fenster-Modus genutzt oder sogar in SWT1 -Umgebungen eingebunden werden. Die OpenGL-API wird durch eine Sammlung statischer Methoden repräsentiert, welche unabhängig vom einzigen2 Fensterkontext und thread-übergreifend aufgerufen werden können. In der gleichen Weise steht auch die Anbindung an OpenAL und an Eingabegeräte zur Verfügung. 5.1.3 Vergleich JOGL und LWJGL sind sich auf den ersten Blick sehr ähnlich, repräsentieren sie doch beide ein binding der OpenGL-API nach Java. Auch im Umfang der abgedeckten Befehle differieren sie kaum, beide erfüllen die OpenGLSpezifikation in der Version 1.5 und unterstützen darüber hinaus zahlreiche Erweiterungen einzelner Hersteller. Und wie zahlreiche Tests und Erfahrungsberichte aus der open source community belegen, liegen die beiden Systeme sogar in ihrer Performanz sehr dicht beeinander. Wirklich deutlich werden die Unterschiede zwischen ihnen erst im zugrundeliegenden Design. JOGL versucht sich möglichst nahtlos in die ereignis-basierenden Umgebungen AWT und Swing zu integrieren und gleichzeitig das stark imperative, 1 SWT = Standard Widget Toolkit [2] Mehrere Kontexte werden nicht unterstützt, da LWJGLs Hauptziel ohnehin der Vollbild-Modus ist. 2 KAPITEL 5. PROTOTYPISCHE UMSETZUNG 40 status-basierende OpenGL zu unterstützen. Dieser Balanceakt hat einige Kompromisse zur Folge, von denen der auffälligste die bereits angesprochene Abhängigkeit vom Renderthread der Zeichenfläche ist. LWJGL hingegen versteht sich als eine reine enabling technology: OpenGL soll für Java verfügbar gemacht werden, so direkt und unkompliziert wie möglich. Trotz dieser Differenzen haben beide Philosophien ihre Daseinsberechtigung, und die Wahl für eines der bindings muss der Anwender abhängig vom Verwendungszweck treffen. 5.1.4 Entscheidung Mithril wird in seiner prototypischen Implementierung auf LWJGL basieren. Diese Entscheidung begründet sich hauptsächlich auf den oben genannten Design-Unterschieden und deren Konsequenzen. LWJGL ist für die Entwicklung einer Spiele-Engine eindeutig besser geeignet, da sein Fokus genau wie der von Mithril eindeutig auf dem exklusiven Vollbild-Modus liegt: Spiele sind für gewöhnlich nicht auf mehrere Render-Kontexte angewiesen und werden auch nicht im Fenster ausgeführt. Die fehlende Integration in AWT und Swing ist also ein Vorteil, denn wie der Name schon sagt ( lightweight“) ” geht es bei LWJGL um eine einfache, schlanke und effiziente Bereitstellung der OpenGL-Funktionalität unter Java. Dieser Gedanke wird sogar bis auf die mögliche Verwendung auf mobilen Endgeräten ausgedehnt. Zwei weitere Pluspunkte, wenn auch nicht ausschlaggebend, sind die integrierte Unterstützung von OpenAL und die Abfragemöglichkeiten der Eingabegeräte. 5.2 5.2.1 Implementierung Allgemein Die Umsetzung besteht im Wesentlichen aus einer Sammlung von Interfaces (die sich sehr stark an der Konzeption orientieren) und einer StandardImplementierung unter der Verwendung von LWJGL. Durch diese starke Abstraktheit verbirgt Mithril die tatsächlich verwendete OpenGL-Anbindung nahezu vollständig vor dem Benutzer. Es wäre daher ohne weiteres möglich, eine zusätzliche Implementierung der Schnittstellen unter der Verwendung von JOGL zu realisieren. Der Benutzer des Frameworks könnte sich dann bei der Initialisierung der Engine für eine der verfügbaren Implementierungen entscheiden. Der Rest seiner Anwendung bliebe unverändert. Im Folgenden wird eine essentielle Komponente der prototypischen Umsetzung im Detail vorgestellt und die wesentliche Vorgehensweise bei der Implementierung beschrieben. KAPITEL 5. PROTOTYPISCHE UMSETZUNG 5.2.2 41 Terrain Innerhalb der gesamten prototypischen Umsetzung des Konzepts stellt die effiziente Darstellung der Terrains die größte Herausforderung dar. Je nach Auflösung und field of view müssen dazu hunderte oder sogar tausende von texturierten und beleuchteten Tiles gerendert werden. Hinzu kommt eine unbestimmte Anzahl von statischen und dynamischen Sprites, die auf dem Terrain registriert sind. Da es sich bei den Tiles um Vierecke handelt, liegt es nahe, die OpenGLPrimitiven QUAD oder QUAD_STRIP zu nutzen. Aufgrund der hohen Redundanzen der Vertizes (ein Vertex stellt ja meist einen Eckpunkt in vier verschiedenen Tiles dar) wäre es jedoch sehr ineffizient, ihre Koordinaten mehrfach an die OpenGL-Pipeline zu übergeben. Aus diesem Grund wurden in OpenGL 1.1 Vertex Arrays eingeführt (vgl. [1]). Mit diesen ist es möglich, Eckpunktdaten wie • Koordinate, • Farbe, • Normale und • Texturkoordinate(n) gebündelt an die Hardware weiterzugeben. Diese Daten lassen sich dann rendern, als ob sie in entsprechender Form innerhalb der klassischen Befehle glBegin() und glEnd() angegeben wären. Dies führt zwar bereits zu einer leichten Steigerung der Geschwindigkeit, beseitigt aber noch nicht die angesprochenen Redundanzen. Zu diesem Zweck kann zusätzlich eine Menge von Indizes spezifiziert werden. Diese beschreiben die Reihenfolge, in denen die übergebenen Eckpunktdaten verwendet werden sollen. Auf diese Weise können identische Vertizes mehrfach referenziert werden und müssen nicht als Kopien an die Pipeline übergeben werden. Mithril verwendet für die Terrains daher Vertex Arrays inklusive Indizes zur effizienten Darstellung von QUAD_STRIPs. KAPITEL 5. PROTOTYPISCHE UMSETZUNG 42 Erzeugung eines Terrains Die Umsetzung des TerrainFactory-Interfaces (s. Abbildung 4.9) sieht die Methode Terrain createTerrain(TerrainModel model, Light light, Texture2D[] surfaces, Texture2D[] waterPairs, Viewport viewport); vor. Über diese kann sich der Benutzer ein Terrain-Objekt erzeugen lassen. Dazu muss er die folgenden Parameter übergeben: TerrainModel model: Standardmäßig ist dies eine DefaultTerrainModelInstanz, basierend auf Instanzen der Klassen DefaultHeightmap und DefaultSurfaceMap. Natürlich könnte der Anwender auch eigene Realisierungen der entsprechenden Interfaces als Instanzen übergeben. Light light: Die Lichtquelle, die das Terrain beleuchtet. Texture2D[] surfaces: Je eine zweidimensionale Textur pro möglichem Landtyp der im Modell enthaltenen SurfaceMap. Texture2D[] waterPairs: Je zwei zweidimensionale Texturen pro möglichem Wassertyp der im Modell enthaltenen SurfaceMap, da Wasser mit Multitexturing dargestellt wird. Viewport viewport: Der Bildschirmausschnitt, in welchem das Terrain gerendert werden soll. Bei dieser Erzeugung werden intern Optimierungsstrukturen über den in model gekapselten Geländedaten aufgebaut. Dabei werden die Tiles in ein zweidimenionales Feld von Pages eingeteilt. Eine Page deckt 162 Tiles in einem rechteckigen Bereich ab. Die Pages werden anschließend ihrerseits zu PageProxys zusammengefasst, von denen jeder 42 Pages umfasst. Abbildung 5.1 zeigt diesen Vorgang. Jede Page unterhält eigene Vertex Arrays zu den Tiles, die sie beinhaltet. Es gibt dabei pro Page genau ein Vertex-Array3 , ein Normalen-Array und ein Texturkoordinaten-Array. Zusätzlich existiert für jeden Land- bzw. Wassertyp genau ein Farb-Array. Die genannten Arrays umfassen dabei jeweils alle 162 Tiles bzw. 172 Nodes. Die Bildung der ersten drei Arrays ist trivial, 3 Also ein Array mit Eckpunkt-Koordinaten, nicht zu verwechseln mit dem Oberbegriff Vertex Arrays“, der alle Arrays umfasst ” KAPITEL 5. PROTOTYPISCHE UMSETZUNG 43 Abbildung 5.1: Tiles, Pages und PageProxys da sie einfach nur die entsprechenden Informationen der beteiligten Nodes enthalten. Die Farb-Arrays hingegen repräsentieren die räumliche Verteilung der Land- bzw. Wassertypen innerhalb der Page. Dabei werden pro Typ die RGB-Werte aller Nodes auf 1 gesetzt, also auf Weiß. Erst der Alphawert trägt die tatsächlich relevante Information: stimmt der jeweils betrachtete Typ mit dem der Node überein, wird der Alphawert auf 1 gesetzt, anderenfalls auf 0. Abbildung 5.2 zeigt beispielhaft die Alphawerte des Farb-Arrays, welches zum Landtyp Grün“ gehört. ” Abbildung 5.2: Alphawerte der Nodes eines Farb-Arrays Die unterschiedlichen Alphawerte realisieren die Übergänge zwischen den Land- bzw. Wassertypen. Dazu werden pro Typ alle zugehörigen Tiles gerendert, also nur solche, bei denen mindestens eine Node dem aktuellen Typ KAPITEL 5. PROTOTYPISCHE UMSETZUNG 44 entspricht. Diese Reduzierung der zu rendernden Tiles auf zusammenhängende Segmente geschiet mittels Index-Arrays. Je nachdem wie stark eine Page durch verschiedene Typen fragmentiert ist, werden also mehr oder weniger Index-Arrays benötigt. Durch das sequentielle Rendern aller Segmente aller Typen ergibt sich aufgrund der Alphawerte ein Gesamtbild der Page mit ansprechenden Übergängen zwischen den Typen. Rendern eines Terrains Der Anwender hat in seiner Realisierung des Renderer-Interfaces die Möglichkeit, das erzeugte Terrain darzustellen. Dies geschieht durch einen Aufruf der render()-Methode des Terrains: void render(float tileX, float tileY, float z); Diese rendert das Terrain zentriert innerhalb des gesetzten Viewports über der mit tileX, tileY und z beschriebenen Geländekoordinate. Vereinfacht ausgedrückt läuft das Rendering wie folgt ab. Zunächst werden die EckVertizes der PageProxys in Bildschirmkoordinaten projeziert und gegen den Viewport getestet. Liegt ein PageProxy komplett außerhalb des Viewports, ist er nicht sichtbar und muss folglich auch nicht gerendert werden. Wird ein PageProxy jedoch als möglicherweise sichtbar klassifiziert, so werden nun alle in ihm enthaltenen Pages auf die gleiche Weise gegen den Viewport getestet. Wieder werden die nicht sichtbaren Pages verworfen, während die möglicherweise sichtbaren in einer sortierten Liste gespeichert werden. Diese Sortierung erfolgt von hinten nach vorne aus Sicht der isometrischen Perspektive. Sind alle PageProxys (und Pages) durchlaufen, werden die Pages in der Liste nacheinander gerendert. Dabei werden zuerst die Landtypen dargestellt und dann die Wassertypen. Abschließend werden die registrierten Sprites pageweise gerendert, ebenfalls von hinten nach vorne sortiert. Abbildung 5.3 zeigt die Unterteilung des Terrains in Tiles und Pages, sowie das Weglassen ganzer Pages außerhalb des Viewports. Der Viewport ist durch das rote Rechteck visualisiert und umfasst normalerweise den ganzen Bildschirm. In dieser Einstellung wurde sein Pixel-Clipping zum besseren Verständnis ausgeschaltet. Der gesamte Renderingprozess wird in den folgenden vier Flussdiagrammen noch einmal detailiert dargestellt: Abbildung 5.4: Auswahl der sichtbaren PageProxys Abbildung 5.5: Auswahl der sichtbaren Pages Abbildung 5.6: Rendern der sichtbaren Pages Abbildung 5.7: Rendern der sichtbaren Sprites KAPITEL 5. PROTOTYPISCHE UMSETZUNG Abbildung 5.3: Tiles, Pages und Viewport 45 KAPITEL 5. PROTOTYPISCHE UMSETZUNG Abbildung 5.4: Rendern eines Terrains (Diagramm 1 von 4) 46 KAPITEL 5. PROTOTYPISCHE UMSETZUNG Abbildung 5.5: Rendern eines Terrains (Diagramm 2 von 4) 47 KAPITEL 5. PROTOTYPISCHE UMSETZUNG Abbildung 5.6: Rendern eines Terrains (Diagramm 3 von 4) 48 KAPITEL 5. PROTOTYPISCHE UMSETZUNG Abbildung 5.7: Rendern eines Terrains (Diagramm 4 von 4) 49 Kapitel 6 Implementierung des Demospiels 6.1 6.1.1 Design Allgemein Namensfindung Das Spiel wird auf Run To The Hills getauft. Dieser Titel beruht einerseits auf der hügeligen Beschaffenheit des Spielareals, andererseits auf dem gleichnamigen Song der Band Iron Maiden aus dem Jahre 1982. Hintergrund Verschiedene Fraktionen wollen einen Planeten besiedeln. Dazu haben sie jeweils eine stationäre, nicht einnehmbare Basis auf der Oberfläche errichtet. Von dieser aus entsenden sie holographische Projektionen ihrer selbst, um Territorium für sich einzunehmen. Ziel Die Spieler versuchen Flaggenpunkte der gegnerischen Teams einzunehmen und die eigenen Flaggen zu verteidigen. Punkte sammeln sie, indem sie Flaggen zum eigenen Team umfärben oder andere Spieler zu ihren Spawnpunkten zurückschicken. Das Team, welches als erstes eine bestimmte Punktezahl erreicht, gewinnt die laufende Runde. Ist in einer Welt eine voreingestellte Anzahl von Runden gespielt worden, beginnt ein neues Spiel in einer neuen Welt. 50 KAPITEL 6. IMPLEMENTIERUNG DES DEMOSPIELS 51 Ein möglicher Spielablauf Der Spieler startet die Client Applikation und wird nach seinem Namen gefragt und nennt sich Dude. Zusätzlich kann er noch weitere Einstellungen, die seinen Rechner betreffen, angeben. Danach verbindet er sich mit einem ihm bekannten Server, auf dem bereits ein Spiel im Gange ist. Es befinden sich 23 Spieler, aufgeteilt in drei Teams (Rot, Grün und Blau) auf dem Server. Da Team Grün zur Zeit zahlenmäßig unterlegen ist, wird Dude automatisch diesem Team zugewiesen. Jetzt wählt Dude seine Rolle, die er im Spiel ausüben will. Er entscheidet sich für den schnellen jedoch mit schwachem Energieschild ausgerüsteten Angreifer. Außerdem bestimmt er Flagge 4 als seinen Startpunkt, da Team Grün bereits mehrere Flaggen kontrolliert. Er wartet die verbleiben Sekunden seiner Spawnzeit ab. [. . . ] Dude erscheint in der Nähe der Flagge 4. Diese befindet sich auf einer kleinen, bewaldeten Insel. Zusammen mit zwei Teamkollegen watet er durch das seichte Küstenwasser in Richtung Flagge 6, welche gerade von fünf Spielern aus Team Rot kontrolliert wird. Der folgende Ansturm ist kurz und erfolglos: Dude und seine Mitangreifer werden durch Antiprotonenstrahlen aufgelöst. Die drei glücklosen Angreifer müssen erneut einen Startpunkt und eine Rolle wählen, sowie einige Sekunden auf die Rekalibrierung ihrer Projektionsmatrix warten. [. . . ] Eine halbe Stunde später endet die Runde mit einem Sieg von Team Rot! 6.1.2 Anforderungsanalyse √ und × siehe Abschnitt 6.2. Zur Bedeutung der Symbole Allgemein • Große, zufällige Spielwelten • Viele Spieler √ √ • Mehr als zwei Teams √ • Spieler sind unbelebte Hologramme √ • Spieler starten bei festgelegten Flaggenpunkten √ • Gegnerische Flaggenpunkte einnehmen um Punkte zu erzielen • Eigene Flaggenpunkte verteidigen √ • Anzahl der Flaggen richtet sich nach Weltgröße × √ KAPITEL 6. IMPLEMENTIERUNG DES DEMOSPIELS 52 • Einteilung in Runden und Welten × • Möglichkeit, andere Spieler zu einem √Spawnpunkt (Startpunkt) zurückzuschicken um Punkte zu erzielen • Verschiedene Spielerrollen × • Verschiedene Ausrüstungsgegenstände × • Bonuskisten × • Chatfunktion (Texte versenden, global oder teamintern) √ • Teaminterne Schnellkommandos per Tastenkombination × • Punktelisten / Highscore, sowohl für Team als auch Einzelspieler × • Statistiken × • Continuous World (Spiel ist immer offen) √ • Spectators (Zuschauer) sind erlaubt × • Verschiedene Serveroptionen √ • Verschiedene Clientoptionen, wie Bildschirmauflösung und Farbtiefe Der Server Bei Serverstart werden unter anderem festgelegt: • Weltdimension, z. B. 2562 Tiles √ • Maximale Spielerzahl (Kapazität), z. B. 128 • Anzahl der Teams, zwischen 2 und 8 √ √ • Maximale Punkte pro Runde, z. B. 100 × • Runden pro Welt, z. B. 3 × • Friendly Fire-Anteil, z. B. 50% × Zur Laufzeit stehen folgende Funktionen zur Verfügung: √ KAPITEL 6. IMPLEMENTIERUNG DES DEMOSPIELS 53 Administration Über ein Webfrontend können Spieler in andere Teams eingeteilt, oder vom Server gekickt (entfernt) oder gebanned (dauerhaft ausgeschlossen) werden. Außerdem können Mitteilungen an die Spieler gesendet werden. × Auto Balancing Bei Eintritt in die Welt wird dem neuen Spieler ein Team vorgeschlagen. √ Dieser Vorschlag richten sich nach der aktuellen Teambalance. Die Welt • Sämtliche Eigenschaften werden zufällig generiert √ • Verschiedene Themen, wie Mond, Erde und Fantasiewelt × • Verschiedene Landschaften, am Beispiel Erde: – Animiertes Wasser (Meere, Flüsse, Seen) – Küste √ – Grasland – Wald √ √ √ – Gebirge √ • Stufenloses Höhenprofil √ • Wege und Straßen × • Einfluss der Oberflächenbeschaffenheit auf den Spielfluss × • Persistente Veränderungen von Höhenprofil und Oberflächen × • Animierbare Objekte, wie Spieler, Bäume und Kisten Der Spieler • Name √ • Teamzugehörigkeit / Farbe • Punkte pro Runde × • Rang pro Welt × √ √ KAPITEL 6. IMPLEMENTIERUNG DES DEMOSPIELS • Position in der Welt (x, y, z) • Ausrichtung 54 √ √ • Geschwindigkeit × • Rolle, wie Angreifer und Verteidiger × • Ausrüstung, wie Waffen und Schutzschild × • Hitpoints √ Das Team • Farbe, ev. auch Wappen √ • Punkte pro Runde × • Gewonnene Runden pro Welt × • Homebase, Holocore, Heimatpunkt. Dieser ist nicht von anderen Teams √ einnehmbar Punktewertung Es gibt zwei Möglichkeiten, Punkte zu erlangen: 1. Desintegrieren eines Gegners Für den Spieler 1 Punkt, für das Team ebenfalls 1 Punkt × 2. Umfärben einer Flagge Für den Spieler 2 Punkte, für das Team sogar 2 Punkte pro beteiligtem Spieler × 6.2 Implementierung Wie in der Aufgabenstellung festgelegt, wurde das Demospiel in Zusammenarbeit mit Christian Stein entwickelt, auf dessen Diplomarbeit in diesem Zusammenhang verwiesen wird.[5] Aufgrund der begrenzten Zeit, die für die Implementierung zur Verfügung stand, konnten nicht alle der angedachten Spielinhalte verwirklicht werden. Um zu zeigen, welche Zielsetzungen aus dem vorigen Abschnitt erreicht wurden und welche nicht, sind diese mit √ entsprechenden Symbolen gekennzeichnet. Ein steht für eine erfolgreiche Umsetzung, ein × für ein Fehlen innerhalb des Spiels. Für anspruchsvollere KAPITEL 6. IMPLEMENTIERUNG DES DEMOSPIELS 55 Graphiken, d. h. Texturen und Sprites, stand ebenfalls nicht genug Zeit zur Verfügung. Beispielsweise sollte die Spielfigur statt der Mensch-ärger-dich” nicht“-Variante flüssig animiert und in verschiedenen Rotationen gerendert sein. 6.3 Screenshots Die folgenden Screenshots wurden bei einer Auflösung von 1024x768 Pixeln und 32 bit Farbtiefe aufgenommen. Sie sollen den Funktionsumfang und die Qualität von Mithril im Bereich der Graphik demonstrieren. Eine Analyse der Performanz wird an dieser Stelle nicht gegeben, da dies im Kapitel 7 erfolgt. KAPITEL 6. IMPLEMENTIERUNG DES DEMOSPIELS 56 Abbildung 6.1: Übergang von seichtem in tiefes Wasser. Der Strand besteht aus zwei unterschiedlichen Landtypen (trockener und feuchter Sand). Dazu kommt ansatzweise Gras als dritter Landtyp. Unter dem Wasser liegt in Küstennähe der feuchte Sand, weiter draußen ein dunkler Meeresgrund als insgesamt vierter Landtyp in diesem Bild KAPITEL 6. IMPLEMENTIERUNG DES DEMOSPIELS 57 Abbildung 6.2: Strand und seichtes Wasser. Die verwendeten Landtypen entsprechen denen aus Abbildung 6.1 KAPITEL 6. IMPLEMENTIERUNG DES DEMOSPIELS 58 Abbildung 6.3: Übergang von Strand nach Wald. Der Wald besteht aus statischen Sprites (inklusive Schatten per Alphakanal) und Gras als Landtyp KAPITEL 6. IMPLEMENTIERUNG DES DEMOSPIELS 59 Abbildung 6.4: Wald mit Flagge“. Um sie herum ist die gegenseitige Ver” deckung der Sprites durch ihre Sortierung besonders gut zu erkennen. Neben der Minimap“ in der Ecke ist ein Symbol zu sehen, welches die Nähe des ” Spielers zu einer Flagge anzeigt KAPITEL 6. IMPLEMENTIERUNG DES DEMOSPIELS 60 Abbildung 6.5: Übergänge zwischen mehreren Landtypen. Am unteren Bildrand ist eine Flagge in der Farbe des Spielers zu sehen KAPITEL 6. IMPLEMENTIERUNG DES DEMOSPIELS 61 Abbildung 6.6: Neutrale Flagge in hügeligem Gelände. Der orangene Spieler ist gerade dabei, sie für sein Team umzufärben“. Hier kann man erkennen, ” daß die Flaggenkristalle transparent sind, denn der verdeckte Spieler scheint leicht hindurch Kapitel 7 Auswertung der Ergebnisse 7.1 Erreichte Ziele Folgende Zielsetzungen der Anforderungsanalyse (s. Kapitel 3) konnten in der prototypischen Umsetzung erreicht bzw. nicht erreicht werden: 7.1.1 Allgemein Effizienz: Die Engine muss schnell sein und mit Ressourcen sparsam umgehen. Dies ist besonders wichtig, weil sie ja nur einen von vielen Teilen eines Spiels darstellt. Dieser Aspekt wurde stark priorisiert und konnte erfolgreich umgesetzt werden (s. Abschnitt 7.2). Visuelle Qualität: Um eine hohe visuelle Qualität zu erreichen, sollen die Fähigkeiten der Graphikhardware möglichst gut ausgenutzt werden. Dabei sollte Mithril adaptiv vorgehen und sich in seinem Vorgehen der jeweiligen Hardware anpassen. Diese Forderung wird nur ansatzweise erfüllt. Durch die Verwendung von Shadern könnten die Fähigkeiten der jeweiligen Graphikhardware in einigen Fällen besser genutzt werden. Flexibilität: Dem Benutzer soll ein hohes Maß an Freiheit geboten werden, die Engine an seine Bedürfnisse anzupassen. Ebenso sollen eigene Erweiterungen durch wohldefinierte Schnittstellen möglich sein. Es steht dem Benutzer frei, einige der Schnittstellen in Mithril selber zu implementieren, anstatt Standard-Implementierungen zu verwenden. Als Beispiel sei DefaultHeightmap genannt. Weiterhin sind viele Aspekte der Engine konfigurierbar, wie z. B. das field of view. Die 62 KAPITEL 7. AUSWERTUNG DER ERGEBNISSE 63 einzelnen Komponenten (wie Sprites und Terrains) sind in ihrer Verwendung möglichst flexibel gehalten, um den Benutzer nicht unnötig einzuschränken. Abstraktheit: Da Mithril ein High-Level-Framework darstellt, soll die tatsächlich verwendete Anbindung an OpenGL vor dem Benutzer verborgen werden. Zusätzlich soll es trotzdem möglich sein, über eine Schnittstelle Low-LevelOpenGL-Befehle aufzurufen. Die verwendete OpenGL-Anbindung wird vollständig vor dem Benutzer verborgen. Eine Schnittstelle für explizite OpenGL-Befehlsaufrufe wurde jedoch nicht umgesetzt. Plattform-Unabhängigkeit: Mithril soll auf unterschiedlichen Betriebssystemen laufen und sich dabei möglichst gleich verhalten. Anvisiert werden hauptsächlich Windows und Linux. Diese Forderung konnte komplett erfüllt werden. Zusätzlich sind auch andere, OpenGL- und Java-fähige Systeme als Zielplattformen denkbar. Kompatibilität: Es werden Mindestanforderungen an die Graphikhardware festgelegt. Solange diese erfüllt sind, soll jede beliebige Hardware unterstützt werden. Es wurden keine Mindestanforderungen explizit festgelegt. Dennoch wurde bei der Entwicklung viel Wert auf Kompatibilität gelegt und diese durch ständige Tests auf unterschiedlicher Hardware auch erreicht: es hat sich gezeigt, dass die Umsetzung auf nahezu allen gängigen Graphikchips bzw. -karten läuft. Stabilität: Die Engine soll robust und fehlertolerant sein. Transparenz ist in diesem Zusammenhang besonders wichtig, so dass der Benutzer auf eventuelle Fehler gezielt reagieren kann. Außerdem soll das System mittels detailiertem Logging leicht zu debuggen sein. Die Engine gibt tolerierbare Fehler als Ausnahmen (engl. exceptions) an den Benutzer weiter, so dass er darauf reagieren kann. Fatale Fehler führen zu einer möglichst sauberen“ Beendigung des Frameworks. ” Das integrierte Logging ist tatsächlich sehr detailiert und ermöglicht eine schnelle und gezielte Fehlersuche. 7.1.2 √ Graphik Ein steht für eine erfolgreiche Umsetzung, ein × für ein Fehlen innerhalb der Engine. KAPITEL 7. AUSWERTUNG DER ERGEBNISSE 64 Landschaften: • Großer Umfang √ • Stufenloses Höhenprofil √ • Natürlich wirkender Übergang zwischen √ verschiedenen Oberflächen, wie Vegetation, Gestein und Schnee • Animierbare Wasserflächen, wie Meere, Seen und Flüsse • Wege und Straßen √ √ • Persistente√ Veränderungen zur Laufzeit, wie Verformungen und Verfärbungen Innenbereiche: × Figuren und Objekte: • Einzelobjekte, wie Charaktere, Fahrzeuge, Vegetation und Gebäude √ • Fortlaufende Objekte, wie Zäune, Mauern und Stromleitungen × Effekte: Animierbare Spezialeffekte, wie Explosionen, Feuer und Magie. √ Animationen: • Präsentation: Einzelereignis, Ereignissequenz und fortlaufendes Ereig√ nis • Steuerung: zeit- und ereignisbasierend √ Verhalten bei verschiedenen Auflösungen: • Skalierung des Sichtbereichs → Keine Skalierung der Graphikelemen√ te • Keine Skalierung des Sichtbereichs → Skalierung der Graphikelemente × Selektion: Es müssen vielseitige Selektionsmöglichkeiten zur Verfügung stehen, um Bildschirmkoordinaten (beispielsweise die Position des Mauszeigers) auf Objekte (d. h. Sprites) und Objektkoordinaten innerhalb von Terrains und Innenbereichen abbilden zu können. Objektkoordinaten innerhalb eines Terrains können in Bildschirmkoordinaten projeziert werden und umgekehrt. Die Abbildung von Bildschirmkoordinaten auf Sprites ist angedacht, aber nicht realisiert worden. KAPITEL 7. AUSWERTUNG DER ERGEBNISSE 7.1.3 65 Eingabe Da die Eingabebehandlung über den Fensterkontext des Betriebssystems geschieht, muss Mithril ebenfalls komfortable Schnittstellen zu Tastatur, Maus und anderen Eingabegeräten bereitstellen. Maus- und Tastatureingaben können flexibel abgefragt werden. Die Unterstützung weiterer Eingabegeräte wie Gamepads ist vorgesehen. 7.2 Effizienzanalyse Basierend auf der prototypischen Umsetzung wurde ein Benchmark -Programm erstellt, welches an zahlreiche Testpersonen verteilt wurde. Insgesamt konnten auf diese Weise Messdaten von 21 unterschiedlichen Rechnerkonfigurationen gesammelt werden. Die als relevant betrachteten Systemeigenschaften umfassen dabei Betriebssystem, Prozessor(en), Hauptspeichergröße und -typ, Version und Hersteller der Java Virtual Machine sowie natürlich den Graphikchip bzw. die Graphikkarte. 7.2.1 Aufbau des Tests Der Test läuft ausschließlich und durchgehend mit einer Bildschirmauflösung von 1024x768 Pixeln. Die dabei durchgeführten Messungen gliedern sich in 10 Phasen: 1. Einfache Sprites I: Ein einfacher Sprite der Größe 642 Pixel wird 192 mal in einem bildschirmfüllenden Gitter von 16x12 Zellen gerendert. Der Sprite ist vollständig opaque. 2. Einfache Sprites II: Ein einfacher Sprite der Größe 642 Pixel wird 500 mal an zufälligen Positionen gerendert. Der Sprite ist vollständig opaque. 3. Einfache Sprites III: Ein einfacher Sprite der Größe 642 Pixel wird 2000 mal an zufälligen Positionen gerendert. Der Sprite ist vollständig opaque. 4. Animierte Sprites I: 192 animierte Sprites werden in einem bildschirmfüllenden Gitter von 16x12 Zellen gerendert. Alle referenzieren auf dieselben Spritedaten und laufen in einer Endlosschleife ab. Die Animation ist im Ganzen zu ca. 75% transparent und misst 642 Pixel. KAPITEL 7. AUSWERTUNG DER ERGEBNISSE 66 5. Animierte Sprites II: 500 animierte Sprites werden an zufälligen Positionen gerendert. Alle referenzieren auf dieselben Spritedaten und laufen in einer Endlosschleife ab. Die Animation ist im Ganzen zu ca. 75% transparent und misst 642 Pixel. 6. Animierte Sprites III: 2000 animierte Sprites werden an zufälligen Positionen gerendert. Alle referenzieren auf dieselben Spritedaten und laufen in einer Endlosschleife ab. Die Animation ist im Ganzen zu ca. 75% transparent und misst 642 Pixel. 7. Terrain I: Rendern eines Terrains mit einem Landtyp ohne Wasser. Normales field of view, welches in ca. 1500 Tiles resultiert. 8. Terrain II: Rendern eines Terrains mit einem Landtyp sowie durchgehendem Wasser eines Typs. Normales field of view, welches in ca. 1500 Tiles resultiert. 9. Terrain III: Wie Terrain I“, aber erweitert um 500 registrierte sta” tische Sprites, zu gleichen Teilen einfache und animierte. 10. Terrain IV: Wie Terrain II“, aber erweitert um 500 registrierte sta” tische Sprites, zu gleichen Teilen einfache und animierte. Jede Phase dauert 20 Sekunden an. In dieser Zeit wird der jeweilige Inhalt so oft wie möglich gerendert. Die Einzelbilder (engl. frames) werden aufsummiert und durch die Phasendauer geteilt, um die Einzelbilder pro Sekunde (engl. frames per second, kurz FPS ) zu ermitteln. Der Test simuliert allerdings kein echtes Spiel, in dem ja auch andere Komponenten Systemresourcen beanspruchen würden. Die Entwicklung eines derart ausgefeilten Benchmarks hätte die dafür zur Verfügung stehende Zeit bei weitem überschritten. Statt dessen werden die Kernfunktionalitäten von Mithril einem Stress-Test unterzogen, der die pure Graphikleistung ermittelt. 7.2.2 Ergebnisse in Zahlen Die folgenden Diagramme 7.1, 7.2 und 7.3 stellen die gesammelten Messdaten nach Themen gruppiert dar. Die Reihenfolge der Testsysteme ist in allen Diagrammen gleich und entspricht einer Sortierung gemäß der Leistung in Phase 1. KAPITEL 7. AUSWERTUNG DER ERGEBNISSE AMD Athlon XP 2500+, 1024 MB DDR, ATI Radeon 9800 Pro, Windows 2000, VM: 1.4.2_04-b05 67 104,4 373,3 1086,3 Intel Pentium 4 3 GHz, 1024 MB DDR, ATI Radeon 9800 Pro, Windows XP, VM: 1.4.2_03-b02 89,3 367,0 1085,0 Intel Pentium 4 HT 2,4 GHz, 1024 MB DDR, ATI Radeon 9700 Pro, Windows XP, VM: 1.4.2_04-b05 81,9 297,3 982,3 AMD Athlon XP-M 2400+, 512 MB SDRAM, NVIDIA GeForceFX 5900 XT, Windows XP, VM: 1.4.2_05-b04 98,5 350,4 952,3 Intel Dual Xeon 2,8 GHz, 1024 MB DDR, NVIDIA GeForceFX 5950 Ultra, Windows XP, VM: 1.4.2_03-b02 68,5 AMD Athlon XP 1800+, 768 MB DDR, NVIDIA GeForce4 Ti4200, Windows XP, VM: 1.4.2_01-b06 67,0 AMD Athlon XP 2600+, 1024 MB DDR, NVIDIA GeForce4 Ti4200, Windows 2000, VM: 1.4.2_05-b04 66,7 AMD Athlon XP 1600+, 512 MB DDR, NVIDIA GeForce4 Ti4200, Windows XP, VM: 1.4.2_03-b02 65,5 246,1 826,1 238,7 622,7 237,6 620,5 230,8 604,8 AMD Athlon XP 1800+, 512 MB DDR, NVIDIA GeForce4 Ti4200, Windows 2000, VM: 1.4.2_04-b05 56,7 AMD Athlon XP 2600+, 1024 MB DDR, NVIDIA GeForce4 Ti4200, Windows XP, VM: 1.4.2_05-b04 54,0 206,0 548,1 195,7 522,6 AMD Athlon XP 2500+, 1024 MB DDR, ATI Radeon 9600 Pro, Windows XP, VM: 1.4.2_05-b04 65,3 230,1 509,4 55,7 AMD Athlon 900 MHz, 512 MB SDRAM, ATI Radeon 9600 Pro, Windows 2000, VM: 1.4.2_03-b02 192,3 414,7 Intel Mobile Pentium 1,7 GHz, 2048 MB DDR, ATI Mobility Fire GL T2, Windows XP, VM: 1.4.2 (IBM) 40,4 145,5 313,0 Intel Mobile Pentium 4 2 GHz, 512 MB DDR, NVIDIA Quadro4 500 GoGL, Windows 2000, VM: 1.4.2_05-b04 30,1 Intel Mobile Pentium 4 2 GHz, 1024 MB DDR, ATI Mobility Radeon 9000, Windows XP, VM: 1.4.2_01-b06 34,0 Intel Pentium 4 1,8 GHz, 512 MB DDR, ATI Radeon 8500, Windows XP, VM: 1.4.1_02-b06 37,2 110,8 293,9 124,8 289,9 119,8 228,0 Intel Mobile Pentium 1,6 GHz, 512 MB SDRAM, ATI Mobility Radeon 9600, Windows XP, VM: 1.4.2_04-b05 22,2 82,3 198,8 AMD Athlon XP 2500+, 1024 MB DDR, ATI Radeon 9600 Pro, Linux 2.6.8, VM: 1.4.2_05-b04 51,1 129,0 192,6 18,1 Intel Pentium 4 1,6 GHz, 256 MB DDR, ATI Mobility Radeon 7500, Windows XP, VM: 1.4.1_02-b06 67,1 159,3 Intel Mobile Pentium 4 1,8 GHz, 512 MB SDRAM, SiS M650, Windows 2000, VM: 1.4.2_04-b05 31,7 Intel Mobile Pentium 3 900 MHz, 256 MB SDRAM, NVIDIA Go, Windows XP, VM: 1.4.2_03-b02 10,9 40,1 107,7 138,8 92,9 0,0 200,0 400,0 600,0 800,0 1000,0 FPS (Frames Per Second) 192 einfache Sprites 500 einfache Sprites 2000 einfache Sprites Abbildung 7.1: Einfache Sprites in verschiedener Anzahl 1200,0 KAPITEL 7. AUSWERTUNG DER ERGEBNISSE AMD Athlon XP 2500+, 1024 MB DDR, ATI Radeon 9800 Pro, Windows 2000, VM: 1.4.2_04-b05 68 88,6 359,3 1160,9 Intel Pentium 4 3 GHz, 1024 MB DDR, ATI Radeon 9800 Pro, Windows XP, VM: 1.4.2_03-b02 77,7 Intel Pentium 4 HT 2,4 GHz, 1024 MB DDR, ATI Radeon 9700 Pro, Windows XP, VM: 1.4.2_04-b05 73,1 316,0 1075,8 271,1 874,1 AMD Athlon XP-M 2400+, 512 MB SDRAM, NVIDIA GeForceFX 5900 XT, Windows XP, VM: 1.4.2_05-b04 90,1 319,3 844,6 Intel Dual Xeon 2,8 GHz, 1024 MB DDR, NVIDIA GeForceFX 5950 Ultra, Windows XP, VM: 1.4.2_03-b02 58,4 218,0 751,1 AMD Athlon XP 1800+, 768 MB DDR, NVIDIA GeForce4 Ti4200, Windows XP, VM: 1.4.2_01-b06 72,1 261,2 607,7 AMD Athlon XP 2600+, 1024 MB DDR, NVIDIA GeForce4 Ti4200, Windows 2000, VM: 1.4.2_05-b04 89,1 312,2 606,4 AMD Athlon XP 1600+, 512 MB DDR, NVIDIA GeForce4 Ti4200, Windows XP, VM: 1.4.2_03-b02 62,8 209,9 548,8 AMD Athlon XP 1800+, 512 MB DDR, NVIDIA GeForce4 Ti4200, Windows 2000, VM: 1.4.2_04-b05 85,8 AMD Athlon XP 2600+, 1024 MB DDR, NVIDIA GeForce4 Ti4200, Windows XP, VM: 1.4.2_05-b04 82,8 297,0 543,9 286,0 534,5 AMD Athlon XP 2500+, 1024 MB DDR, ATI Radeon 9600 Pro, Windows XP, VM: 1.4.2_05-b04 93,0 322,2 622,8 44,7 AMD Athlon 900 MHz, 512 MB SDRAM, ATI Radeon 9600 Pro, Windows 2000, VM: 1.4.2_03-b02 172,3 537,5 Intel Mobile Pentium 1,7 GHz, 2048 MB DDR, ATI Mobility Fire GL T2, Windows XP, VM: 1.4.2 (IBM) 63,0 213,4 392,6 Intel Mobile Pentium 4 2 GHz, 512 MB DDR, NVIDIA Quadro4 500 GoGL, Windows 2000, VM: 1.4.2_05-b04 31,9 Intel Mobile Pentium 4 2 GHz, 1024 MB DDR, ATI Mobility Radeon 9000, Windows XP, VM: 1.4.2_01-b06 34,0 Intel Pentium 4 1,8 GHz, 512 MB DDR, ATI Radeon 8500, Windows XP, VM: 1.4.1_02-b06 37,5 Intel Mobile Pentium 1,6 GHz, 512 MB SDRAM, ATI Mobility Radeon 9600, Windows XP, VM: 1.4.2_04-b05 31,9 116,8 267,9 124,7 370,2 119,6 256,9 114,9 263,3 AMD Athlon XP 2500+, 1024 MB DDR, ATI Radeon 9600 Pro, Linux 2.6.8, VM: 1.4.2_05-b04 63,9 Intel Pentium 4 1,6 GHz, 256 MB DDR, ATI Mobility Radeon 7500, Windows XP, VM: 1.4.1_02-b06 18,1 67,0 156,2 205,0 240,5 Intel Mobile Pentium 4 1,8 GHz, 512 MB SDRAM, SiS M650, Windows 2000, VM: 1.4.2_04-b05 29,6 Intel Mobile Pentium 3 900 MHz, 256 MB SDRAM, NVIDIA Go, Windows XP, VM: 1.4.2_03-b02 12,5 46,2 100,8 138,2 103,8 0,0 200,0 400,0 600,0 800,0 1000,0 1200,0 1400,0 FPS (Frames Per Second) 192 animierte Sprites 500 animierte Sprites 2000 animierte Sprites Abbildung 7.2: Animierte Sprites in verschiedener Anzahl KAPITEL 7. AUSWERTUNG DER ERGEBNISSE 69 95,8 104,0 AMD Athlon XP 2500+, 1024 MB DDR, ATI Radeon 9800 Pro, Windows 2000, VM: 1.4.2_04-b05 325,2 563,4 75,4 84,4 Intel Pentium 4 3 GHz, 1024 MB DDR, ATI Radeon 9800 Pro, Windows XP, VM: 1.4.2_03-b02 334,0 566,2 64,4 71,4 Intel Pentium 4 HT 2,4 GHz, 1024 MB DDR, ATI Radeon 9700 Pro, Windows XP, VM: 1.4.2_04-b05 245,3 392,9 AMD Athlon XP-M 2400+, 512 MB SDRAM, NVIDIA GeForceFX 5900 XT, Windows XP, VM: 1.4.2_05-b04 70,1 77,6 Intel Dual Xeon 2,8 GHz, 1024 MB DDR, NVIDIA GeForceFX 5950 Ultra, Windows XP, VM: 1.4.2_03-b02 75,7 80,4 193,8 303,8 344,0 498,2 83,3 93,0 AMD Athlon XP 1800+, 768 MB DDR, NVIDIA GeForce4 Ti4200, Windows XP, VM: 1.4.2_01-b06 221,8 363,8 110,0 122,6 AMD Athlon XP 2600+, 1024 MB DDR, NVIDIA GeForce4 Ti4200, Windows 2000, VM: 1.4.2_05-b04 220,3 364,6 69,4 78,0 AMD Athlon XP 1600+, 512 MB DDR, NVIDIA GeForce4 Ti4200, Windows XP, VM: 1.4.2_03-b02 189,9 283,5 49,4 AMD Athlon XP 1800+, 512 MB DDR, NVIDIA GeForce4 Ti4200, Windows 2000, VM: 1.4.2_04-b05 74,5 66,4 140,1 111,1 123,6 AMD Athlon XP 2600+, 1024 MB DDR, NVIDIA GeForce4 Ti4200, Windows XP, VM: 1.4.2_05-b04 188,9 325,0 94,0 108,7 AMD Athlon XP 2500+, 1024 MB DDR, ATI Radeon 9600 Pro, Windows XP, VM: 1.4.2_05-b04 201,4 423,8 39,2 48,5 AMD Athlon 900 MHz, 512 MB SDRAM, ATI Radeon 9600 Pro, Windows 2000, VM: 1.4.2_03-b02 76,6 160,8 89,4 Intel Mobile Pentium 1,7 GHz, 2048 MB DDR, ATI Mobility Fire GL T2, Windows XP, VM: 1.4.2 (IBM) 124,0 133,6 253,2 59,8 75,8 Intel Mobile Pentium 4 2 GHz, 512 MB DDR, NVIDIA Quadro4 500 GoGL, Windows 2000, VM: 1.4.2_05-b04 103,0 160,5 65,6 79,5 Intel Mobile Pentium 4 2 GHz, 1024 MB DDR, ATI Mobility Radeon 9000, Windows XP, VM: 1.4.2_01-b06 115,2 217,1 52,0 61,1 Intel Pentium 4 1,8 GHz, 512 MB DDR, ATI Radeon 8500, Windows XP, VM: 1.4.1_02-b06 102,7 194,1 51,2 Intel Mobile Pentium 1,6 GHz, 512 MB SDRAM, ATI Mobility Radeon 9600, Windows XP, VM: 1.4.2_04-b05 79,4 81,5 163,2 62,9 69,0 AMD Athlon XP 2500+, 1024 MB DDR, ATI Radeon 9600 Pro, Linux 2.6.8, VM: 1.4.2_05-b04 103,1 147,6 37,7 53,4 58,8 Intel Pentium 4 1,6 GHz, 256 MB DDR, ATI Mobility Radeon 7500, Windows XP, VM: 1.4.1_02-b06 103,5 Intel Mobile Pentium 4 1,8 GHz, 512 MB SDRAM, SiS M650, Windows 2000, VM: 1.4.2_04-b05 23,8 29,3 48,7 Intel Mobile Pentium 3 900 MHz, 256 MB SDRAM, NVIDIA Go, Windows XP, VM: 1.4.2_03-b02 23,0 32,3 39,3 91,2 77,4 0,0 100,0 200,0 300,0 400,0 500,0 600,0 FPS (Frames Per Second) Terrain ohne Wasser Terrain mit Wasser Terrain ohne Wasser mit statischen Sprites Terrain mit Wasser und statischen Sprites Abbildung 7.3: Verschiedene Terrainkonfigurationen KAPITEL 7. AUSWERTUNG DER ERGEBNISSE 7.2.3 70 Schlussfolgerung Bei aller Funktionalität liegt das Hauptaugenmerk von Mithril natürlich in der Effizienz, die für den Einsatz in Echtzeit-Spielen essentiell ist. Dieses Ziel der prototypischen Umsetzung scheint erreicht worden zu sein, wenn man an dieser Stelle von erforderlichen 60 FPS bei Spielen ausgeht. Allerdings ist die letztendliche Deutung der Messdaten schwierig, da diese aus unterschiedlichen Systemkonfigurationen und funktional eng begrenzten Einzeltests resultieren. Dazu kommt die bereits angesprochene Beschränkung auf die Graphik, wodurch von den Ergebnissen ein unbestimmter Anteil abgezogen werden muss, je nach Komplexität der Zielanwendung. Kapitel 8 Fazit und Ausblick In diesem Kapitel sollen die erreichten und nicht erreichten Ziele mit denen aus der Aufgabenstellung verglichen werden, um das Gesamtergebnis der vorliegenden Arbeit bewerten zu können. 8.1 Erreichte Ziele In Kapitel 5 sowie in Abschnitt 7.1 wurden die erfolgreich umgesetzten Inhalte der Anforderungsanalyse und des Konzepts beschrieben. Zu beurteilen ist nun, ob mit ihnen auch die Aufgabenstellung als erfüllt anzusehen ist. Ziel dieser Arbeit ist die Konzeption und Entwicklung einer isometrischen Graphik-Engine, die auf den plattform-unabhängigen Standards Java und OpenGL basiert. Diese soll in der Lage sein, komplexe Welten ansprechend und effizient darzustellen. Nach eigener Einschätzung kann Mithril in dieser Hinsicht mehr als überzeugen. Große und vielschichtige Welten lassen sich performant und mit hoher graphischer Qualität rendern, wie das Demospiel und der Benchmark belegen. Um eine hohe graphische Qualität sicherzustellen wird die Möglichkeit untersucht, Vertex- und Pixel-Shader zu unterstützen. Bedauerlicherweise blieb während der Entwicklung des Frameworks keine Zeit, die Unterstützung von Shadern im Konzept oder in der Umsetzung zu berücksichtigen. Im folgenden Abschnitt 8.2.1 werden die Integration und die Einsatzmöglichkeiten von Shadern ausblickend behandelt. 71 KAPITEL 8. FAZIT UND AUSBLICK 72 Diese Arbeit entsteht in enger Zusammenarbeit mit Christian Stein, dessen Diplomarbeit die Entwicklung einer Massive Multiplayer Network Engine in Java umfasst. Die Realisierung eines gemeinsamen Demospiels ist ein zusätzliches Ziel beider Arbeiten. Das Demospiel kann die Leistungsfähigkeit beider Frameworks (Mithril und Mamuneen [5]) eindrucksvoll unter Beweis stellen. Das Spiel läuft auf unterschiedlichster Hardware bis hin zu Laptops mit Graphikchips, deren 3DTauglichkeit bzw. Graphikleistung im Allgemeinen als eher zweifelhaft anzusehen ist. Dies kann zusätzlich als eine Erfüllung des Nebenziels“, auch ” weniger leistungsstarke Graphikhardware zu unterstützen, gewertet werden. 8.2 Erweiterungen und Verbesserungen Abgesehen von allgemeinen Korrekturen und Optimierungen innerhalb des Systems sollten in der Zukunft vorallem folgende Punkte adressiert werden: 8.2.1 Shader Vertex- und Pixelshader stellen mächtige Werkzeuge zur Erweiterung der Engine durch den Benutzer dar. Innerhalb des Frameworks sollten daher spezielle Schnittstellen vorgesehen werden, mit deren Hilfe eigene Shaderprogramme durch den Anwender eingebunden werden können. Diese Schnittstellen repräsentieren bestimmte Stationen innerhalb der Renderingprozesse des Frameworks, an denen Shader anwendbar sind. Diese Stationen umfassen beispielsweise: • die benutzer-abhängige render()-Methode • das Rendern von Sprites oder Terrains Innerhalb der Terrains sind auch feinere Unterteilungen der Stationen denkbar, um nur einzelne Land- bzw. Wassertypen oder Regionen zu beeinflussen. Beispielsweise könnte ein Pixelshader speziell für den Wassertyp Lava“ ” zuständig sein, und diesen mit einem roten Glühen umgeben. 8.2.2 Innenbereiche Innenbereiche mit den in der Anforderungsanalyse (s. Kapitel 3) formulierten Eigenschaften sollten auf Basis der bereits umgesetzten Terrains realisierbar sein, ohne das bestehende Konzept grundlegend verändern zu müssen. Für KAPITEL 8. FAZIT UND AUSBLICK 73 die neuen Komponenten wie Wände, Türen und Aufzüge müssen zusätzliche und teilweise variable Geometrien eingeführt werden. Diese sind vermutlich auch für die ebenfalls noch fehlenden fortlaufenden“ Terrain-Objekte wie ” Zäune und Stromleitungen geeignet. 8.2.3 Verschiedenes Mobile Endgeräte: Es ist zu untersuchen, ob Mithril im Ganzen oder in einer verkleinerten Version auf mobilen Endgeräten einsetzbar ist. Derzeit werden verstärkt 3D-Chips für Geräte wie beispielsweise Mobiltelefone entwickelt, die in Zukunft voraussichtlich zum Standard werden. Bessere Schriftausgabe: Das Rendering von Schrift und das generelle FontSystem muss verbessert werden, um beispielsweise Schriften mit variablem Zeichenabstand zu unterstützen. Bessere Selektion: Über die bestehenden Selektionsmöglichkeiten hinaus sollten auch Sprites komfortabel selektierbar sein. Dabei könnte neben der bounding box auch der Alphawert eines Sprites ein Selektionskriterium sein. Fog: Das in OpenGL zur Verfügung stehende Fog Array, als Teil der Vertex Arrays, könnte für visuelle Effekte innerhalb der Terrains genutzt werden. Flexibleres Multitexturing: Land- und Wassertypen sollten mit Detailtexturen ausstattbar sein, die per Multitexturing umgesetzt werden. Speziell beim Wasser sollte die Animation der Texturmatrizen konfigurierbar sein. Mehr Eingabegeräte: Zusätzliche Eingabegeräte wie Joysticks und Gamepads sollten unterstützt werden. task switch: Der Wechsel zwischen mehreren laufenden Programmen (in Windows mittels der Tastenkombination ALT+TAB) ist derzeit nicht möglich und sollte realisiert werden. Partikelsysteme: Die meist zur Umsetzung von Spezialeffekten genutzten Partikelsysteme könnten auf der Basis von Sprites in Mithril umgesetzt werden. Literaturverzeichnis [1] OpenGL Architecture Review Board. OpenGL Reference Manual (Fourth Edition): The Official Reference Document to OpenGL, Version 1.4. Addison-Wesley, 2004. 41 [2] Eclipse Foundation. SWT - Standard Widget Toolkit, 2004. http://eclipse.org/swt. 39 [3] lwjgl.org. Lightweight Java Game Library, 2004. http://lwjgl.org. 39 [4] Ernest Pazera. Isometric Game Programming with DirectX 7.0. Stacy L. Hiquet, 2001. 10, 12 [5] Christian Stein. Entwicklung einer Massive Multiplayer Engine in Java, 2004. Diplomarbeit. Universitt Koblenz-Landau, Standort Koblenz. 54, 72 [6] Sun Microsystems, Inc. Java bindings for OpenGL, 2004. http://jogl.dev.java.net. 38 [7] Sun Microsystems, Inc. Java3D API, 2004. http://java.sun.com/products/java-media/3D. 27 74 Index 2D-Hardware, 12 3D-Hardware, 12 PageProxy, 42 Paging, 44 Abstraktheit, 21 Anforderungsanalyse, 14 API, 18 Ausblick, 71 Auswertung der Ergebnisse, 62 Axonometrie, 6 Run To The Hills, 50 Benchmark, 65 Demospiel, 50 Dimetrie, 7 Einleitung, 6 Factory, 21 Fazit, 71 FPS, 66 Indizes, 41 Interface, 18 Isometrie, 7 JOGL, 38 Konzeption, 18 LWJGL, 39 Schnittstelle, 18 Sprite, 10 animierte, 30 einfache, 30 SWT, 39 Systeme bestehende, 8 Tile, 10 Trimetrie, 7 Umsetzung prototypische, 38 Verfahren bestehende, 8 hybride, 13 modell-basierende, 13 tile-basierende, 10 Verkürzungsfaktor, 7 Vertex Arrays, 41 Wrapperklasse, 20 Zaxxon, 8 Marble Madness, 8 Mithril, 14 Node, 32 Page, 42 75