Lag Space Invaders med Swift og Sprite Kit: Ferdigstilling Gameplay

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.



Previous:
Next Page: