Creëer Space Invaders met Swift en Sprite Kit Implementing Classes

Wat je gaat creëren

In het eerste deel van deze serie verkenden we de basis van het Sprite Kit-framework en implementeerden we het startscherm van de game. In deze tutorial zullen we de hoofdklassen van de game implementeren.

Swift is een object-georiënteerde taal en we zullen hiervan profiteren door alle entiteiten van de game te scheiden in hun eigen klassen. We beginnen met het implementeren van de binnendringer klasse.

1. Implementatie van de binnendringer Klasse

Stap 1: Maak het binnendringer Klasse

kiezen nieuwe > Het dossier…  van Xcode's het dossier menu, kies Cocoa Touch Class van de iOS> Bron sectie en klik op volgende. Geef de klas een naam binnendringer en zorg ervoor dat het erft van SKSpriteNode. Zeker weten dat Taal ingesteld op Snel. Voer de volgende code in Invader.swift.

import UIKit import SpriteKit class Invader: SKSpriteNode var invaderRow = 0 var invaderColumn = 0 init () let texture = SKTexture (imageNamed: "invader1") super.init (textuur: texture, kleur: SKColor.clearColor (), size: texture.size ()) self.name = "invader" vereist init? (coder aDecoder: NSCoder) super.init (coder: aDecoder) func fireBullet (scène: SKScene) 

De binnendringer klasse is een subklasse van de SKSpriteNodeklasse. Het heeft twee eigenschappen, invaderRow en invaderColumn. De indringers worden uitgelijnd in een raster, net als in het originele spel Space Invaders. De twee eigenschappen geven ons een eenvoudige manier om bij te houden welke rij en kolom de indringer bevat.

In de in hetmethode, initialiseren we een SKTexture aanleg. De init (imageNamed :) methode neemt een afbeelding als een parameter. Vervolgens roepen we de initialisator van de superklasse aan, passerend in de structuur, SKColor.clearColor voor de kleur parameter, en voor de grootte parameter geven we in de grootte van de textuur door. Ten slotte hebben we de naam ingesteld op "Invader" zodat we het later kunnen identificeren.

De in het methode is een aangewezen initialisator, wat betekent dat we de initialisatie moeten delegeren naar een aangewezen initialisator van de binnendringerde superklasse. Dat is waarom we het init (textuur: kleur: maat :) methode.

Je vraagt ​​je misschien af ​​waarom het nodig is init (coder :) methode is er ook. De SKSpriteNode voldoet aan de NSCoding protocol. De init (coder :) methode is gemarkeerd als verplicht, wat betekent dat elke subklasse deze methode moet overschrijven.

We zullen het fireBullet methode later in deze tutorial.

Stap 2: voeg Invaders toe aan de scène

In deze stap zullen we de indringers toevoegen aan de GameScene. Open GameScene.swift en verwijder alles binnen de didMoveToView (_ :) methode evenals alles binnen de touchesBegan (_: withEvent :) methode. De inhoud van GameScene.swift zou er nu zo uit moeten zien.

importeer de SpriteKit-klasse GameScene: SKScene override func didMoveToView (weergave: SKView)  override func touchesBegan (touches: Set, withEvent event: UIEvent) / * Wordt aangeroepen wanneer een aanraking begint * / overschrijft functie-update (currentTime: CFTimeInterval) / * Wordt aangeroepen voordat elk frame wordt weergegeven * /

We zullen één globale variabele hebben in ons project, invaderNum. Deze variabele wordt gebruikt om het huidige niveau van het spel bij te houden. Door het te verklaren als een globale variabele, hebben we toegang tot invaderNum over scènes. Om de variabele als een globale variabele te declareren, verklaren we deze buiten de GameScene klasse.

import SpriteKit var invaderNum = 1 class GameScene: SKScene ... 

Voeg vervolgens de volgende eigenschappen toe aan de GameScene klasse.

class GameScene: SKScene let rowsOfInvaders = 4 var invaderSpeed ​​= 2 let leftBounds = CGFloat (30) var rightBounds = CGFloat (0) var invadersWhoCanFire: [Invader] = [] negeren func didMoveToView (bekijk: SKView) 

De rowsOfInvaders eigenschap is hoeveel rijen indringers het spel zal hebben en de invaderSpeed eigenschap is hoe snel de indringers zullen bewegen. De leftBounds en rightBounds eigenschappen worden gebruikt om een ​​marge aan de linker- en rechterkant van het scherm te creëren, waardoor de beweging van de indringers in de linker- en rechterrichtingen wordt beperkt. En tot slot, de invadersWhoCanFire property is een array die wordt gebruikt om bij te houden welke indringers een kogel kunnen afvuren.

Voeg de toe setupInvaders methode onder de Update (currentTime :) methode in de GameScene klasse.

func setupInvaders () var invaderRow = 0; var invaderColumn = 0; laat numberOfInvaders = invaderNum * 2 + 1 voor var i = 1; ik <= rowsOfInvaders; i++  invaderRow = i for var j = 1; j <= numberOfInvaders; j++  invaderColumn = j let tempInvader:Invader = Invader() let invaderHalfWidth:CGFloat = tempInvader.size.width/2 let xPositionStart:CGFloat = size.width/2 - invaderHalfWidth - (CGFloat(invaderNum) * tempInvader.size.width) + CGFloat(10) tempInvader.position = CGPoint(x:xPositionStart + ((tempInvader.size.width+CGFloat(10))*(CGFloat(j-1))), y:CGFloat(self.size.height - CGFloat(i) * 46)) tempInvader.invaderRow = invaderRow tempInvader.invaderColumn = invaderColumn addChild(tempInvader) if(i == rowsOfInvaders) invadersWhoCanFire.append(tempInvader)     

We hebben de invaderRow en invaderColumn variabelen die zullen worden gebruikt om de eigenschappen van dezelfde naam in te stellen op de indringer. Vervolgens gebruiken we een dubbele voor om de indringers op het scherm te leggen. Er vindt veel conversie van het numerieke type plaats, omdat swift impliciet geen cijfers naar het juiste type converteert. Dat moeten we zelf doen.

We stellen eerst een nieuw voor binnendringertempInvader, en verklaar vervolgens een constante invaderHalfWidth dat is de helft van de grootte van tempInvader.

Vervolgens berekenen we de xPositionStart zodat de indringers altijd in het midden van de scène worden uitgelijnd. We krijgen de helft van de breedte van de scène en trekken de helft van de breedte van de indringer af, omdat het standaard registratiepunt het midden is (0,5, 0,5) van de sprite. We moeten dan de breedte van de tijd van de indringer even vaak aftrekken invaderNum is gelijk aan, en voeg 10 toe aan die waarde, omdat er 10 spatiepunten zijn tussen de indringers. In het begin is dit misschien wat moeilijk te begrijpen, dus neem je tijd om het te begrijpen.

We hebben vervolgens de binnendringer's positie eigendom, dat is een GGPoint. We gebruiken een beetje meer wiskunde om te zorgen dat elke indringer 10 punten ruimte tussen zich heeft en dat elke rij 46 punten ruimte tussen hen heeft.

We wijzen de invaderRow en invaderColumn eigenschappen en voeg de tempInvader naar de scène met de addChild (_ :) methode. Als dit de laatste rij indringers is, plaatsen we de tempInvader in de invadersWhoCanFire rangschikking.

De setupInvaders methode wordt aangeroepen in de didMoveToView (_ :) methode. In deze methode hebben we ook de Achtergrond kleur eigendom aan SKColor.blackColor.

 override func didMoveToView (weergave: SKView) backgroundColor = SKColor.blackColor () setupInvaders ()

Als je de applicatie test, zou je 4 rijen van 3 indringers moeten zien. Als je instelt invaderNum tot 2je zou 4 rijen van 5 indringers in het midden van de scène uitgelijnd moeten zien.

2. Implementatie van de Speler Klasse

Stap 1: Maak het Speler Klasse

Maak een nieuw Cocoa Touch Class genaamd Spelerdat is een subklasse van SKSpriteNode. Voeg de volgende implementatie toe aan Player.swift.

import UIKit import Klasse SpriteKit Speler: SKSpriteNode init () let texture = SKTexture (imageNamed: "player1") super.init (textuur: textuur, kleur: SKColor.clearColor (), size: texture.size ()) animeren ( ) required init? (coder aDecoder: NSCoder) super.init (coder: aDecoder) private func animate () var playerTextures: [SKTexture] = [] voor i in 1 ... 2 playerTextures.append (SKTexture (imageNamed : "player \ (i)")) laat playerAnimation = SKAction.repeatActionForever (SKAction.animateWithTextures (playerTextures, timePerFrame: 0.1)) self.runAction (playerAnimation) func die ()  func kill ()  func respawn ()  func fireBullet (scène: SKScene)  

De in het methode moet bekend voorkomen. Het enige verschil is dat we een andere afbeelding gebruiken voor de eerste installatie. Er zijn twee afbeeldingen met de naam player1 en player2 in de afbeeldingenmap heeft men de boegschroef ingeschakeld en de andere heeft de boegschroef uitgeschakeld. We zullen constant schakelen tussen deze twee beelden, waardoor de illusie ontstaat dat een boegschroef aan en uit gaat. Dit is wat de bezielen methode doet.

In de bezielen methode, we hebben een array playerTextures die de texturen voor de animatie zal bevatten. We voegen het toe SKTexture objecten aan deze array met behulp van a voor inlus en een gesloten bereik met behulp van de operator voor gesloten bereik. We gebruiken stringinterpolatie om de juiste afbeelding te krijgen en een te initialiseren SKTexture aanleg.

We verklaren een constante, playerAnimation, die de repeatActionForever methode van de SKAction klasse. In die actie roepen we aan animateWithTextures (_: timePerFrame :). De animateWithTextures (_: timePerFrame :) methode neemt als parameters een array van texturen en de hoeveelheid tijd dat elke textuur wordt getoond. Ten slotte voeren we aan runAction (_ :) en pas in de playerAnimation.

De andere methoden worden later in deze zelfstudie geïmplementeerd.

Stap 2: De speler aan de scène toevoegen

Declareer een constante eigenschap met de naam speler naar de GameScene klasse.

class GameScene: SKScene ... var invadersWhoCanFire: [Invader] = [Invader] () let player: Player = Player ()

Voeg vervolgens de setupPlayer methode onder de setupInvaders methode.

func setupPlayer () player.position = CGPoint (x: CGRectGetMidX (self.frame), y: player.size.height / 2 + 10) addChild (speler)

U zou bekend moeten zijn met de implementatie van de setupPlayer methode. We hebben de speler's positie en voeg het toe aan de scène. We gebruiken echter een nieuwe functie, CGRectGetMidX (_ :), die het midden van een rechthoek langs de x-as retourneert. Hier gebruiken we de tafereelhet kader.

U kunt nu het setupPlayer methode in de didMoveToView (_ :) methode.

 override func didMoveToView (weergave: SKView) backgroundColor = SKColor.blackColor () setupInvaders () setupPlayer () 

Als je de applicatie test, zou je de speler aan de onderkant van het scherm moeten zien verschijnen terwijl de stuwraketten aanstaan ​​en schieten.

3. Implementatie van de Kogel Klassen

Stap 1: Maak het Kogel klasse

Maak een nieuw Cocoa Touch Class genaamd Kogel dat is een subklasse van de SKSpriteNode klasse.

import UIKit import SpriteKit class Bullet: SKSpriteNode init (imageName: String, bulletSound: String?) let texture = SKTexture (imageNamed: imageName) super.init (textuur: texture, kleur: SKColor.clearColor (), size: texture. size ()) if (bulletSound! = nil) runAction (SKAction.playSoundFileNamed (bulletSound !, waitForCompletion: false)) vereist init? (coder aDecoder: NSCoder) super.init (coder: aDecoder)

De in het methode neemt twee parameters, imageName en Bulletsound. De tweede parameter is optioneel. De speler speelt een lasergeluid telkens wanneer een kogel wordt afgevuurd. Ik heb niet de indringers die dat doen in deze game, hoewel je dat zeker wel zou kunnen. Dat is ook de reden waarom het kogelgeluid een optionele parameter is. Je zou zelfs een ander geluid kunnen gebruiken voor elk geluid.

Het eerste deel zou bekend moeten zijn, hoewel we nu het structuur met welk beeld dan ook als het eerste argument werd doorgegeven. Hiermee kun je verschillende afbeeldingen voor de kogels van de speler en indringers gebruiken als je dat wilde.

Als Bulletsound is niet nul, we hebben een SKAction methode playSoundFileNamed (_: waitForCompletion :). Deze methode neemt als parameters a Draad, welke is de naam van het geluidsbestand inclusief de extensie, en een BoolwaitForCompletion. De waitForCompletion parameter is niet belangrijk voor ons. Als het was ingesteld op waar, dan zou de actie duren, hoe lang het geluidsbestand ook is.

Stap 2: Maak het InvaderBullet Klasse

Maak een nieuw Cocoa Touch Class genaamd InvaderBullet dat is een subklasse van de Kogel klasse.

import UIKit import SpriteKit class InvaderBullet: Bullet overschrijven init (imageName: String, bulletSound: String?) super.init (imageName: imageName, bulletSound: bulletSound) required init? (coder aDecoder: NSCoder) super.init (coder : aDecoder)

De implementatie van de InvaderBullet klasse is misschien niet zo logisch, omdat we alleen de init (imageName: Bulletsound :) methode van de superklasse in de init (imageName: Bulletsound :) initializer. Het zal echter veel logischer zijn waarom het op deze manier is ingesteld wanneer we code toevoegen voor detectie van botsingen.

Stap 3: Maak het PlayerBullet Klasse

Maak een nieuw Cocoa Touch Class genaamd PlayerBullet dat is ook een subklasse van de Kogel klasse. Zoals u kunt zien, is de implementatie van de PlayerBullet klasse is identiek aan die van de InvaderBullet klasse.

import UIKit import SpriteKit class PlayerBullet: Bullet overschrijven init (imageName: String, bulletSound: String?) super.init (imageName: imageName, bulletSound: bulletSound) required init? (coder aDecoder: NSCoder) super.init (coder : aDecoder)

Conclusie

In deze zelfstudie hebben we enkele van de belangrijkste klassen van het spel gemaakt en geïmplementeerd. We hebben een raster van indringers aan de scène toegevoegd en het ruimteschip dat de speler zal besturen. We zullen blijven werken met deze klassen in het volgende deel van deze serie waarin we de gameplay implementeren.