📄 platformgamebasicsdeu.html
字号:
<html><!-- InstanceBegin template="/Templates/template.dwt" codeOutsideHTMLIsLocked="false" -->
<head>
<!-- InstanceBeginEditable name="doctitle" -->
<title>Java Cooperation: platform game basics </title>
<!-- InstanceEndEditable --><meta http-equiv="Content-Type" content="text/html; charset=windows-1251">
<link href="jc_style.css" rel="stylesheet" type="text/css">
<script src="klayers.js"></script>
<script>
function cancelCloseMenu(){
if(self.tm) clearTimeout(tm) // cancel delayed closing
}
function showMenu(){
if(!self.menu || !self.submenu){
menu=layer("menulayer")
submenu=layer("submenulayer")
}
cancelCloseMenu()
submenu.moveTo(menu.getAbsoluteLeft(), menu.getAbsoluteTop() + menu.getHeight()) // move second menu relatively
submenu.show()
}
function initiateHideMenu(){
tm=setTimeout("hideMenu()",400) // 1 second delay to close a submenu
}
function hideMenu(){
submenu.hide()
}
</script>
<!-- InstanceBeginEditable name="head" --><!-- InstanceEndEditable -->
</head>
<body bgcolor="#ffffff" leftmargin="0" topmargin="0" rightmargin="0" bottommargin="0">
<div align="center">
<a name="top"></a>
<table bgcolor="#737373" border="0" cellpadding="0" cellspacing="0" width="618">
<tr>
<td rowspan="8" bgcolor="#000000"><img src="Pics/spacer.gif" width="1" height="1" border="0" alt=""></td>
<td bgcolor="#000000"><img src="Pics/spacer.gif" width="131" height="1" border="0" alt=""></td>
<td bgcolor="#000000"><img src="Pics/spacer.gif" width="13" height="1" border="0" alt=""></td>
<td bgcolor="#000000"><img src="Pics/spacer.gif" width="58" height="1" border="0" alt=""></td>
<td bgcolor="#000000"><img src="Pics/spacer.gif" width="94" height="1" border="0" alt=""></td>
<td bgcolor="#000000"><img src="Pics/spacer.gif" width="81" height="1" border="0" alt=""></td>
<td bgcolor="#000000"><img src="Pics/spacer.gif" width="48" height="1" border="0" alt=""></td>
<td bgcolor="#000000"><img src="Pics/spacer.gif" width="23" height="1" border="0" alt=""></td>
<td bgcolor="#000000"><img src="Pics/spacer.gif" width="38" height="1" border="0" alt=""></td>
<td bgcolor="#000000"><img src="Pics/spacer.gif" width="19" height="1" border="0" alt=""></td>
<td bgcolor="#000000"><img src="Pics/spacer.gif" width="108" height="1" border="0" alt=""></td>
<td bgcolor="#000000"><img src="Pics/spacer.gif" width="5" height="1" border="0" alt=""></td>
</tr>
<tr>
<td rowspan="5"><a href="index.html"><img name="fab_r1_c1" src="Pics/fab_r1_c1.gif" width="131" height="71" border="0" alt="Java cooperation home"></a></td>
<td rowspan="6" bgcolor="#737373"></td>
<td colspan="9" height="15" bgcolor="#737373"></td>
<td rowspan="8" bgcolor="#000000"><img src="Pics/spacer.gif" width="1" height="1" border="0" alt=""></td>
</tr>
<tr>
<td colspan="5"><div class="colontitul"><!-- InstanceBeginEditable name="Colontitul" -->Tutorial<!-- InstanceEndEditable --></div></td>
<td colspan="2" bgcolor="#737373"><img name="fab_r2_c8" src="Pics/fab_r2_c8.gif" width="57" height="19" border="0" alt="type and press Enter"></td>
<td colspan="2">
<FORM method=GET action="http://www.google.com/search">
<TABLE bgcolor="#FFFFFF" cellpadding="0" cellspacing="0" height="10px"><tr>
<td height="10" valign="middle">
<INPUT TYPE=text name=q size=18 maxlength=60 value="" style="font-size:12px; color:#000000; border-width: 0px;">
<input type=hidden name=domains value="http://javacooperation.gmxhome.de/">
<input type=hidden name=sitesearch value="http://javacooperation.gmxhome.de/" checked>
</td></tr></TABLE>
</td>
</form>
<td><img src="Pics/spacer.gif" width="1" height="19" border="0" alt=""></td>
</tr>
<tr>
<td colspan="9" bgcolor="#666666"><img name="fab_r3_c3" src="Pics/fab_r3_c3.gif" width="474" height="8" border="0" alt=""></td>
<td><img src="Pics/spacer.gif" width="1" height="8" border="0" alt=""></td>
</tr>
<tr>
<td colspan="6"><a href="TutorialStartDeu.html"><img src="Pics/deupanel_01.gif" width="56" height="22" border="0"></a><a href="OnlineSpieleStartDeu.html"><img src="Pics/deupanel_02.gif" width="94" height="22" border="0"></a><a href="DownloadsDeu.html"><img src="Pics/deupanel_03.gif" width="76" height="22" border="0"></a><a href="LinksDeu.html"><img src="Pics/deupanel_04.gif" width="52" height="22" border="0"></a><a href="KontaktDeu.html"><img src="Pics/deupanel_05.gif" width="64" height="22" border="0"></a></td>
<td><img name="fab_r4_c9" src="Pics/fab_r4_c9.gif" width="19" height="22" border="0" alt=""></td>
<td width="108" height="22" bgcolor="#666666"><div align="center"><span id="menulayer" style="position: relative; background-color:#666666; width:108; height:20; vertical-align:baseline; border:1px solid #FFFFFF"><a href="#" class="text" onmouseover="showMenu(); return true" onmouseout="initiateHideMenu(); return true">Select language</a></span>
</div>
<div id="submenulayer" style="position: absolute; visibility: hidden; background-color: #666666; width:108px;">
<div class="text" style="border:1px solid #FFFFFF; border-top-width:0; padding:5px">
<a href="#" onmouseover="cancelCloseMenu(); return true" onmouseout="initiateHideMenu(); return true">German</a><br>
<a href="indexEng.html" onmouseover="cancelCloseMenu(); return true" onmouseout="initiateHideMenu(); return true">English</a><br>
<a href="http://javacooperation.dev.juga.ru/index.html" onmouseover="cancelCloseMenu(); return true" onmouseout="initiateHideMenu(); return true">Russian</a>
</div>
</div>
</td>
<td><img name="fab_r4_c11" src="Pics/fab_r4_c11.gif" width="5" height="22" border="0" alt=""></td>
</tr>
<tr>
<td colspan="9"><img name="fab_r5_c3" src="Pics/fab_r5_c3.gif" width="474" height="7" border="0" alt=""></td>
</tr>
<tr>
<td colspan="11" style="padding:18; background-color:#333333; vertical-align:top;">
<div class="text">
<!-- InstanceBeginEditable name="EditRegion" -->
<h2>Grundlagen eines Platform - Games</h2>
<p align="justify">Seit ich vor fast einem Jahr begonnen habe <a href="Applets/Games/JrioGame/Jrio.html">J-Rio</a> zu implementieren, spiele ich mit dem Gedanken ein Kapitel über die Grundlagen von Platform Games für dieses Tutorial zu schreiben, hatte aber bis jetzt keine Zeit dies zu tun. Das ein solches Kapitel aber durchaus seine Berechtigung hat zeigen die vielen Mails, die ich seit J-Rio auf den Online Seiten zu finden ist von euch bekommen habe und die alle Probleme betrafen, welche bei der Implementierung von Platform - Spielen auftreten. Da ich den kompletten Sourcecode von J-rio nicht öffentlich zur Verfügung stellen werde, können all jene, die mich nach dem Code gefragt haben mit diesem Kapitel auch ein wenig hinter die Kulissen von J-rio blicken, denn das Beispiel - Applet, das wir im Folgenden entwickeln werden ist im Grunde eine sehr abgespeckte Version von J-rio. Ich wünsche euch also wie immer viel Spaß mit diesem Kapitel, hoffe, dass ich euch ein wenig weiterhelfen kann und möchte noch anmerken, dass es sehr empfehlenswert wäre sich die Kapitel über den <a href="LeveleditorDeu.html">Leveleditor für arraybasierte Spiele</a> und <a href="ScrollingDeu.html">Bildschirmscrollen</a> nochmals anzusehen, da ich die dort vorgestellten Techniken verwenden, aber nicht mehr im Detail darauf eingehen werde. Außerdem lohnt es sich sicherlich den Sourcecode für das Beispielapplet jetzt schon <a href="SourceCodes/PlatformGameBasics/PlatformGameBasics.zip">runterzuladen</a> und während des Kapitels auch durchzusehen, denn ich kann nicht den kompletten Code, sondern nur Ausschnitte vorstellen und besprechen. Das Kapitel gliedert sich dabei in drei Abschnitte. Zuerst werden wir uns dem von mir gewählten <a href="#PlatformGameKlassendesign">Klassendesign</a> des Spiels zuwenden, anschließend werden wir
<a href="#PlatformGamePlayerObjekt">das Player - Objekt</a> und dort vor allem jene Teile der Klasse, die für die Bewegung des Spielers nötig sind genauer unter die Lupe nehmen und zum Schluss werden wir noch einen Blick auf <a href="#PlatformGameLevelObjekt">die Struktur und Funktionen der Klasse Level</a> werfen.</p>
<h3><a name="PlatformGameKlassendesign">Das Klassendesign</a></h3>
<p align="justify">Umso größer und komplexer ein Spiel wird desto wichtiger ist ein flexibles und erweiterbares Klassendesign. Ich glaube, dass mir dies im Falle von J-rio ziemlich gut gelungen ist, denn es ist eine einfache Prozedur z. B. eine neue Art Levelelement in das Spiel einzufügen und neue Levels für das Spiel zu schreiben (siehe auch <a href="../../../Downloads/J-RioLevelEditor.zip">J-rio LevelEditor</a>). Ein einfaches Leveldesign ist sehr wichtig, denn nicht zu letzt lebt das Spiel ja von den zu spielenden Levels. Wir wollen also erreichen neue Level Elemente einfach hinzufügen zu können und wir wollen die Level des Spiels frei und schnell gestallten können.<br>
Wenn ihr euch den Sourcecode runtergeladen und das *.zip - File entpackt habt, dann findet ihr folgende Klassen: </p>
<h4>Die Klasse <i>Main</i></h4>
<p align="justify">Zunächst implementiert die Main - Klasse, wie bei allen meinen Spielen und Beispielen, das Applet selbst, also die <b>init()</b>, <b>start()</b>, <b>stop()</b>, <b>destroy()</b> und <b>paint(Graphics g)</b> - Methoden und enthält die Hauptschleife, in der das Spiel läuft (implementiert also das Interface <b>Runnable</b>), sowie die Ereignisskontrolle für die Tastatureingaben des Spielers. Zusätzlich hält sie zwei Attribute: das Player - Objekt und jeweils eine konkrete Instanz eines Levels, in unserem Fall ausschließlich eine Instanz der LevelOne - Klasse. In der <b>run()</b> - Schleife werden sowohl Level als auch Player gezeichnet, gescrollt (wenn nötig), bewegt (Player) und nach Kollisionen des Spielers mit Level - Elementen getestet, wobei diese Methoden, wie ihr im Sourcecode sehen könnt, lediglich in der run() - Methode aufgerufen werden, aber in anderen Klassen (Level und Player) implementiert sind.</p>
<h4>Die Klasse <i>Player</i></h4>
<p align="justify">Die Klasse Player zeigt sich für Attribute und Eigenschaften des Spielers verantwortlich. Sie hält im wesentlichen Informationen über die Position(en) des Spielers im Spiel, speichert die Bilder des Spielers, die für die Animation nötig sind und zeigt sich für die Bewegungen des Spieler verantwortlich (Details darüber kommen später).</p>
<h4>Die Klasse <i>LevelElement</i></h4>
<p align="justify">Ein Spiel wie <b>J-rio</b> beinnhaltet viele verschiedenen Arten von beweglichen und auch unbeweglichen Level - Elementen. Dazu gehören z.B. die Ereignissteine (die mit dem Fragezeichen), welche z. B. zwei verschiedene Zustände haben, nämlich "noch nicht angestoßen" und "schon angestoßen", kompliziertere Elemente wie die sich bewegenden Platformen, sowie ganz einfache Elemente wie die Grundelemente. Für dieses Kapitel beschränken wir uns auf das einfachste LevelElement in J-rio, die Klasse <b>Ground</b>. Allerdings haben alle Levelelemente viele Eigenschaften gemeinsam, diese Eigenschaften werden in der Klasse LevelElement implementiert. Dazu gehören die Speicherung allgemeiner Attribute wie Position, eine eindeutige Integer - ID des Elementes, eine "inSight" - Variable, die für das Scrollen und die Anzeige des LevelElements von Bedeutung ist (siehe auch Kapitel über Scrolling), sowie das GIF, als das das LevelElement gezeichnet wird. Alle J-rio Level Elemente erweitern diese Klasse, alle Elemente von J-rio sind LevelElement - Instanzen (außer die Gegner und J-rio selbst). Die interne Datenrepräsentation des Levels wird in der Klasse <b>Level</b> nur über Instanzen der Kinderklassen der Klasse <b>LevelElement</b> realisiert, die verschiedenen Elemente werden durch ihre verschiedenen ID's identifiziert!</p>
<h4>Die Klasse <i>Ground</i></h4>
<p align="justify">Die Klasse Ground ist eben eine solche konkrete Implementierung der Klasse LevelElement. Allerdings hat ein Ground - Element keine zusätzlichen Attribute, so dass hier lediglich der Konstruktor der Parent - Klasse aufgerufen wird.</p>
<h4>Die Klasse <i>Level</i></h4>
<p align="justify">Die abstrakte Klasse <b>Level</b> ist die vielleicht wichtigste Klasse des Spiels. Die Klasse Level kann die aus 25 Strings bestehende Level - Definition (gespeichert in der Klasse LevelOne) in ein zweidimensionales (bzw. in Kopie auch eindimensional) Array aus LevelElementen übersetzen. Sie verfügt auch über die nötigen Methoden zur Kollisionskontrolle und das Scollen des Levels nötig sind. Wie genau die Methoden arbeiten und wie die interne Repräsentation genau aussieht werden wir im Folgenden noch genauer betrachten.</p>
<h4>Die Klasse <i>LevelOne</i></h4>
<p align="justify">Diese Klasse erweitert die Klasse Level und beinhaltet nun die konkrete Definition des Levels in Form von 25 Strings. Auch das Array mit den Hintergrund - Farben wird hier initialisiert um es in jedem Level anders gestallten zu können.</p>
<h4>Die Klasse <i>C_Jump</i></h4>
<p align="justify">Wie schon im Kapitel über den Leveleditor empfiehlt es sich auch in einem Platform - Game alle konstanten Größen (Spielfeldgröße, Größe der Levelelemente, des Spielers...) in eine Konstantenklasse auszulagern, wie auch hier geschehen.</p>
<h4>Zusammenspiel der Klassen und Erweiterbarkeit</h4>
<p align="justify">Wie schon erwähnt hält die Klasse <b>Main</b> eine Instanz des <b>Player</b> - Objektes, sowie eine Instanz des <b>Levels</b> und ist für das managen des Spielablaufs verantwortlich. Während die Bewegungen, Scollen, Zeichnen und Animieren des Spielers von der Klasse <b>Player</b> übernommen werden, zeigt sich die Klasse <b>Level</b> für die Kollisionskontrolle von Spieler und LevelElementen, das Scollen der Levelelemente und das Zeichnen des Levels verantwortlich und natürlich hält sie eine interne Datenrepräsentation des Levels. Ein Level wird durch eine konkrete Child - Klasse der Klasse <b>Level</b>, in unserem Fall <b>LevelOne</b> implementiert. Bei der Konstruktion einer Instanz der Klasse LevelOne werden die String - Definitionen der Klasse LevelOne in der Klasse Level in ein zweidimensionales Array von <b>LevelElement</b> - Objekten übersetzt. Alle Levelelemente werden von der Klasse <b>LevelElement</b> abgeleitet, in unserem Beispiel ist dies nur die Klasse <b>Ground</b>. <br>
Ich hoffe, dass euch das Klassendesign unseres Platform - Games nun soweit geläufig ist, wenn nicht, dann solltet ihr euch vielleicht nochmal die "Leveleditor" und "Scrolling" - Kapitel sowie den Sourcecode (im Bezug auf das Klassendesign unseres Platform - Games) zu Gemüte führen. Wir wollen uns nun detailierter den Aufgaben der Klassen <b>Player</b> und <b>Level</b> widmen. </p>
<h3><a name="PlatformGamePlayerObjekt">Eine nähere Betrachtung der Klasse <i>Player</i></a></h3>
<p align="justify">Der Steuerung und Animation des Spielers kommt natürlich eine entscheidende Rolle in unserem Spiel zu. Da allerdings das Ganze nicht so einfach ist, denn unser Spieler muss ja auf Platformen stehen bleiben, herunterfallen, wenn er eine Platform verlässt, bzw. auf eine höhere Platform springen können, möchte ich hier kurz beschreiben, wie ich diese Probleme gelöst habe.<br>
Zunächst einmal ist es vielleicht wichtig zu erkennen, dass die Bewegungen des Player - Objektes nicht direkt vom Spieler sondern über einen Umweg gesteuert werden. In der Klasse Player werden durch die Tastatureingaben des Spielers bzw. auch durch die Kollisionskontrolle des Levels lediglich boolsche Variablen auf true oder false gesetzt, die von der run() - Methode der Klasse Main aufgerufene Methode der Klasse Player playerMove() realisiert nun die Bewegung gemäß der aktuellen Werte der boolschen Variablen (flags).</p>
<h4>Die vier verschiedenen Zustände und ihre Kontrolle</h4>
<p align="justify">Dabei verfügt unser Spieler über vier verschiedene Bewegungszustände, die teilweise unabhängig voneinander sind:</p>
<ul>
<li><p align="justify"><b>walking_left</b>: zeigt an ob der Spieler nach links laufen soll, wird hauptsächlich durch die Tastatureingaben des Spielers gesteuert, also auf true gesetzt bei Druck auf die linke Pfeiltaste, auf false gesetzt bei Loslassen der linken Pfeiltaste. Allerdings beeinflusst auch eine Kollision mit einem LevelElement links diesen Flag, da die Bewegung dann natürlich stoppen soll.</p></li>
<li><p align="justify"><b>walking_right</b>: äquivalent zu der walking_left, nur in die andere Richtung (wer hätte das gedacht ;-)</p></li>
<li><p align="justify"><b>jumping</b>: dieser Flag zeigt an, ob der Spieler springen soll. Allerdings wird die Sache hier etwas komplizierter, da das Springen zwar vom Spieler angestoßen wird (Drücken und Halten der 'a' - Taste), aber auch andere Faktoren (z. B. eine Begrenzung nach oben durch eine Platform, getestet durch die Kollisionskontrolle der Klasse Level) den Sprung beeinflussen können. Auch soll der Spieler nicht sozusagen im Sprung bzw. während er fällt springen können, Details dazu folgen später.</p></li>
<li><p align="justify"><b>falling</b>: dieser Flag wird ausschließlich durch die Kollisionskontrolle des Levels beeinflusst und gesetzt. Auch hier bedarf es einiger kleiner Tricks um das Fallen im richtigen Moment zu stoppen und den Spieler genau auf der Oberfläche der Platform zum halten zu bringen (bzw. hier auftretende Fehler zu korrigieren).</p></li>
</ul>
<p align="justify">Die Flags <b>walking_left</b> und <b>walking_right</b> beeinflussen einander gegenseitig, da sie nicht gemeinsam zum selben Zeitpunkt ausgeführt werden können. Ebenso können die Zustände <b>falling</b> und <b>jumping</b> nicht gemeinsam auftreten. Allerdings sind falling / jumping und walking_left / walking_right vollkommen unabhängig voneinander und können somit gemeinsam auftreten, denn der Spieler soll ja in der Lage sein, z. B. nach rechts zu springen oder nach links zu fallen. Gesetzt werden die Flags von zwei unabhängigen Seiten: den Tastatureingaben des Spielers, sowie der Kollisionskontrolle des Levels, die wir uns im Folgenden noch genauer ansehen werden. Zuvor möchte ich euch aber noch kurz den Sourcecode der set - Methoden für die einzelnen Flags, sowie die Methode playerMove(), die für Bewegung und Animation des Spielers zuständig ist vorstellen.</p>
<p align="justify">Die set - Methoden für die Links- und Rechtsbewegungs - Flags sind wirklich denkbar einfach, es braucht hier wohl keine weiteren Erläuterungen.</p>
<i><ul>
// Methode setzt den Flag für eine Linksbewegung des Spielers<br>
public void playerWalkLeft(boolean value)<br>
{<br>
<ul>walking_left = value;</ul>
}<br><br>
// Methode setzt den Flag für eine Rechtsbewegung des Spielers<br>
public void playerWalkRight(boolean value)<br>
{<br>
<ul>walking_right = value;</ul>
}<br>
</ul></i>
<p align="justify">Im Falle der set - Methode für den Flag <b>jumping</b> wird die Sache schon schwieriger. Denn wir müssen hier einige Dinge zusätzlich zum einfachen Setzen der Werte beachten. Zum einen kann ein Sprung nur dann angestoßen werden, wenn der Spieler nicht gerade schon fällt, <b>falling</b> also false ist und wenn der Spieler nicht schon springt, und damit ein weiterer Flag namens <b>jump_lock</b> ebenfalls false ist. Eine weitere Sache betrifft die Sprungbewegung selbst. Um zu steuern, wie weit der Spieler springen kann gibt es einen Counter <b>jump_counter</b>, der zählt, wie weit der Spieler schon gesprungen ist. Dieser muss bei einem ganz neuen Sprung (in diesem Fall ist <b>jumping</b> = false, <b>jump_lock</b> = false und der zu setzende Wert (value) ist true) natürlich wieder auf 0 gesetzt werden (Details zu diesem und anderen Countern findet ihr weiter unten). Hier kommt also der Sourcecode:</p>
<i><ul>
// Methode um die Variable jumping zu setzen<br>
public void playerJump(boolean value)<br>
{<br>
<ul>
// den Sprungcounter zurücksetzen, wenn ein neuer Sprung beginnt<br>
if(!jumping && !jump_lock && value)<br>
{<br>
<ul>jump_counter = 0;</ul>
}<br>
// man kann nur dann springen, wenn man nicht fällt!!<br>
if(falling)<br>
{<br>
<ul>jumping = false;</ul>
}<br>
else<br>
{<br>
<ul>jumping = value;</ul>
}<br>
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -