Final Starfighter DeluxeDie Patterns
Ein Spielentwicklungsprojekt von
Stefan Radicke
&&
Thomas Fuchsmann
sr038 && tf014 2
Inhalt 1
1. Einführung1.1 Grober Überblick über das Projekt1.2 Das Spiel: Ein virtueller Ausflug
2. Game Patterns, zum Ersten (ein grober Überblick)2.1 Erklärung vom PollingThread
2.2 Vom PollingThread zur Session2.3 Scrolling und Zeichenvorgang
3. Game Patterns, zum Zweiten (Objekte im Detail)3.1 Karl, der Raumschiff3.2 Feinde3.3 Bewegungsmuster3.4 Komm, wir basteln uns einen Feind
sr038 && tf014 3
Inhalt 2
4. Objekte erzeugen Objekte: Die Schüsse4.1 Was unterscheidet Schüsse von den anderen Objekten?
5. Kollisionserkennung5.1 Problematik – Kleine Fragestunde5.2 Das Konzept: Die konkreten Kollisionsklassen (Chain Of Responsibility, Strategy)
6. Das Level: Von der Engine zum Spiel6.1 Ein vereinfachter Iterator6.2 Konstruktoren und Arrays: Wunder der Technik6.3 Zusatzinformationen
7. Der Punkt, zu dem wir nicht mehr kommen werden, falls doch...
8. Danksagung und Ressourcen
sr038 && tf014 4
1.1 Grober Überblick über das Projekt- Statt Patterns ein Projekt- Das Rad neu erfunden?- Entwicklungszeit / Umfang- Erste Planung - Ziel
1. Einführung
1.2 Das Spiel: Ein virtueller Ausflug - Patterns in Aktion
sr038 && tf014 5
2.1 Erklärung vom PollingThread- Grundlegender Spielablauf (Endlosschleife)- Einzelne Schritte (berechnen, rendern, zeichnen, warten)
2. Game Patterns, zum Ersten
2.2 Vom PollingThread zur Session- Zentrale Klasse (Mediator)- Alle Objekttypen (Diagramm)- Listen und Manager- Zusammenhang zwischen Session und PollingThread
2.3 Scrolling und Zeichenvorgang- Der Eindruck von Bewegung- Darstellung in mehreren Ebenen (Multiscrolling)
sr038 && tf014 6
2.1 Erklärung vom PollingThread
Vereinfachter Spielablauf:
while (true) {
//Aktuellen Zustand des Spiels berechnen.berechnen();
//Das Spiel rendern und auf den Bildschirm zeichnen.
zeichnen();
//Ein wenig schlafen.Thread.sleep(25);
}
sr038 && tf014 7
Session
2.2 Vom PollingThread zur Session
Karl
Feinde
Wände
Items
FactoriesDatenbanken
Schüsse
Level
Session als zentrale Klasse: Mediator
sr038 && tf014 8
2.2 Vom PollingThread zur Session
Listen und Manager:
Die Session hält alle Feinde, Schüsse, Items und Wände in ArrayListen.=> Alle Objekte, die in den Listen gespeichert sind, sind am aktuellen Spielgeschehen beteiligt.
Für alle Objekttypen sind Manager-Methoden vorhanden, die vom PollingThread in einer bestimmten Reihenfolge aufgerufen werden.
Funktionsweise der Manager:=> über die entsprechende ArrayList wird iteriert=> Objekt wird bewegt=> Kollision wird geprüft
sr038 && tf014 9
2.2 Vom PollingThread zur Session
enemyManager() als Beispiel:
public void enemyManager() { for (iterator = enemies.iterator(); iterator.hasNext();) { enemy = (IEnemy)iterator.next();
if (enemy.isAlive()) { enemy.move(); //Kollision wird an anderer Stelle geprüft } else { enemy.suicide(); } }}
sr038 && tf014 10
2.2 Vom PollingThread zur Session
Zusammenhang zwischen Session und PollingThread:
PollingThread Session
levelManager()
wallManager()
enemyManager()
enemyShootManager()
shootManager()itemManager()
karlManager()
repaint()
Spielzustand berechnen
rendern,zeichnen
sr038 && tf014 11
2.3 Scrolling und Zeichenvorgang
Der Eindruck von Bewegung:
sr038 && tf014 12
2.3 Scrolling und Zeichenvorgang
Der Eindruck von Bewegung:
sr038 && tf014 13
2.3 Scrolling und Zeichenvorgang
Darstellung in mehreren Ebenen:
sr038 && tf014 14
3. Game Patterns, zum Zweiten
3.1 Karl, der Raumschiff- Wichtige Attribute- Ergänzung zur Session: Der KeyListener- Steuerung und Bewegung- Waffen für Karl: Ballern wie gestört
3.2 Feinde- Wichtige Attribute- Analogie zu den Grundpatterns (State, Template und Facade)- Das Interface: Jeder Feind wird gleich behandelt (Facade)
3.3 Bewegungsmuster- Lustig, aber wahr: Feinde dekorieren Bewegungsmuster (Decorator)- Die Hierarchie der MovePatterns und ihrer Datenbank- Problemloser Wechsel, zu Laufzeit (State)
3.4 Komm, wir basteln uns einen Feind
sr038 && tf014 15
3.1 Karl, der Raumschiff
Attribute für die Steuerung:
+ isMovingUp : boolean+ isMovingDown : boolean+ isMovingLeft : boolean+ isMovingRight : boolean+ isShooting : boolean
=> In den Methoden keyPressed(...) und keyReleased(...) werden die boolschen Variablen gesetzt.=> Dadurch ist eine flüssige Steuerung, unabhängig von Interruptzeiten, möglich.
Ergänzung zur Session: Der KeyListener
sr038 && tf014 16
3.1 Karl, der Raumschiff
Waffen für Karl: Ballern wie gestört
=> Im shootManager() wird von Karl ein Schuss angefordert, wenn karl.isShooting auf true steht.
=> Karl entscheidet selbstständig, ob er zum aktuellen Zeitpunkt schießen darf.
=> Unter folgenden Voraussetzungen darf Karl schießen:- Es befinden sich nicht mehr Schüsse im Bild, als erlaubt.- Die vorgegebene Verzögerungszeit ist abgelaufen.Die nötigen Variablen sind in der allgemeinen abstrakten Klasse Shoot statisch festgelegt.
=> Die Verzögerungszeit wird ignoriert, wenn der Spieler die Feuern-Taste schnell hintereinander mehrmals betätigt.
sr038 && tf014 17
3.2 Feinde
=> State: Bewegung und Schussverhalten=> Template: Algorithmen und deren Reihenfolge in abstrakten Superklassen. Subklassen bestimmen nur die Attributwerte.=> Facade: Allgemeines Interface für alle Feinde
Wichtige Attribute
# isAlive : boolean# hp : int# points : int- enemyImage : Image- suicideImage : Image- lineArray : Line2D[ ]# movePattern : IMovePattern# isHitByShoot : boolean
Analogie zu den Grundpatterns
sr038 && tf014 18
3.2 Feinde
Jeder Feind wird gleich behandelt: Das Feindkonzept
<< IEnemy >>
Enemy
AnimatedEnemy
Weitere Klassen:
Datenbanken, Karl, Level, etc.
Fighter5
GroundCannon1
Session
<< IMovePattern >>
Subsystem
Facade
sr038 && tf014 19
3.3 Bewegungsmuster
Feinde dekorieren Bewegungsmuster: Decorator + State
<< IMovePattern >>
MovePattern
PointMovePattern SimpleMovePattern
<< IEnemy >>
+move() : void
Session
=> Das Interface IEnemy sieht eine Methode setMovePattern(...) vor, mit der es möglich ist, die Movepatterns zur Laufzeit auszutauschen.
PointListData
sr038 && tf014 20
3.4 Komm, wir basteln uns einen Feind!public class FieserFeind extends Enemy {
}
public FieserFeind(Session session) { super(session); }
public int getHeight() { return 60; }
public int getWidth() { return 80; }
public int getHP() { return 100; }
public int getPoints() { return 10999; }
public Image getEnemyImage() { return session.graphicData.getFieserFeindImage(); }
public Image getSuicideImage() { return session.graphicData.getExplosion1Image(); }
public void playSuicideSound() { session.soundData.playExplosion1(); }
IMovePattern movePattern = new SimpleMovePattern(...); public IMovePattern getMovePattern()
{ return movePattern; }
sr038 && tf014 21
4. Objekte erzeugen Objekte: Die Schüsse
4.1 Was unterscheidet Schüsse von den anderen Objekten?- Die Hierarchie aller Schüsse- Karls Schüsse und feindliche Schüsse im Vergleich- Factories: Unterbindung von new
sr038 && tf014 22
4.1 Was unterscheidet Schüsse von anderen Objekten?
=> Schüsse werden zu unvorhersehbaren Zeiten in beliebiger Anzahl erzeugt.
=> Weiteres Problem: Schüsse dürfen nicht zur Laufzeit initialisiert werden.
Lösung: Factories, die alle Schüsse beim Laden der Engine in ausreichender Anzahl erzeugen.
=> Worin unterscheiden sich Karls Schüsse von feindlichen Schüssen?- Art der Erzeugung- Karls Schüsse haben Trefferpunkte- feindliche Schüsse vernichten Karl sofort- feindliche Schüsse fliegen auf einen konkreten Zielpunkt zu
sr038 && tf014 23
4.1 Was unterscheidet Schüsse von anderen Objekten?
Die Hierarchie aller Schüsse
<< IShoot >>
Shoot
KarlShoot EnemyShoot
EnemyShootFactory
Session
Fireball1 RedLaser1
KarlShootFactory
n
Karl
sr038 && tf014 24
Factories: Unterbindung von new
EnemyShootFactory als Beispiel:
4.1 Was unterscheidet Schüsse von anderen Objekten?
EnemyShootFactory
- redLaser1Array : EnemyShoot [ ]
... //weitere Schusstypen-Arrays
+ getEnemyShoot(shootName:String) : EnemyShoot
Enemy
+ createShoot() : void
sr038 && tf014 25
5. Kollisionserkennung
5.1 Problematik – Kleine Fragestunde- Anforderungen an die Kollision- ???
5.2 Das Konzept: Die konkreten Kollisionsklassen (Chain Of Responsibility, Strategy)
- Prinzipielle Zusammenarbeit der Klassen- Unterscheidung von Objekttypen- Performance, was ist das? (Grobüberprüfung, Reihenfolge)
sr038 && tf014 26
5. Kollisionserkennung
5.1 Anforderungen an die Kollisionserkennung:
=> Möglichst pixelgenaue Prüfung
=> Zuverlässigkeit
=> Einfache Handhabung: Flexibilität
=> Performance: Welche Prüfung ist wirklich nötig?
Welche Lösung erfüllt diese Anforderungen?
Unsere Lösung: Vektoren zur Kollisionsprüfung
sr038 && tf014 27
5.2 Die konkreten Kollisionsklassen
Prinzipielle Zusammenarbeit der Klassen
<< ICollision >>
+ check(obj:Object) : boolean
AbstractCollision
# session : Session
Collision
+ check(obj:Object) : boolean
Session
KarlShootCollision
EnemyShootCollision
ItemCollision
KarlCollision
sr038 && tf014 28
5.2 Die konkreten Kollisionsklassen
Unterscheidung von Objekttypen in der Klasse Collision
public boolean check(Object obj) {
if (obj instanceof Karl) { return karlCollision.check(obj); }
if (obj instanceof KarlShoot) { return karlShootCollision.check(obj); }
if (obj instanceof EnemyShoot) { return enemyShootCollision.check(obj); }
if (obj instanceof Item) { return itemCollision.check(obj); }
return false;}
sr038 && tf014 29
5.2 Die konkreten Kollisionsklassen
Kollisionserkennung im Detail: KarlCollision
Schritt 1 => Grobe Überprüfung, um Performance zu sparen: Bounding-Box
Kollision kann ausgeschlossen werden.
=> Abbruch der Überprüfung
sr038 && tf014 30
5.2 Die konkreten Kollisionsklassen
Kollisionserkennung im Detail: KarlCollision
Schritt 2=> Grobe Überprüfung, um Performance zu sparen: Bounding-Box
Kollision ist möglich.
=> Überprüfung auf Vektorebene
sr038 && tf014 31
5.2 Die konkreten Kollisionsklassen
Kollisionserkennung im Detail: KarlCollision
Schritt 3=> Genaue Überprüfung: Vektoren
Vektoren überschneiden sich nicht.
=> keine Kollision
sr038 && tf014 32
5.2 Die konkreten Kollisionsklassen
Kollisionserkennung im Detail: KarlCollision
Schritt 4=> Genaue Überprüfung: Vektoren
Vektoren überschneiden sich.
=> Kollision .. Karl ist tot!
sr038 && tf014 33
6. Das Level: Von der Engine zum Spiel
6.1 Ein vereinfachter Iterator- Hierarchie der Level- Skriptprinzip (Iterator)
6.2 Konstruktoren und Arrays: Wunder der Technik- Woraus besteht ein Objekt?
=> Teilung schwergewichtiger Ressourcen
6.3 Zusatzinformationen: - Hintergrundbild, Scrolling, Musik, besondere Ereignisse
sr038 && tf014 34
6.1 Ein vereinfachter Iterator
<< ILevel >>
+ hasNextStep() : boolean
+ nextStep() : void
+ putWallsIntoList() : void
+ putEnemiesIntoList() : void
Level
+ nextStep() : void
+ putWallsIntoList() : void
+ putEnemiesIntoList() : void
Hierarchie der Level
Level1_1
- enemyArray : IEnemy [ ] [ ]
- wallArray : IWall [ ] [ ]
+ hasNextStep() : boolean
Der levelManager() in der Session „iteriert“ mit den Methoden hasNextStep() und nextStep() über die Arrays der konkreten Level.
=> Das Level wird wie ein Skript abgearbeitet.
sr038 && tf014 35
6.2 Konstruktoren und Arrays
Erzeugung der Feinde und Wände
=> Alle im Level vorkommenden Objekte werden beim Laden des Levels im Konstruktor initialisiert und in Arrays gespeichert. Dadurch gibt es praktisch kein new zur Laufzeit.=> Vor beginn des Levels sind bereits alle Feinde komplett einsatzbereit zusammengebaut (inklusive aller denkbaren Verhaltensmuster).
=> Eine so riesige Menge von Objekten im Speicher zu halten, ist kein Problem, da alle schwergewichtigen Resourcen (Bilder, Sounds, ...) über Datenbanken geteilt werden. Somit besteht ein konkreter Feind nur aus ein paar Zahlenwerten und benötigt kaum Speicherplatz.
=> Zur Laufzeit des Spiels werden lediglich Referenzen kopiert bzw. verschoben (Level => Session) und nur in Sonderfällen neu erzeugt.
sr038 && tf014 36
6.2 Konstruktoren und Arrays
Erzeugung der Feinde und Wände II: Codebeispiel
public class Level1_1 extends Level { //... private IEnemy[][] enemyArray = new IEnemy[5000][]; //... IEnemy[] e101; //... public Level1_2(Session session) { //... e101 = new IEnemy[] { new Fighter5(session) }; e101[0].setPosition(500, 0); e101[0].setMovePattern(new PointMovePattern( e101[0], session.pointListData.getDropIn(true, 100, 5))); e101[0].setShoot( EnemyShoot.ROUNDFIREBALL1, session.shootBehaviorData.shootRepeatedlyToKarl(80, 6)); //... enemyArray[101] = e101; //... }}
sr038 && tf014 37
6.3 Zusatzinformationen
Hintergrundbild, Scrolling, Musik, besondere Ereignisse
=> Das Level kann zu jedem beliebigen Zeitpunkt angehalten werden: + stopLevel() : boolean Beliebige Bedingungen, z.B. Endgegner im Bild, sind möglich.
=> Informationen über Scrolling-Verhalten: - weit entferntes Hintergrundbild - näheres Hintergrundbild - Wände Alle drei Schichten können sowohl einzeln, als auch in Abhängigkeit zueinander bewegt werden.
=> Es können zu beliebigen Zeitpunkten Musikstücke oder Geräusche abgespielt oder gestoppt werden.
=> Beim Start eines Levels kann eine Animation abgespielt werden.
=> Zur Laufzeit kann jedes Objekt beliebig manipuliert werden.
sr038 && tf014 38
- Schussverhalten- Datenbanken für Sound und Grafik- Vererbungshierarchien im Detail- Animationen:
=> Zwischensequenzen- Auch Feinde haben das Recht recycelt zu werden!- Viele bunte Items- Karls magisches Schutzschild- Zusammenspiel und Abhängigkeiten zwischen Objekten:
=> Wände und Feinde- Intelligente Feinde- Animierte Feinde- Die Pausefunktion: versteckte Problematik- Ein Ladebildschirm- Geschichten eines Entwicklers:
=> Nette Bugs! (Freude am Iterator)
7. Der Punkt, zu dem wir nicht mehr kommen werden, falls doch:
sr038 && tf014 39
8. Danksagung und Ressourcen
Großen Dank an...... Tobias Herborth für seine Nachhilfe in Sachen Animation, Threads und Softwaredesign.... Rebecca Trippe für ihre tollen Charakterdesigns und Spielgrafiken.... Nicolas Schmid für nützliche Tipps und seine ausgiebigste Misshandlung unserer Engine.... Herrn Walter Kriha für sein Talent, uns massiv zu motivieren.... alle Studenten, die uns durch ihre Design Patterns Präsentation in vielerlei Hinsicht die Augen geöffnet haben.... nicht zuletzt alle Testspieler, Kritiker und Versuchkaninchen (Die Flosse bleibt!)
Ressourcen:- GOF-Book (Die Pattern-Bibel)- http://java.sun.com- Spieleentwicklung in Java: http://fivedots.coe.psu.ac.th/~ad/jg/index.html- Java2D, O‘Reilly-Verlag