Birthday Sale! Up to 40% off unlimited courses & creative assets Birthday Sale! Save up to 40%!
Advertisement
  1. Code
  2. SpriteKit
Code

Erschaffen der Weltraum Eindringlinge mit Swift und Sprite-Satz: Implementierung des Spielablaufs

by
Difficulty:BeginnerLength:LongLanguages:
This post is part of a series called Create Space Invaders with Swift and Sprite Kit.
Create Space Invaders with Swift and Sprite Kit: Implementing Classes
Create Space Invaders with Swift and Sprite Kit: Finishing Gameplay

German (Deutsch) translation by Katharina Nevolina (you can also view the original English article)

Final product image
What You'll Be Creating

Im vorherigen Teil dieser Serie haben wir den Stummel für die Hauptklassen des Spiels implementiert.  In diesem Tutorial werden die Eindringlinge in Bewegung versetzt, Kugeln sowohl für Angreifer als auch für Spieler abgefeuert und die Kollisionserkennung implementiert.  Lassen Sie uns anfangen.

1. Verlagerung der Eindringlinge

Wir werden die update-Methode der Szene verwenden, um die Eindringlinge zu bewegen.  Wann immer Sie etwas manuell verschieben möchten, ist die update-Methode im Allgemeinen, wo Sie es tun möchten.

Bevor wir das jedoch tun, müssen wir die Eigenschaft rightBounds aktualisieren.  Es wurde anfangs auf 0 gesetzt, weil wir size der Szene verwenden müssen, um die Variable zu setzen.  Wir konnten das außerhalb keiner der Methoden der Klasse tun, daher werden wir diese Eigenschaft in der Methode didMoveToView(_:) aktualisieren.

Implementieren Sie als Nächstes die moveInvaders-Methode  unter der setupPlayer-Methode, die Sie im vorherigen Tutorial erstellt haben.

Wir deklarieren eine Variable, changeDirection, um den Überblick zu behalten, wenn die Eindringlinge die Richtung ändern müssen, wenn sie sich nach links oder nach rechts bewegen.  Dann verwenden wir die Methode enumerateChildNodesWithName(usingBlock:), die die untergeordneten Elemente eines Knotens durchsucht und die Schließung einmal für jeden übereinstimmenden Knoten aufruft, der mit dem übereinstimmenden Namen "invader" gefunden wird.  Die Schließung akzeptiert zwei Parameter, node ist der Knoten, der name entspricht, und stop ist ein Zeiger auf eine boolesche Variable, um die Aufzählung zu beenden.  Wir werden hier nicht stop verwenden, aber es ist gut zu wissen, wofür es gebraucht wird.

Wir werfen node auf eine SKSpriteNode-Instanz, deren invader eine Unterklasse ist, bekommen die Hälfte ihrer Breite invaderHalfWidth und aktualisieren ihre Position.  Wir überprüfen dann, ob seine position innerhalb der Grenzen liegt, linksBounds und rechtsBounds, und wenn nicht, setzen wir changeDirection auf true.

Wenn changeDirection true ist, negieren wir invaderSpeed, was die Richtung ändert, in die sich der Eindringling bewegt.  Wir zählen dann durch die Eindringlinge und aktualisieren ihre y-Position.  Zum Schluss setzen wir changeDirection zurück auf false.

Die moveInvaders-Methode  wird in der update(_:)-Methode aufgerufen.

Wenn Sie die Anwendung jetzt testen, sollten Sie sehen, dass sich die Eindringlinge nach links, rechts und dann nach unten bewegen, wenn sie die Grenzen erreichen, die wir auf beiden Seiten festgelegt haben.

2. Schießen mit Kugeln von Eindringlingen

Schritt 1: fireBullet

Hin und wieder wollen wir, dass einer der Eindringlingen eine Kugel abfeuert.  So wie es jetzt aussieht, sind die Eindringlinge in der unteren Reihe so eingestellt, dass sie eine Kugel abfeuern, weil sie sich im invadersWhoCanFire-Feld  befinden.

Wenn ein Eindringling von einer Kugel eines Spielers getroffen wird, wird der Eindringling, der eine Reihe höher ist und in der gleichen Spalte steht, zum invadersWhoCanFire-Feld  hinzugefügt, während der Eindringling, der getroffen wurde, entfernt wird.  Auf diese Weise kann nur der unterste Eindringling jeder Spalte Geschosse abfeuern.

Fügen Sie die fireBullet-Methode zur InvaderBullet-Klasse in InvaderBullet.swift hinzu.

In der fireBullet-Methode instanziieren wir eine InvaderBullet-Instanz, indem wir "laser" für imageName übergeben, und da wir keinen Sound abspielen möchten, geben wir für bulletSound nil ein.  Wir setzen seine position so, dass sie mit der der y-Position übereinstimmt, und fügen sie der Szene hinzu.

Wir erstellen zwei SKAction-Instanzen, moveBulletAction und removeBulletAction.  Die  moveBulletAction-Aktion verschiebt das Geschoss über eine bestimmte Dauer auf einen bestimmten Punkt, während die removeBulletAction-Aktion  es aus der Szene entfernt.  Durch Aufrufen der sequence(_:)-Methode für diese Aktionen werden sie sequenziell ausgeführt.  Deshalb habe ich die Methode waitForDuration beim Abspielen eines Sounds im vorherigen Teil dieser Serie erwähnt.  Wenn Sie ein SKAction-Objekt erstellen, indem Sie playSoundFileNamed(_: waitForCompletion:) aufrufen und waitForCompletion auf true setzen, dauert die Dauer dieser Aktion so lange, wie der Sound abgespielt wird. Andernfalls wird sofort zur nächsten Aktion in der Sequenz gesprungen.

Schritt 2: invokeInvaderFire

Fügen Sie die invokeInvaderFire-Methode unter den anderen Methoden hinzu, die Sie in GameScence.swift erstellt haben.

Die runBlock(_:)-Methode der SKAction-Klasse erstellt eine SKAction-Instanz und ruft sofort den Abschluss auf, der an die runBlock(_:)-Methode übergeben wird.  In der Schließung rufen wir die fireInvaderBullet-Methode  auf.  Da wir diese Methode in einer Schleißung aufrufen, müssen wir self verwenden, um sie aufzurufen.

Wir erstellen dann eine SKAction-Instanz mit dem Namen waitToFireInvaderBullet, indem wir waitForDuration(_:) aufrufen und die Wartezeit in Sekunden angeben, bevor wir fortfahren.  Als Nächstes erstellen wir eine SKAction-Instanz, invaderFire, indem wir die sequenz(_:)-Methode aufrufen.  Diese Methode akzeptiert eine Sammlung von Aktionen, die von der invaderFire-Aktion  aufgerufen werden.  Wir möchten, dass sich diese Sequenz für immer wiederholt, also erstellen wir eine Aktion namens repeatForeverAction, übergeben die SKAction-Objekte zur Wiederholung und rufen runAction auf, wobei die Aktion repeatForeverAction übergeben wird.  Die runAction-Methode wird in der SKNode-Klasse deklariert.

Schritt 3: fireInvaderBullet

Fügen Sie die fireInvaderBullet-Methode  unterhalb der invokeInvaderFire-Methode  hinzu, die Sie im vorherigen Schritt eingegeben haben.

In dieser Methode rufen wir eine Methode Namens randomElement auf, die ein zufälliges Element aus dem invadersWhoCanFire-Array  zurückgibt und dann die fireBullet-Methode  aufruft.   Es gibt leider keine eingebaute randomElement-Methode für die Array-Struktur.  Wir können jedoch eine Array-Erweiterung erstellen, um diese Funktionalität bereitzustellen.

Schritt 4: Implementieren Sie randomElement

GehenSie zu Datei > Neu > Datei (File > New > File)... und wählen Sie Swift File.  Wir machen etwas anderes als vorher, also stellen Sie sicher, dass Sie Swift File und nicht Cocoa Touch Class wählen.  Drücken Sie Weiter und benennen Sie die Datei Dienstprogramme (Utilities).  Fügen Sie Folgendes zu Utilities.swift hinzu.

Wir erweitern die Array-Struktur um eine Methode namens randomElement.  Die arc4random_uniform-Funktion  gibt eine Zahl zwischen 0 und dem übergebenen Wert zurück.  Da Swift numerische Typen nicht implizit konvertiert, müssen wir die Konvertierung selbst durchführen.  Schließlich geben wir das Element des Arrays am index index zurück.

Dieses Beispiel zeigt, wie einfach es ist, der Struktur und den Klassen Funktionalität hinzuzufügen.  Sie können mehr über das Erstellen von Erweiterungen in der Swift-Programmiersprache lesen.

Schritt 5: Schießen mit Kugeln

Mit all dem können wir jetzt die Kugeln abfeuern.  Fügen Sie der didMoveToView(_:)-Methode  Folgendes hinzu.

Wenn Sie die Anwendung jetzt testen, sollten Sie jede Sekunde sehen, dass einer der Eindringlinge aus der unteren Reihe eine Kugel abfeuert.

3. Schießen Kugeln von Spielern

Schritt 1: fireBullet (szene:)

Fügen Sie der Player-Klasse in Player.swift die folgende Eigenschaft hinzu.

Wir wollen begrenzen, wie oft der Spieler eine Kugel abfeuern kann.  Die canFire-Eigenschaft wird verwendet, um das zu regulieren.  Fügen Sie als Nächstes der fireBullet(scene:)-Methode  in der Player-Klasse Folgendes hinzu.

Zuerst stellen wir sicher, dass der Spieler feuern kann, indem wir prüfen, ob canFire auf true gesetzt ist.  Wenn nicht, kehren wir sofort von der Methode zurück.

Wenn der Spieler feuern kann, setzen wir canFire auf false, damit nicht sofort eine andere Kugel abgefeuert werden kann.  Wir instanziieren dann eine PlayerBullet-Instanz und übergeben "laser" für den imageNamed-Parameter.  Da wir wollen, dass ein Sound abgespielt wird, wenn der Spieler eine Kugel abfeuert, geben wir "laser.mp3"  für den  bulletSound-Parameter ein.

Wir setzen dann die Position der Kugel und fügen sie dem Bildschirm hinzu.  Die nächsten paar Zeilen sind die gleichen wie die fireBullet-Methode des Invaders, indem wir das Geschoss verschieben und es aus der Szene entfernen.  Als Nächstes erstellen wir eine SKAction-Instanz, waitToEnableFire, indem wir die waitForDuration(_:)-Klassenmethode  aufrufen.  Schließlich rufen wir runAction auf, übergeben waitToEnableFire und setzen nach Abschluss canFire auf true zurück.

Schritt 2: Schießen Kugeln vom Spieler

Wenn der Benutzer den Bildschirm berührt, möchten wir eine Kugel abfeuern.  Das ist so einfach wie das Aufrufen von fireBullet auf dem player-Objekt in der touchsBegan(_:withEvent:)-Methode  der GameScene-Klasse.

Wenn Sie die Anwendung jetzt testen, sollten Sie in der Lage sein, eine Kugel zu schießen, wenn Sie auf den Bildschirm tippen.  Außerdem sollten Sie den Laser jedes Mal hören, wenn eine Kugel abgefeuert wird.

4. Kollisionskategorien

Um zu erkennen, wann Knoten kollidieren oder miteinander in Kontakt kommen, verwenden wir die in Sprite-Satz integrierte Physik-Engine.  Das Standardverhalten der Physik-Engine besteht jedoch darin, dass alles mit allem kollidiert, wenn ihnen ein physikalischer Körper hinzugefügt wird.  Wir brauchen einen Weg, um das zu trennen, was wir miteinander interagieren wollen, und wir können das tun, indem wir Kategorien schaffen, zu denen bestimmte physikalische Körper gehören.

Sie definieren diese Kategorien mithilfe einer Bitmaske, die eine 32-Bit-Ganzzahl mit 32 einzelnen Flags verwendet, die entweder aktiviert oder deaktiviert sein können.  Das bedeutet auch, dass Sie maximal 32 Kategorien für Ihr Spiel haben können.  Das sollte für die meisten Spiele kein Problem darstellen, aber es ist etwas zu beachten.

Fügen Sie der GameScene-Klasse unter dem invaderNum die folgende Strukturdefinition in GameScene.swift hinzu.

Wir verwenden eine Struktur, CollsionCategories, um Kategorien für die Invader-, Player-, InvaderBullet- und PlayerBullet-Klassen zu erstellen.  Wir verwenden Bit-Shifting, um die Bits einzuschalten.

5. Player und InvaderBullet Collision

Schritt 1: InvaderBullet für Collision einrichten

Fügen Sie der init(imageName:bulletSound:)-Methode  in InvaderBullet.swift den folgenden Codeblock hinzu.

Es gibt mehrere Möglichkeiten, einen Physikkörper zu erstellen.  In diesem Beispiel verwenden wir den Initialisierer init(texture:size:), der dafür sorgt, dass die Kollisionserkennung die Form der Textur verwendet, die wir übergeben.  Es sind mehrere andere Initialisierer verfügbar, die Sie in der Klassenreferenz SKPhysicsBody sehen können.

Wir hätten leicht den init(rectangleOfSize:)-Initialisierer  verwenden können, da die Kugeln eine rechteckige Form haben.  Bei einem so kleinen Spiel spielt es keine Rolle.  Beachten Sie jedoch, dass die Verwendung der init(texture:size:)-Methode  rechnerisch sein kann, da sie die genaue Form der Textur berechnen muss.  Wenn Sie Objekte mit rechteckiger oder kreisförmiger Form haben, sollten Sie diese Initialisierungsarten verwenden, wenn die Leistung des Spiels zum Problem wird.

Damit die Kollisionserkennung funktioniert, muss mindestens einer der Körper, die Sie testen, als dynamisch markiert werden. Wenn Sie die usesPreciseCollisionDetection-Eigenschaft auf true festlegen, verwendet Sprite Kit eine genauere Kollisionserkennung.  Setzen Sie diese Eigenschaft in kleinen, sich schnell bewegenden Körpern wie unseren Kugeln auf true.

Jeder Körper gehört zu einer Kategorie und Sie definieren das, indem Sie seine categoryBitMask festlegen.  Da es die InvaderBullet-Klasse ist, legen wir sie auf CollisionCategories.InvaderBullet fest.

Um zu bestimmen, wann dieser Körper Kontakt mit einem anderen Körper aufgenommen hat, an dem Sie interessiert sind, legen Sie die contactBitMask fest.  Hier möchten wir wissen, wann der InvaderBullet Kontakt mit dem Player aufgenommen hat, also verwenden wir CollisionCategories.Player.  Da eine Kollision keine physikalischen Kräfte auslösen soll, setzen wir collisionBitMask auf 0x0.

Schritt 2: Einrichten Player für Collsion

Fügen Sie der init-Methode in Player.swift Folgendes hinzu.

Vieles davon sollte aus dem vorherigen Schritt bekannt sein, also werde ich es hier nicht erneut aufarbeiten.  Es gibt jedoch zwei Unterschiede zu beachten.  Einer davon ist, dass usesPreciseCollsionDetection auf false gesetzt wurde, was der Standardwert ist.  Es ist wichtig zu wissen, dass nur einer der berührenden Körper diese Eigenschaft auf true setzen muss (was die Aufzählung war).  Der andere Unterschied ist, dass wir auch wissen wollen, wann der Spieler einen Eindringling kontaktiert.  Sie können mehr als eine contactBitMask-Kategorie haben, indem Sie sie mit dem bitweisen oder (|) -Operator trennen.  Abgesehen davon sollten Sie bemerken, dass es im Grunde im Gegensatz zu InvaderBullet ist.

