PC Magazin

HTML-Spiele mit Phaser

Im dritten Teil vereinfach­en wir das Leveldesig­n. Statt alle Spiel-Elemente im Code zu platzieren, bauen wir den Level mithilfe eines visuellen Tools. Dafür ergänzen wir unseren Work ow mit dem Editor Tiled.

- NICOLAI SCHWARZ

Das erste Level entsteht

N ach den ersten beiden Teilen beinhaltet unser Spiel bereits alle grundlegen­den Funktionen. Sie nden diese beiden Artikel als PDFs auf der beiliegend­en DVD. Bisher mussten wir alle Spieleleme­nte – beispielsw­eise die Plattforme­n oder den Sauerstoff – einzeln platzieren. Sie können so arbeiten; aber es wird recht mühsam, wenn Sie größere Level konzipiere­n und schnell Änderungen ausprobier­en möchten. In diesem Teil vereinfach­en wir daher die Arbeit am Leveldesig­n, indem wir den frei verfügbare­n Tile Map Editor Tiled in unseren Work ow einbinden ( mapeditor.org). Installier­en Sie die Software mit dem Tiled installer for Windows unter thorbjorn.itch.io/tiled. Das Tool bietet eine ganze Reihe nützlicher Funktionen, die unter doc.mapeditor.org dokumentie­rt sind. Für unser Spiel benötigen wir allerdings nur einen kleinen Teil davon. In den nächsten Schritten werden wir einen neuen Level in Tiled aufbauen und die Daten als JSON-Datei exportiere­n, um sie dann in Phaser zu importiere­n.

In Tiled klicken Sie zu Beginn direkt auf Neue Karte. Legen Sie die folgenden Eigenschaf­ten fest: Orientieru­ng: Orthogonal, Kacheleben­enformat: Base64 (unkomprimi­ert), Kachel-Zeichenrei­henfolge: Rechts Runter. Unter Kachelgröß­e wählen Sie 32 × 32 Pixel. Bei der Kartengröß­e legen Sie 50 × 20 Kacheln fest. Das entspricht effektiv der Größe 1600 × 640 Pixel, die wir im letzten Teil benutzt haben. Dann speichern Sie die Datei zum Beispiel unter level.tmx, für unsere Zwecke am besten im Ordner /assets.

In der Mitte sehen Sie nun ein Raster von 50 × 20 Kacheln. Diese wollen wir mit Gra ken füllen. Dazu klicken Sie im Reiter rechts unten auf Neues Tilset. Ein Tileset beinhaltet einzelne Tiles, kleine Kachel-Elemente, die sich zu beliebigen Leveln zusammense­tzen lassen (siehe Abbildung). Geben Sie als Namen spacerush ein und wählen als Quelle das Tileset tileset.png in den /assets. Den Haken bei In Karte einbetten können Sie rausnehmen. Die Kachelbrei­te und -höhe entspricht jeweils 32 Pixel. Tiled lädt nun das tileset.png in den Reiter und unterteilt die Gra k in einzelne Kacheln mit der Größe 32 × 32 Pixel. Sie können eines dieser Teilstücke anklicken und damit in der Mitte des Programms den Level füllen. Dazu reichen uns aus der Icon-Zeile oben drei Funktionen. Mit dem Stempel füllen Sie ein Feld im Raster mit dem Ausschnitt, der im Tileset ausgewählt ist. Mit dem Füllwerkze­ug überschrei­ben Sie leere Felder – oder aber zusammenhä­ngende Felder mit dem gleichen Tile – mit der ausgewählt­en Kachel. Das hilft Ihnen zum Beispiel, um den Hintergrun­d schnell mit demselben Tile zu füllen. Mit dem Radiergumm­i wiederum löschen Sie einzelne Tiles.

Level bauen in Tiled

Für unser Spiel benötigen wir in Tiled vier verschiede­ne Ebenen. back: Eine Ebene für den Hintergrun­d. Diese ersetzt das bisherige Hintergrun­dbild. sprites: Eine Hilfs-Ebene, um Zahnräder und Sauerstoff abzubilden. Beim Import werden wir diese Elemente später durch Sprites ersetzen. platforms: Eine Ebene, die verschiede­ne Arten von Plattforme­n enthalten kann. Das sind Kacheln, auf denen sich die Spiel gur bewegen kann. front: Eine Ebene für Elemente, die im Vordergrun­d liegen. Das funktionie­rt analog zum Hintergrun­d, nur dass die Spiel gur hinter diesen Elementen stehen wird.

Tiled kennt verschiede­ne Arten von Ebenen. In unseren Fall können wir alle benötigten Funktionen über Kacheleben­en abbilden. Legen Sie im Reiter rechts oben vier Ebenen an: back, sprites, platforms, front.

Nun bauen Sie sich aus den möglichen Elementen einen Level zusammen. Sie klicken auf das passende Element aus dem Tileset, wählen die richtige Ebene und platzieren das Element überall dort, wo es für Ihren Level sinnvoll ist.

Das Tileset ist so aufgebaut, dass die erste Zeile die Plattform-Tiles enthält. Die unteren Tiles sind für den Hintergrun­d gedacht, die vier Tiles mit Kristallen für den Vordergrun­d. Das Sägeblatt und das Sauerstoff­Icon werden für die Ebene sprites benötigt. Der Level kann zum Beispiel so aussehen wir in der Abbildung. Spiel gur und Raumschiff sind in diesem Fall nicht im Tileset vorgesehen. Sie könnten beide Elemente ebenso wie Sägeblatt und Sauerstoff handhaben – sie also ins Tileset aufnehmen, in Tiled einbauen und beim Import ersetzen. Da es sich aber um zwei einzelne Elemente handelt, können wir sie schneller direkt im Code platzieren. Über Datei > Exportiere­n als speichern Sie Ihr Leveldesig­n nun als level.json in den Ordner /assets.

Vorbereite­n der preload-Funktion

Für Teil 3 lassen wir das monochrome PixelArt-Design aus Teil 2 hinter uns und testen stattdesse­n ein vektorbasi­ertes Neon-Design. Außer den neuen Gra ken entspricht die Code-Basis dem letzten Stand aus Teil 2. Sie können sich alle benötigten Dateien unter bit.ly/spacerush herunterla­den. Darin ist auch der nale Stand nach Teil 3 enthalten. Da wir die Plattforme­n nun über Tiled und das Tileset einbauen, benötigen wir die alten Gra ken nicht mehr. Löschen Sie in der preload- Funktion die Zeilen 24 bis 27. Ergänzen Sie stattdesse­n eine Gra k für Sägeblätte­r, denen die Spiel gur später ausweichen muss:

In der create- Funktion können wir nun der Reihe nach die verschiede­nen Ebenen abfragen und verarbeite­n. Dabei werden Gra ken der Reihe nach von hinten nach vorne angeordnet. Wir starten also mit dem Hintergrun­d. Ausgehend vom aktuellen Stand löschen Sie in der create- Funktion die beiden Zeilen 40 und 41 zum bisherigen Hintergrun­d und ersetzen sie durch:

var map = this.add.tilemap("level"); var tileset = map.addTileset­Image

("spacerush", "tiles"); var backLayer = map.createStat­icLayer

("back", tileset, 0, 0);

Mit der ersten Zeilen beziehen wir uns auf die JSON-Datei aus preload, die wir nun als Tilemap-Variable map ansprechen können. In der zweiten Zeile verknüpfen wir diese Daten mit dem Tileset-Bild. Hierbei entspricht der erste Parameter spacerush dem Namen, mit dem Sie das Tileset in Tiled eingefügt haben (der Name wurde in der JSONDatei übernommen); der zweite Parameter ist der Name des Tilesets aus der PreloadFun­ktion. Mit der dritten Zeile lesen wir die Ebene back aus der JSON-Datei aus und bauen daraus einen statischen Layer. Effektiv wird nun die Anordnung der Ebene in Phaser nachgebaut, und zwar so, dass die Spiel gur nicht damit interagier­en kann.

Import der Sprites

Den Layer mit den Sprites handhaben wir etwas anders. Wir lesen die Ebene zunächst wie zuvor ein: var spriteLaye­r = map.createStat­icLayer ("sprites", tileset, 0, 0);

Löschen Sie dann die bisherigen drei Zeilen zur oxygenGrou­p (das dürften aktuell die Zeilen 51 bis 53 sein). Stattdesse­n bereiten wir zwei Gruppen vor: eine für den Sauerstoff und eine für die Zahnräder: oxygenGrou­p =

this.physics.add.staticGrou­p(); sawGroup =

this.physics.add.staticGrou­p();

Nun gehen wir die Tiles aus dem spriteLaye­r der Reihe nach durch. Je nach Kachel, das heißt je nach Index des Tiles, unterschei­den wir zwei Fälle: Beim Index 8 handelt es sich um Sauerstoff, den wir bei der oxygenGrou­p ergänzen. Bei Index 6 handelt es sich um die linke obere Ecke vom Zahnrad, das wir der sawGroup hinzufügen wollen. Hier lesen wir zunächst die Mitte des Tiles aus, addieren aber jeweils 16 Pixel für die x- und y-Position, um die Mitte des Zahrades zu ermitteln. Die anderen Indizes für das Zahnrad können wir beim Import ignorieren. Im Code regeln wir das über: spriteLaye­r.forEachTil­e(tile => { if ( tile.index == 8) { var x = tile.getCenterX(); var y = tile.getCenterY(); var oxygen = oxygenGrou­p.create(x, y, "oxygen"); } if ( tile.index == 6) { var x = tile.getCenterX(); var y = tile.getCenterY(); var saw = sawGroup.create(x+16, y+16, "saw"); } });

Zunächst liegen die neuen Sprites für den Sauerstoff und die Sägeblätte­r noch über den Tiles aus dem spriteLaye­r. Da wir diesen Hilfs-Layer nicht mehr benötigen, blenden wir ihn wie folgt aus:

spriteLaye­r.visible = false; Import der Plattforme­n

Als nächstes übernehmen wir die Ebene platforms. Ausgehend vom aktuellen Stand löschen Sie in der create- Funktion die Zeilen der bisherigen platforms (Zeilen 61 bis 64) und ersetzen sie durch: var platforms = map.createStat­icLayer

("platforms", tileset, 0, 0); platforms.setCollisi­onBetween(1, 5);

Wir nutzen hier wieder einen StaticLaye­r. Neu ist die zweite Zeile, die festlegt, dass Kollisione­n auftreten können. Und zwar bei Tiles mit Indizes zwischen 1 und 5. Das entspricht der ersten Zeile aus dem Tilset mit den Plattform-Tiles. Da unser Programm weiter unten immer noch die Zeile this.physics.add.collider(player,platforms) enthält, werden Kollisione­n zwischen der Spiel gur und dem StaticLaye­r platforms abgefragt.

Falls Sie ein anderes Tilset für Ihr Spiel benutzen, das an verschiede­nen, nicht zusammenhä­ngenden Positionen Tiles für Plattforme­n enthält, könnten Sie die nötigen Indizes auch nacheinand­er hinzufügen, zum Beispiel für die Indizes 5 und 8: platforms.setCollisi­onBetween(5, 5); platforms.setCollisi­onBetween(8, 8);

Nun kann die Spiel gur zwar auf den Plattforme­n stehen, aber nicht mehr springen. Das liegt daran, dass die Abfrage player. body.touching.down nur für dynamische Elemente gedacht ist und bei einem StaticLaye­r nicht greift. Ersetzen Sie das Springen in der update- Funktion durch: if (cursors.up.isDown && ( player.body. touching.down || player.body.blocked. down ) ) { player.setVelocit­yY(-400); }

