*“Return of Billy Jeans“

das ultimative Actionspiel

 

 

Im Zuge des

 Praktikums zu Software Entwicklung II

an dem Institut SSW der Universität Linz

 

 

 

Erstellt von

Markus Keller

Markus.Keller@Students.uni-linz.ac.at

 

 

 

 


 

Inhaltsverzeichnis

 

 

 

1     Vorwort. 1

2     Einleitung.. 1

3     Lösungsidee.. 2

3.1       Spielfigur 2

3.2       Rubine. 2

3.3       Bälle. 3

3.4       Interaktion zwischen Hero und Ball 3

3.5       Interaktion zwischen Hero und Rubin. 4

3.6       Interaktion zwischen Ball und Wand. 4

3.7       Interaktion zwischen Hero und Wand. 4

4     Aufbau und Implementierung.. 5

4.1       Übersicht 5

4.2       AppletGame. 5

4.3       MyKeyListener 6

4.4       BallVector 6

4.5       RubinVector 6

4.6       RubinAnimation. 7

4.7       Hero. 7

4.8       Walls. 7

5     Schlußwort. 7

 

 

 


1      Vorwort

Dieses Spiel wurde als Java Applet implementiert um es über das Internet für viele Leute zugänglich zu machen. Es wurde auch darauf geachtet, dass nur Klassen und Methoden der JDK1.1 verwendet wurden, um es für so viele Browser wie möglich darstellbar zu machen. Das Applet wurde unter Netscape 4.7 und Internet Explorer 5.5 getestet.

 

Die Internet Seite des Spiels ist http://members.aon.at/sam-ware

 

2      Einleitung

Bei diesem Projekt handelt es sich um die Implementierung eines kleinen Aktion-Spieles als Java Applet. Der Titel des Spieles ist: „Return of Billy Jeans“.

 

Das Spiel besteht aus einer Spielfläche mit vertikalen und horizontalen Wänden, einer Spielfigur (die der Spieler per Tastatur steuern kann), mehrere Rubine (die die Spielfigur aufsammeln muss) und mehrere Bälle die sich wirr über die Spielfläche bewegen und die Spielfigur verwunden können. Abbildung 1 zeigt einen Screenshot des Spiels.

 

Abbildung 1: Screenshot des Spieles

 

Dieser Screenshot zeigt alle Elemente des Spieles, einschließlich der Anzeige der Energie der Spielfigur und der Anzeige für den Score, Level und Lives. Weiters erkennt man links oben die Spielfigur, in der Mitte den blauen Rubin der aufgesammelt werden muss und die wirr verteilten roten Bälle.

 

Das Ziel ist es nun alle Rubine aufzusammeln ohne dabei von den Bällen getroffen zu werden. Wenn alle Rubine aufgesammelt sind, kommt man in das nächste Level. Pro Level ändert sich das Hintergrundbild, die Anzahl der Bälle und Rubine, und das Layout der Spielfläche.

3      Lösungsidee

3.1           Spielfigur

Die Spielfigur, wird im Weiteren auch Hero genannt, besteht aus mehreren Bildern die zu einer Animation zusammengefügt werden. Die Reihenfolge der Bilder-Sequenz hängt davon ab in welche Richtung die Spielfigur bewegt wird. Die Anzahl der verwendeten Bilder wurde minimal gehalten um die benötigte Ladezeit des Applets so gering wie möglich zu halten. (Diese Überlegung zieht sich durch die ganze Entwicklung des Spieles.) Der Hero wird in Summe mit neun Bildern animiert, die in Abbildung 2 dargestellt sind.

 

Hero wird nicht bewegt

Hero rennt nach unten

Hero rennt nach links

Hero rennt nach rechts

Hero rennt nach oben

Abbildung 2: Animation des Heros

 

Weitere Eigenschaften wie Kollisons-Überprüfung werden später noch behandelt.

 

3.2           Rubine

Die Rubine müssen nicht bewegt werden und sind so statisch an einem Punkt fixiert. Sie müssen lediglich eine kleine Animation ausführen um attraktiver auf den Hero zu wirken. Ein Rubin besteht aus zwei Animationen. Die erste wird ständig wiederholt bis der Rubin eingesammelt wird und zwar besteht die Animation aus einem Funkeln (siehe Abbildung 3). Die Zweite Animation wird nur einmal gezeigt und zwar wenn der Rubin aufgesammelt wird. In diesem Fall erscheint ein Dollarzeichen aus dem Inneren des Rubins (siehe Abbildung 4).

 

  

Abbildung 3: Rubin Normalzustand

 

Abbildung 4: Rubin aufgesammelt

 

Die Animation des Rubins wird in einem eigenen Thread verwaltet, um sie flüssig und in der richtigen Geschwindigkeit darzustellen.

 

3.3           Bälle

Ein Ball besteht nur aus einem einzigen Bild (siehe Abbildung 5).

 

*

Abbildung 5: Ball

 