6. Invader und PlayerBullet Collision

Schritt 1: Invader für Collision einrichten

Fügen Sie der init-Methode in Invader.swift Folgendes hinzu.

Das sollte alles sinnvoll sein, wenn Sie mitbekommen haben.  Wir richten die physicsBody, categoryBitMask und contactBitMask ein.

Schritt 2: Einrichten von PlayerBullet für Collision

Fügen Sie Folgendes zu dem init(imageName:bulletSound:) in PlayerBullet.swift hinzu.  Auch hier sollte die Implementierung vertraut sein.

7. Einrichten von Physik für GameScene

Schritt 1: Konfigurieren der Welt der Physik

Wir müssen die GameScene-Klasse einrichten, um SKPhysicsContactDelegate zu implementieren, damit wir reagieren können, wenn zwei Körper kollidieren.  Fügen Sie Folgendes hinzu, um die GameScene-Klasse dem SKPhysicsContactDelegate-Protokoll anzupassen.

Als nächstes müssen wir einige Eigenschaften in der physicsWorld der Szene einrichten.  Geben Sie oben in der Methode didMoveToView(_:) in GameScene.swift Folgendes ein.

Wir setzen die gravity-Eigenschaft von physicsWorld auf 0, so dass keiner der Physikkörper in der Szene von der Schwerkraft beeinflusst wird.  Sie können das auch für jeden einzelnen Körper tun, anstatt die ganze Welt so einzustellen, dass sie keine Schwerkraft hat, indem Sie die affectedByGravity-Eigenschaft festlegen.  Wir setzen auch die contactDelegate-Eigenschaft  der Physikwelt auf self, die GameScene-Instanz.

Schritt 2: Implementieren des SKPhysicsContactDelegate-Protokolls

Um die GameScene-Klasse an das SKPhysicsContactDelegate-Protokoll anzupassen, müssen wir die didBeginContact(_ :)-Methode  implementieren.  Diese Methode wird aufgerufen, wenn zwei Körper in Kontakt kommen.  Die Implementierung der didBeginContact(_:)-Methode sieht folgendermaßen aus.

Wir deklarieren zunächst zwei firstBody- und secondBody-Variablen .  Wenn zwei Objekte Kontakt haben, wissen wir nicht, welcher Körper welches ist.  Das bedeutet, dass wir zuerst einige Überprüfungen durchführen müssen, um sicherzustellen, dass firstBody mit der niedrigeren categoryBitMask übereinstimmt.

Als nächstes gehen wir jedes mögliche Szenario durch, indem wir den bitweisen & Operator und die Kollisionskategorien verwenden, die wir zuvor definiert haben, um zu überprüfen, was Kontakt macht.  Wir protokollieren das Ergebnis an der Konsole, um sicherzustellen, dass alles ordnungsgemäß funktioniert.  Wenn Sie die Anwendung testen, sollten alle Kontakte ordnungsgemäß funktionieren.

Schlussfolgerung

Das war ein ziemlich langes Tutorial, aber wir haben jetzt die Eindringlinge in Bewegung, Kugeln werden sowohl vom Spieler als auch von den Eindringlingen abgefeuert und die Kontakterkennung funktioniert mit Hilfe von Kontaktbitmasken.  Wir sind auf der Zielgeraden bis zum letzten Spiel.  Im nächsten und letzten Teil dieser Serie werden wir ein abgeschlossenes Spiel haben.

Advertisement
Advertisement
Advertisement
Advertisement
Looking for something to help kick start your next project?
Envato Market has a range of items for sale to help get you started.