Create Space Invaders med Swift og Sprite Kit: Ferdigstilling Gameplay 
 26 
 Del 
 3 
 Share < .no> Dette Cyber mandag Envato Tuts + kurs vil bli redusert til bare $ 3. Ikke gå glipp av 
 Dette innlegget er en del av en serie som heter Lag Space Invaders med Swift og Sprite Kit.Create Space Invaders med Swift og Sprite Kit. Implementering GameplayWhat du skal lage 
 I forrige del av denne serien, gjorde vi inntrengerne flytte, spilleren og inntrengerne skyte kuler, og implementert dueller. I den fjerde og siste del av denne serien, vil vi legge til muligheten til å flytte spilleren ved hjelp av akselerometer, administrere nivåene, og sikre spilleren dør når truffet av en kule. La oss komme i gang. 
 
 1. Avslutte Player Klasse 
 
 Trinn 1: Legge Properties 
 
 Legg følgende egenskaper til spilleren klassen under canFire eiendom 
 private Var uovervinnelig = falseprivate Var bor. Int = 3 {didSet {if (liv < 0) {drepe ()} else {respawn ()}}} 
 Den uovervinnelige Eiendommen vil bli brukt til å gjøre spilleren midlertidig uovervinnelig når det mister et liv. Den liv Eiendommen er antall liv spilleren har før han ble drept. 
 
 Vi bruker en eiendom observatør på liv eiendommen, som vil bli kalt hver gang sin verdi er satt. Den didSet observatør kalles umiddelbart etter at den nye verdien av eiendommen er satt. Ved å gjøre dette, hver gang vi minske liv egenskapen det sjekker automatisk om liv er mindre enn null, ringer kill metoden hvis det er. Dersom spilleren har liv igjen, er respawn metoden påberopes. Eiendoms observatører er veldig praktisk og kan spare en masse ekstra kode 
 
 Trinn 2:. Respawn 
 
 respawn metoden gjør spilleren uovervinnelig for en kort tid og fades spilleren inn og ut for å indikere at det er midlertidig uovervinnelig. Gjennomføringen av respawn metoden ser slik ut: 
 func respawn () {uovervinnelig = true la fadeOutAction = SKAction.fadeOutWithDuration (0,4) la fadeInAction = SKAction.fadeInWithDuration (0,4) la fadeOutIn = SKAction.sequence ([fadeOutAction, fadeInAction ]) la fadeOutInAction = SKAction.repeatAction (fadeOutIn, telle: 5) la setInvicibleFalse = SKAction.runBlock () {self.invincible = false} runAction (SKAction.sequence ([fadeOutInAction, setInvicibleFalse]))} 
 Vi setter uovervinnelig å true og skape en rekke SKAction stedene. Nå bør du være kjent med hvordan SKAction klassen fungerer 
 
 Trinn 3:. Dø 
 
 Terningen metoden er ganske enkel. Den sjekker om uovervinnelig er falsk, og hvis det er, dekrementerer livene variabel 
 func die () {if (uovervinnelig == false) {liv - = 1}} 
 Trinn 4:. Drepe 
 < p> kill metoden tilbake invaderNum til 1 og tar brukeren tilbake til StartGameScene slik at de kan starte et nytt spill 
 func kill () {invaderNum = 1 la gameOverScene = StartGameScene. (size: self.scene .size) gameOverScene.scaleMode = self.scene .scaleMode la transitionType = SKTransition.flipHorizontalWithDuration (0,5) self.scene .Utsikten .presentScene (gameOverScene, overgang: transitionType)!!} 
 Denne koden skal være kjent for deg som det er nesten identisk med koden vi pleide å flytte til GameScene fra StartGameScene. Merk at vi tvinge pakke scenen for å få tilgang til scenens størrelse og scaleMode egenskaper. 
 
 Dette fullfører Player klassen. Vi trenger nå å ringe dø og drepe metoder i didBeginContact (_ :) metode 
 func didBeginContact (kontakt:. SKPhysicsContact) {... if ((firstBody.categoryBitMask & CollisionCategories.Player = 0! ) & & (secondBody.categoryBitMask & CollisionCategories.InvaderBullet = 0)) {player.die ()} if ((firstBody.categoryBitMask &! CollisionCategories.Invader = 0) & & (secondBody.categoryBitMask &! CollisionCategories.Player = 0)) {player.kill ()}} 
 Vi kan nå teste alt. En rask måte å teste formen metoden er ved å kommentere ut moveInvaders ringe i oppdateringen (_ :) metoden. Etter at spilleren dør og respawns tre ganger, bør du bli tatt tilbake til StartGameScene. 
 
 For å teste kill metoden, pass på at moveInvaders samtalen er ikke kommentert ut. Sett invaderSpeed eiendommen til en høy verdi, for eksempel, bør 200. Inntrengerne nå spiller veldig raskt, noe som resulterer i en umiddelbar drepe. Endre invaderSpeed tilbake til to når du er ferdig med testing. 
 
 2. Behandling Firing Invaders 
 
 I det kampen står akkurat nå, kan bare den nederste raden av inntrengerne skyte kuler. Vi har allerede dueller for når en spiller kule treffer en inntrenger. I dette trinnet, vil vi fjerne en inntrenger som blir truffet av en kule og legge inntrengeren én rad opp til rekken av inntrengere som kan avfyres. Legg til følgende til didBeginContact (_ :) metode 
 func didBeginContact (kontakt:.! SKPhysicsContact) {... if ((firstBody.categoryBitMask & CollisionCategories.Invader = 0) & & (secondBody.categoryBitMask & CollisionCategories.PlayerBullet = 0)) {if (contact.bodyA.node .parent == null || contact.bodyB.node .parent == null) {return} la invadersPerRow = invaderNum * 2 + 1 la theInvader!? = firstBody.node som! Invader la newInvaderRow = theInvader.invaderRow - en la newInvaderColumn = theInvader.invaderColumn if (newInvaderRow > = 1) {self.enumerateChildNodesWithName ("invader") {node, stopp i la inntrenger = node som! Invader hvis invader.invaderRow == newInvaderRow & & invader.invaderColumn == newInvaderColumn {self.invadersWhoCanFire.append (inntrenger) stop.memory = true}}} la invaderIndex = findIndex (invadersWhoCanFire, valueToFind:? firstBody.node som Invader) if (! invaderIndex = null) {invadersWhoCanFire.removeAtIndex (invaderIndex!)} theInvader.removeFromParent () secondBody.node? .removeFromParent ()}} 
 Vi har fjernet NSLog erklæringen og først sjekke om contact.bodyA.node? .parent og contact.bodyB.node ?. forelder er ikke null. De vil være null hvis vi allerede har behandlet denne kontakten. I så fall, vi tilbake fra funksjonen. 
 
 Vi beregner invadersPerRow som vi har gjort før, og satt theInvader til firstBody.node, støping den til en inntrenger. Deretter får vi newInvaderRow ved å trekke en og newInvaderColumn, som forblir den samme. 
 
 Vi ønsker bare å aktivere inntrengerne til brann hvis newInvaderRow er større enn eller lik 1, ellers ville vi være å prøve å sette en inntrenger i rad 0 for å kunne skyte. Det er ingen rad 0 slik at dette ville føre til en feil. 
 
 Neste, nummerere vi gjennom inntrengerne, på jakt etter inntrenger som har riktig rad og kolonne. Når den er funnet, vi legge det til invadersWhoCanFire array og ringe stop.memory til sann så opptellingen vil slutte tidlig. 
 
 Vi må finne inntrengeren som ble truffet med en kule i invadersWhoCanFire matrise slik at vi kan fjerne det. Normalt arrays har noen form for funksjonalitet som en indexOf metode eller noe lignende for å oppnå dette. I skrivende stund er det ingen slik metode for arrays i Swift språk. Swift Standard Library definerer et funn funksjon som vi kan bruke, men jeg fant en metode i avsnittene om generika i Swift Programming Language Guide som vil oppnå det vi trenger. Funksjonen er treffende navnet findIndex. Legg til følgende til bunnen av GameScene.swift 
 func findIndex < T: equatable > (matrise: [T], valueToFind: T) -. ≫ Int? {For (indeks, verdi) i nummerere (array) {if verdi == valueToFind {return index}} avkastning lik null} 
 Hvis du er nysgjerrig på hvordan det denne funksjonen fungerer, så jeg anbefaler deg å lese mer om generika i Swift Programming Language Guide. 
 
 Nå som vi har en metode vi kan bruke til å finne inntrengeren, vi påkalle det, passerer i invadersWhoCanFire matrise og theInvader. Vi sjekker om invaderIndex ikke er lik null og fjerne inntrengeren fra invadersWhoCanFire array ved hjelp av removeAtIndex (indeks: Int). Metode 
 
 Du kan nå teste om det fungerer som det skal. En enkel måte ville være å kommentere ut hvor kallet til player.die i didBeginContact (_ :) metoden. Pass på at du fjerner kommentar når du er ferdig å teste. Legg merke til at programmet krasjer hvis du dreper alle inntrengerne. Vi vil fikse dette i neste trinn. 
 
 De programmet krasjer, fordi vi har en SKAction repeatActionForever (_ :) ringer for inntrengere å skyte kuler. På dette punktet, er det ingen inntrengere igjen å skyte kuler så de spillene krasjer. Vi kan fikse dette ved å sjekke gjelder tom bil eiendommen på invadersWhoCanFire array. Dersom matrisen er tom, er nivået over. Skriv inn følgende i fireInvaderBullet metoden. 
 Func fireInvaderBullet () {if (invadersWhoCanFire.isEmpty) {invaderNum + = 1 levelComplete ()} else {la randomInvader = invadersWhoCanFire.randomElement () randomInvader.fireBullet (egen-)}} < p> Nivået er fullført, noe som betyr at vi inkrementer invaderNum, som brukes for nivåene. Vi påberope også levelComplete, som vi fortsatt trenger å skape i trappen kommer opp. 
 
 3. Fullfører en Level 
 
 Vi må ha et visst antall nivåer. Hvis vi ikke gjør det, etter flere runder vil vi ha så mange inntrengere de ikke får plass på skjermen. Legge en eiendom maxLevels til GameScene klassen 
 klasse GameScene. SKScene, SKPhysicsContactDelegate {... la spiller: Spiller = Player () la maxLevels = 3 
 Nå kan du legge den levelComplete metoden nederst GameScene.swift. 
 func levelComplete () {if (invaderNum < = maxLevels) {la levelCompleteScene = LevelCompleteScene? (størrelse: størrelse) levelCompleteScene.scaleMode = scaleMode la transitionType = SKTransition.flipHorizontalWithDuration (0,5) view .presentScene (levelCompleteScene, overgang: transitionType )} else {invaderNum = 1 newGame ()}} 
 Vi sjekker først for å se om invaderNum er mindre enn eller lik maxLevels vi har satt. Hvis ja, vi overgangen til LevelCompletScene, ellers null vi invaderNum til en og ringe newGame. LevelCompleteScene ikke finnes ennå og heller ikke den newGame metoden så la oss takle disse en om gangen i løpet av de neste to trinnene. 
 
 4. Implementere LevelCompleteScene Class 
 
 Opprett en ny Cocoa Touch Class heter LevelCompleteScene som er en sublclass av SKScene. Gjennomføringen av klassen ser slik ut: 
 import Foundationimport SpriteKitclass LevelCompleteScene: SKScene {ride func didMoveToView (vis: SKView) {self.backgroundColor = SKColor.blackColor () la startGameButton = SKSpriteNode (imageNamed: "nextlevelbtn") startGameButton. stilling = CGPointMake (size.width /2, size.height /2 - 100) startGameButton.name = "nextlevel" addChild (startGameButton)} overstyring func touchesBegan (berørt: Set < NSObject >, withEvent hendelse: UIEvent) {la berørings = touches.first som! UITouch la touchLocation = touch.locationInNode (egen-) la touchedNode = self.nodeAtPoint (touchLocation) if (touchedNode.name == "nextlevel") {la gameOverScene = GameScene (størrelse: størrelse) gameOverScene.scaleMode = scaleMode la transitionType = SKTransition. ? flipHorizontalWithDuration (0,5) view .presentScene (gameOverScene, overgang: transitionType)}}} 
 Gjennomføringen er identisk med StartGameScreen klassen, bortsett vi setter navnet tilhører startGameButton til «nextlevel". Denne koden skal være kjent. Hvis ikke, så dra tilbake til den første delen av denne opplæringen for en oppfriskning. 
 
 5. newGame 
 
 newGame metoden bare overganger tilbake til StartGameScene. Legg til følgende til bunnen av GameScene.swift 
 func newGame () {la gameOverScene = StartGameScene.? (Størrelse: størrelse) gameOverScene.scaleMode = scaleMode la transitionType = SKTransition.flipHorizontalWithDuration (0,5) view .presentScene (gameOverScene, overgang : transitionType)} 
 Hvis du tester programmet, kan du spille noen nivåer eller tape et par kamper, men spilleren har ingen måte å flytte og dette gir et kjedelig spill. La oss fikse det i neste trinn. 
 
 6. Flytte spilleren ved hjelp av akselerometer 
 
 Vi vil bruke akselerometer til å flytte spilleren. Vi må først importere CoreMotion rammeverket. Legg en import statement for rammen på toppen av GameScene.swift 
 importere SpriteKitimport CoreMotion 
 Vi trenger også et par nye eiendommer 
 la maxLevels = 3let Motion:.. CMMotionManager = CMMotionManager () Var accelerationX: CGFloat = 0.0 
 Deretter legge til en metode setupAccelerometer nederst GameScene.swift 
 func setupAccelerometer () {motionManager.accelerometerUpdateInterval = 0,2 motionManager.startAccelerometerUpdatesToQueue (NSOperationQueue.currentQueue (), withHandler:. {(accelerometerData: CMAccelerometerData !, error: NSError) i utleid akselerasjon = accelerometerData.acceleration self.accelerationX = CGFloat (acceleration.x)})} 
 Her kan vi sette accelerometerUpdateInterval, som er intervallet i sekunder for å gi oppdateringer til behandleren. Jeg fant 0,2 fungerer godt, kan du prøve forskjellige verdier hvis du ønsker det. Inne behandleren; en nedleggelse, vi får accelerometerData.acceleration, som er en struktur av typen CMAcceleration 
 struct CMAcceleration {var x., yy Double:: Double Var y: Double Var z: Double init () init (xx Double, zz : Double)} 
 Vi er bare interessert i x eiendom og vi bruker numerisk type konvertering kaste det for en CGFloat for vår accelerationX eiendom 
 
 Nå som vi har accelerationX eiendom sett, kan vi flytte. spilleren. Vi gjør dette i didSimulatePhysics metoden. Legg til følgende til bunnen av GameScene.swift 
 styre funk didSimulatePhysics () {player.physicsBody .velocity = CGVector (dx: accelerationX * 600, dy: 0)?}. 
 Invoke setupAccelerometer i didMoveToView (_: ), og du bør være i stand til å bevege spilleren med akselerometer. Det er bare ett problem. Spilleren kan flytte off-screen til begge sider, og det tar noen få sekunder å få ham tilbake. Vi kan fikse dette ved hjelp av fysikkmotoren og kollisjoner. Vi gjør dette i neste trinn 
 styre func didMoveToView (vis: SKView). {... SetupInvaders () setupPlayer () invokeInvaderFire () setupAccelerometer ()} 
 7. Begrense spillerens bevegelse 
 
 Som nevnt i forrige trinn, kan spilleren flytte off-screen. Dette er en enkel løsning å bruke Sprite Kits fysikkmotor. Først legger en ny CollisionCategory heter EdgeBody 
 struct CollisionCategories {statisk utleid Invader:. Uint32 = 0x1 < < 0 statiske la Player: uint32 = 0x1 < < En statisk let InvaderBullet: uint32 = 0x1 < < 2 statisk let PlayerBullet: uint32 = 0x1 < < 3 statiske la EdgeBody: uint32 = 0x1 < < 4} 
 Sett dette som spillerens collisionBitMask i sin init-metoden. 
 Overstyring init () {... self.physicsBody? .categoryBitMask = CollisionCategories.Player self.physicsBody? .contactTestBitMask = CollisionCategories.InvaderBullet | CollisionCategories.Invader self.physicsBody? .collisionBitMask = CollisionCategories.EdgeBody animere ()} 
 Til slutt lager vi en physicsBody på scenen selv. Legg til følgende til didMoveToView (vis: SKView). Metoden i GameScene.swift 
 styre func didMoveToView (vis: SKView) {self.physicsWorld.gravity = CGVectorMake (0, 0) self.physicsWorld.contactDelegate = selv selv. physicsBody = SKPhysicsBody? (edgeLoopFromRect: ramme) self.physicsBody .categoryBitMask = CollisionCategories.EdgeBody} 
 Vi initial en fysikk kroppen ved å påberope init (edgeLoopFromRect :), passerer i scenen ramme. Den initializer skaper en kant sløyfe fra scenens ramme. Det er viktig å merke seg at en kant ikke har noen volum eller masse, og er alltid behandles som om den dynamiske egenskap er lik usann. Kanter kan også bare kolliderer med volumbaserte fysikk organer, som vår spiller er. 
 
 Vi har også satt den categoryBitMask til CollisionCategories.EdgeBody. Hvis du tester programmet, kan du legge merke til at skipet ikke lenger kan bevege seg utenfor skjermen, men noen ganger er det roterer. Når en fysikk legeme kolliderer med en annen fysikk kroppen, er det mulig at dette resulterer i en rotasjon. Dette er standard oppførsel. For å bøte på dette, setter vi allowsRotation til false i Player.swift. 
 Styre init () {... self.physicsBody? .collisionBitMask = CollisionCategories.EdgeBody self.physicsBody? .allowsRotation = False animere ()} 
 8. Stjerne Feltet 
 
 Trinn 1: Opprette Star Feltet 
 
 Spillet har en bevegelig stjernefelt i bakgrunnen. Vi kan lage startfeltet ved hjelp av Sprite Kit partikkelmotor. 
 
 Opprett en ny fil og velge Resource fra iOS-delen. Velg SpriteKit Particle fil som mal, og klikk på Neste. For Particle mal velge regn og lagre det som Starfield. Klikk Opprett for å åpne filen i editoren. Å se alternativene, åpner SKNode Inspektør på høyre høyre. 
 
 
 
 I stedet for å gå gjennom alle som sitter her, som ville ta lang tid, ville det være bedre å lese dokumentasjonen for å lære om hver enkelt innstilling. Jeg vil ikke gå i detalj om innstillingene av startfeltet heller. Hvis du er interessert, åpne filen i Xcode og ta en titt på de innstillingene jeg brukte 
 
 Trinn 2:. Legge Star Feltet å kulissene 
 
 Legg til følgende didMoveToView . (_ :) i StartGameScene.swift 
 styre func didMoveToView (vis: SKView) {bakgrunnsfarge = SKColor.blackColor () la Star = SKEmitterNode (fileNamed: "Starfield") starField.position = CGPointMake (size.width /2 , size.height /2) starField.zPosition = -1000 addChild (Starfield)} 
 Vi bruker en SKEmitterNode å laste StarField.sks filen, angi sin posisjon og gi den en lav zPosition. Årsaken til den lave zPosition er å sørge for at det ikke hindrer brukeren i å trykke på startknappen. Partikkel systemet genererer hundrevis av partikler så ved å sette det veldig lav vi overvinne det problemet. Du bør også vite at du manuelt kan konfigurere alle partikkelegenskaper på en SKEmitterNode, selv om det er mye enklere å bruke editor for å lage en .sks fil og laste den under kjøring. 
 
 Nå kan du legge den stjerners felt til GameScene.swift og LevelCompleteScene.swift. Koden er nøyaktig det samme som ovenfor. 
 
 9. Implementerer PulsatingText Class 
 
 Trinn 1: Lag PulsatingText Class 
 
 StartGameScene og LevelCompleteScene ha tekst som vokser og krymper gjentatte ganger. Vi vil underklasse SKLabeNode og bruke et par SKAction tilfeller å oppnå denne effekten. 
 
Opprett en ny Cocoa Touch Class som er en underklasse av SKLabelNode, name it PulsatingText, og legge til følgende kode i den. Anmeldelser import UIKitimport SpriteKitclass PulsatingText: SKLabelNode {func setTextFontSizeAndPulsate (theText: String, theFontSize: CGFloat) {self.text = theText; self.fontSize = theFontSize la scaleSequence = SKAction.sequence ([SKAction.scaleTo (2, varighet: 1), SKAction.scaleTo (1,0, varighet: 1)]) la scaleForever = SKAction.repeatActionForever (scaleSequence) self.runAction (scaleForever )}}
 En av de første tingene du kanskje har lagt merke til er at det er ingen initializer. Hvis underklasse ikke definerer en utpekt initializer, automatisk arver det alle sine super utpekt initializers 
 
 Vi har en metode setTextFontSizeAndPulsate (theText. TheFontSize :), som gjør akkurat det den sier. Det setter SKLabelNode tekst og fontegenskaper, og skaper en rekke SKAction tilfeller å gjøre teksten skalere opp og deretter ned igjen, og skaper en pulserende effekt 
 
 Trinn 2:. Legg PulsatingText til StartGameScene 
 
 Legg til følgende kode i StartGameScene.swift i didMoveToView (_ :) 
 styre func didMoveToView (vis. SKView) {bakgrunnsfarge = SKColor.blackColor () la invaderText = PulsatingText (fontNamed: "ChalkDuster") invaderText .setTextFontSizeAndPulsate ("Invaderz", theFontSize: 50) invaderText.position = CGPointMake (size.width /2, size.height /2 + 200) addChild (invaderText)} 
 Vi initial en PulsatingText eksempel invaderText, og påberope setTextFontSizeAndPulsate (theText: theFontSize :) på den. Vi satt sin posisjon og legge den til scenen 
 
 Trinn 3:. Legg PulsatingText til LevelCompleteScene 
 
 Legg til følgende kode i LevelCompleteScene.swift i didMoveToView (_:). 
 overstyring func didMoveToView (vis: SKView) {self.backgroundColor = SKColor.blackColor () la invaderText = PulsatingText (fontNamed: "ChalkDuster") invaderText.setTextFontSizeAndPulsate ("LEVEL COMPLETE", theFontSize: 50) invaderText.position = CGPointMake ( size.width /2, 
 Dette er size.height /2 + 200) addChild (invaderText)} nøyaktig det samme som det foregående trinnet. Bare teksten vi passerer i er annerledes. 
 
 10. Tar Spill Ytterligere 
 
 Dette fullfører spillet. Jeg har noen forslag til hvordan du kan utvide på spillet. Inne i bilder mappen, der et tre forskjellige invader bilder. Når du legger inntrengerne til åstedet, tilfeldig velge en av disse tre bildene. Du må oppdatere inntrenger er initializer å akseptere et bilde som en parameter. Se i Bullet klasse for et hint. 
 
 Det er også en UFO image. Prøv å gjøre dette dukker opp og beveger seg over skjermen hver femten sekunder eller så. Hvis spilleren treffer den, gi dem et ekstra liv. Det kan være lurt å begrense antall liv de kan ha hvis du gjør dette. Til slutt, prøv å lage en HUD for spillerne bor. 
 
 Dette er bare noen forslag. Prøv og gjør spillet til din egen. 
 
 Konklusjon 
 
Dette bringer denne serien til en avslutning. Du bør ha et spill som ligner den opprinnelige Space Invaders spillet. Jeg håper du fant denne opplæringen nyttig og har lært noe nytt. Takk for lesing.