Dieses Bild eines Balles wird einfach bei jedem Animationsschritt um eine bestimmte Anzahl von Pixel weiterverschoben. Dadurch ergibt sich der Effekt, dass der Ball über die Spielfläche fliegt. Wenn der Ball an eine Wand stößt, wird er einfach reflektiert (nach der Regel: Einfallswinkel ist gleich Ausfallswinkel). Dazu mehr unter 3.6 Interaktion zwischen Ball und Wand.

 

3.4           Interaktion zwischen Hero und Ball

Der Sachverhalt ist klar. Wenn der Hero von einem Ball getroffen wird, soll der Hero verwundet werden. Es stellt sich nun die Frage wie man feststellen soll, wann der Hero getroffen wurde. Da es zu aufwendig ist die exakte Kontur der Spielfigur abzutasten, ob sie von einem Ball getroffen wurde, wird eine Vereinfachung herangezogen. Es wird dem Hero ein Rechteck umschrieben, das als Approximation zur Kontur angesehen werden kann. Als Rechteck wird aber nicht direkt die Umrandung des Bildes herangezogen, sondern ein etwas kleineres Rechteck, das die Kontur besser anpassen.

 

Das Rechteck das dem Ball umschrieben wird ist ein Quadrat das den Durchmesser des Balles als Seitenlänge besitzt.

 

Wenn sich nun das Rechteck des Heros mit dem des Balles trifft, wird dem Hero Energie abgezogen. Diesen Sachverhalt zeigt Abbildung 6.

 

Abbildung 6: Interaktion zwischen Hero und Ball

 

Das überschneiden der Rechtecke kann in Java sehr einfach abgefragt werden, da bereits eine Methode dafür implementiert ist (public boolean java.awt.Rectangle.intersects(Rectangle)). Wenn die Methode true zurück gibt, haben sich die zwei Rechtecke geschnitten.

3.5           Interaktion zwischen Hero und Rubin

Die Abfrage der Kollision zwischen Hero und Rubin ist äquivalent zu dem zwischen Hero und Ball (siehe 3.4 Interaktion zwischen Hero und Ball).

 

3.6           Interaktion zwischen Ball und Wand

Wie der Screenshot (siehe Abbildung 1: Screenshot des Spieles) zeigt, besteht das Spielfeld aus vertikalen und horizontalen Wänden. Das heißt es muss abgefragt werden ob der Ball an einer Wand ansteht oder nicht.

 

Das Problem ist, dass in diesem Fall nicht einfach die Methode java.awt.Rectangle.intersects(Rectangle) verwendet werden kann, da ein Ball pro Animationsschritt mehrere Pixel weiterspringt, die Wand aber nur einen Pixel Breit ist. Das heißt die Wand wird einfach übersprungen und es kommt zu keiner Berührung zwischen dem Ball und der Wand.

 

Dadurch musste eine etwas aufwendigere Möglichkeit gefunden werden um das Problem zu lösen. Unter Hilfenahme der Abbildung 7 wird die Vorgehensweise erklärt.

 

Abbildung 7: Interaktion zwischen Ball und Wand

 

Bevor der Ball um einen Schritt, der mehrere Pixel groß ist, bewegt wird, wird überprüft ob sich dazwischen eine Wand befindet. Diese Überprüfung wird einfach über die entsprechenden Koordinaten des Balles und der Wand bewerkstelligt. Das heißt bevor jeder einzelne Ball bewegt werden kann, muss er auf Kollision mit allen Wänden auf dem Spielfeld überprüft werden. Das ist mit einem entsprechenden Rechenaufwand verbunden. Diese Implementierung hat sich im Spiel als erfolgreich herausgestellt.

3.7           Interaktion zwischen Hero und Wand

Die Kollision zwischen Hero und Wand basiert auf dem gleichen Prinzip wie 3.6 Interaktion zwischen Ball und Wand.

 

Da nun alle wichtigen Objekte des Spieles und deren Interaktion geklärt wurden, ist es an der Zeit zum konkreten Aufbau des Applets zu kommen.

 

4      Aufbau und Implementierung

4.1           Übersicht

In diesem Abschnitt wird genauer auf den Aufbau und die Implementierung des Spieles als Java Applet eingegangen. Das heißt, es wird gezeigt wie die Objekte und Klassen miteinander agieren. Dies wird auf einer niedrigen Abstraktionsebene dargestellt. Für noch genauere Beschreibungen ist der Source-Code und die darin enthaltenen Kommentare zu berücksichtigen. Abbildung 8 zeigt auf einen Blick alle verwendeten Objekte und deren wichtigsten Methoden.

 

Abbildung 8: Aufbau des Applets

 

Im Weiteren werden die einzelnen Objekte dieses Diagrames erläutert.

4.2           AppletGame

Das Herz des Spiels ist das Applet AppletGame das Runnable implementiert. Die run() Methode wird durch einen Thread ständig ausgeführt und bewerkstelligt die wichtigsten Operationen des Spiels. Das sind:

 

Eine weitere sehr wichtige Methode von AppletGame ist paint(). Sie übernimmt das zeichnen des gesamten Spieles mit allen darin befindlichen Objekten.

4.3           MyKeyListener

Die Klasse MyKeyListener erweitert KeyAdapter und verwaltet die gesamte Steuerung des Spieles. Die wichtigste Aufgabe ist die Steuerung des Heros. Sie ist wie folgt aufgebaut. In MyKeyListener wird, zum Beispiel durch das drücken der Cursor Aufwärts Taste, die Methode Hero.setUp() aufgerufen, die dem Hero sagt, dass er nach oben laufen soll. In der Methode AppletGame.paint() wird nun durch den Aufruf von Hero.isUp() überprüft, ob der Hero nach oben laufen soll. Wenn Hero.isUp() true zurück gibt, wird das Bild des Heros, wo er nach oben läuft, gezeichnet und zwar durch den Aufruf von Hero.runUp(Grphics).

 

Es besteht auch die Möglichkeit die Spielfigur schneller laufen zu lassen. Das wird durch die Abfrage der Shift-Taste erledigt.

 

Dadurch, dass das Spiel auf verschieden leistungsstarken Rechnern unterschiedlich schnell rennen kann, wurde eine komfortable Möglichkeit zur Steuerung der Geschwindigkeit vorgesehen. Durch betätigen der + und – Taste kann der Spielablauf verschnellert oder verlangsamt werden. Dabei wird einfach die Suspendierungszeit von Thread.sleep(ms) in AppletGame verändert.

 

Hier nochmals die Tastenbelegung im Überblick

4.4           BallVector

Die Klasse BallVector ist von Vector abgeleitet und verwaltet alle Bälle die sich auf der Spielfläche befinden. Sie beinhaltet unter anderm die Methode addElement(Ball) um einen Ball hinzuzufügen. Weiters die Methode update() um die Bälle auf die neue Position zu setzen und die Methode draw(Graphics) um die Bälle zu zeichnen.

4.5           RubinVector

Die Klasse RubinVector ist von Vector abgeleitet und verwaltet alle Rubine die sich auf der Spielfläche befinden. Sie beinhaltet unter anderm die Methode addElement(Rubin) um einen Rubin hinzuzufügen. Weiters die Methode update() um das nächste Bild der Animation zu initialisieren und die Methode draw(Graphics) um alle Rubine zu zeichnen. Eine weitere wichtige Methode ist removeChecked(). Sie entfernt einen Rubin aus dem Vector, wenn er von dem Hero aufgesammelt wurde.

4.6           RubinAnimation

Die Klasse RubinAnimation ist nur für die Animation der Rubine verantwortlich und implementiert Runnable. Das animieren der Rubine könnte auch direkt in AppletGame.run() bewerkstelligt werden. Da aber die Animation der Rubine, das Funkeln, langsamer abläuft als der Rest des Spieles wurde dafür eine eigene Klasse RubinAnimation implementiert.

 

In der Methode RubinAnimation.run() wird der aktuelle Frame der Animation durch RubinVector.update() initialisiert. Tatsächlich gezeichnet wird der Rubin dann in AppletGame.paint(Graphics).

4.7           Hero

Hero ist eine umfangreichere Klasse und in diesem Punkt werden nur die wichtigsten Aspekt dargestellt.

 

Wie bereits unter 3.4 Interaktion zwischen Hero und Ball und 3.5 Interaktion zwischen Hero und Rubin erwähnt wurde, ist es notwendig die Kollision des Heros zu untersuchen. Dies ist mit den zwei Methoden Hero.testCollision(BallVector) und Hero.testCollision(RubinVector) implementiert. Die Kollision zwischen Hero und den Wänden (siehe 3.7 Interaktion zwischen Hero und Wand) ist direkt in Hero.runUp(Graphics), Hero.runDown(Graphics), Hero.runLeft(Graphics) und Hero.runRight(Graphics) implementiert.

4.8           Walls

Die Klasse Walls besteht nur aus zwei Vektoren die die vertikalen und horizontalen Wände abspeichert. Die Klassen BallVector und Hero können dann direkt auf die vorhanden Wände der Spielfläche zugreifen.

 

An diesem Punkt sollte jedem klar sein, welche Objekte zur Realisierung des Spieles verwendet werden und wie sie miteinander agieren.

 

Der nächste Schritt ist es nun den Source-Code zur Hand zu nehmen und den Ablauf des Spiels genauer zu inspizieren, oder einfach das Spiel zu starten und Spaß daran zu haben ;-).

 

5      Schlußwort

Das Projekt hat gezeigt, dass man mit einfachen Objekten ein lustiges und actionreiches Spiel implementieren kann. Dieses Spiel ist ein Spielrumpf, das heißt es kann ganz einfach verändert und erweitert werden, zum Beispiel eine andere Spielfigur, oder eine größere Spielfläche verwendet werden.

 

Jetzt kann ich nur noch sagen: „Watch out the balls and catch the rubins“