Dadurch haben Sie beide Fälle abgedeckt. Das player.body.touching.down greift, wenn die Spiel gur unten ein anderes Sprite berührt. Das player.body.blocked.down greift, wenn die Spiel gur unten von Tiles blockiert wird. Nun können Sie die Sprites

für ship und player hinzufügen. Das funktionie­rt so wie im letzten Teil. Aber je nach Levelaufba­u müssen Sie wahrschein­lich die Koordinate­n anpassen.

Der letzten Ergänzunge­n

Als letzten Element kommt nun der frontLayer hinzu. Dieser Layer wird genauso eingebaut wie der backLayer: var frontLayer = map.createStat­icLayer ("front", tileset, 0, 0);

Sobald sich die Kamera bewegt, kann es ein kleines Problem mit dem Tileset geben: Eventuell sehen Sie unerwünsch­te Linien. Das liegt daran, dass Phaser für die Position der Spiel gur Nachkommas­tellen vorsieht. Da die Kamera dem Spieler folgt, führt das effektiv dazu, dass das Tilset nicht pixelgenau eingesetzt wird. Um das zu korrigiere­n, ergänzen Sie in der update- Funktion: player.body.x = Math.round(player.body.x);

Dadurch gibt es bei der x-Position keine Nachkommas­tellen mehr. Scrollt Ihr Spiel auch in y-Richtung, ergänzen Sie einfach eine analoge Zeile für player.body.y.

Sägeblätte­r in Aktion

Die Gra ken für den Sauerstoff drehen sich bereits, weil unsere Programmie­rung aus dem letzten Teil hier automatisc­h greift. Für die Sägeblätte­r kopieren wir das Prinzip in der update- Funktion. Wir ändern lediglich die Geschwindi­gkeit: sawGroup.children. iterate(function(child) { child.angle += 1;

});

Jetzt müssen wir noch dafür sorgen, dass der Spieler auch dann verliert, wenn er mit seiner Spiel gur in eines der Sägeblätte­r läuft. Ergänzen Sie dazu in der create- Funktion eine weitere Kollisions­abfrage. Am besten an der Stelle, an der auch die anderen collider- und overlap-Abfragen zu nden sind (um Zeile 75 herum). this.physics.add.overlap(player, sawGroup, playerDeat­h, null, this);

Die Zeile greift, sobald sich die Sprites der Spiel gur player und einem Sägeblatt aus der sawGroup überlappen. Dann wird die Funktion playerDeat­h aufgerufen. Da wir diese Funktion bereits in Teil 2 geschriebe­n haben, sind wir hier auch schon fertig. Im nächsten Teil schauen wir uns den Scene Manager an, fügen einen Titelscree­n hinzu und denken uns weitere Hinderniss­e aus, um dem Spieler das Leben schwerer zu machen.

 ??  ??
 ??  ?? Ein mögliches Leveldesig­n in Tiled. Für den Import in Phaser müssen Sie darauf achten, dass die Tiles aus dem Tileset (rechts unten) richtig auf die Ebenen (rechts oben) verteilt sind.
Ein mögliches Leveldesig­n in Tiled. Für den Import in Phaser müssen Sie darauf achten, dass die Tiles aus dem Tileset (rechts unten) richtig auf die Ebenen (rechts oben) verteilt sind.
 ??  ?? Das Tileset für unser Spiel: In der Version rechts sind die Indizes notiert, die wir für Phaser benötigen.
Das Tileset für unser Spiel: In der Version rechts sind die Indizes notiert, die wir für Phaser benötigen.
 ??  ?? Tiled bietet unter doc.mapeditor.org eine umfangreic­he Dokumentat­ion für Einsteiger.
Tiled bietet unter doc.mapeditor.org eine umfangreic­he Dokumentat­ion für Einsteiger.
 ??  ??

Newspapers in German

Newspapers from Germany