SpriteKit Basics Att lägga allt ihop

Vad du ska skapa

I det här inlägget bygger vi ett enkelt spel från början. Längs vägen kommer vi att beröra några av de viktigaste aspekterna av SpriteKit-biblioteket.

Det här inlägget bygger på vad vi har lärt oss tidigare i serien SpriteKit Basics. Om du vill uppdatera din SpriteKit-kunskap, ta en titt på några av mina andra inlägg.

Nytt projekt

Öppna Xcode och starta ett nytt projekt från menyn Fil > Ny Projekt. Se till iOS är markerat och välj Spel som din mall.

Ge ditt projekt ett namn och se till att Språk är satt till Snabb, Spelteknik är satt till SpriteKit, och enheter är satt till iPad.

Planerar spel scenerna

En av de första sakerna jag tycker om att skapa ett projekt är att bestämma hur många scener jag behöver för projektet. Jag brukar ha minst tre scener: en introscene, en huvudspelscene och en scen för att visa höga poäng osv.

För det här exemplet behöver vi bara en intro och huvudspelplats eftersom vi inte kommer att hålla reda på liv, poäng etc. SpriteKit kommer redan med en scen när du skapar ett nytt projekt, så vi behöver bara en introscen.

Från Xcode-menyn, välj Fil > Ny > Fil. Se till iOS är markerat och välj Kakao Touch Class.

Namnge klassen StartGameScene, och se till att Underklass av är satt till SKScene och Språk är satt till Snabb.

Ställa in GameViewController

Öppna GameViewController.swift. Ta bort allt i den filen och ersätt den med följande.

importera UIKit import SpriteKit import GameplayKit klass GameViewController: UIViewController överrra func viewDidLoad () super.viewDidLoad () låt scen = StartGameScene (storlek: view.bounds.size) låt skView = self.view as! SKView skView.showsFPS = false skView.showsNodeCount = false skView.ignoresSiblingOrder = false scene.scaleMode = .aspektFill skView.presentScene (scene) åsidosätta var prefersStatusBarHidden: Bool return true 

När du skapar ett nytt projekt, GameViewController.swift är inställd för att ladda GameScene.sks från disken. GameScene.sks används tillsammans med SpriteKits inbyggda scenredigerare, som låter dig visuellt lägga ut dina projekt. Vi kommer inte att använda GameScene.sks, och skapar istället allt från kod, så här initierar vi en ny instans av StartGameScene och presentera det.

Skapa Intro-scenen

Lägg till följande på den nyskapade StartGameScene.swift.

importera UIKit-import SpriteKit-klass StartGameScene: SKScene override func didMove (för att visa: SKView) scen? .backgroundColor = .blå låtlogo = SKSpriteNode (imageNamed: "bigplane") logo.position = CGPoint (x: size.width / 2 , y: size.height / 2) addChild (logotyp) låt newGameBtn = SKSpriteNode (imageNamed: "newgamebutton") newGameBtn.position = CGPoint (x: size.width / 2, y: size.height / 2 - 350) newGameBtn. name = "newgame" addChild (newGameBtn) åsidosätta func touchesBegan (_ berör: Set, med händelse: UIEvent?) vakt låt beröra = berör.första gången return låt touchLocation = touch.location (i: själv) låt touchedNode = self.atPoint (touchLocation) om (touchedNode.name == "newgame")  låt newScene = GameScene (storlek: storlek) newScene.scaleMode = scaleMode view? .presentScene (newScene) 

Denna scen är ganska enkel. I didMove metod, vi lägger till en logotyp och en knapp. Sedan i touchesBegan, vi upptäcker berör på den nya spelknappen och svarar genom att ladda huvudbilden GameScene.

Planering spel klasser

Nästa sak som jag gillar att göra när du skapar ett nytt spel är att bestämma vilka klasser jag behöver. Jag kan direkt berätta att jag behöver en Spelare klass och an Fiende klass. Båda dessa klasser kommer att förlängas SKSpriteNode. Jag tror för det här projektet kommer vi bara att skapa spelare och fiendens kulor direkt från sina respektive klasser. Du kan göra separata spelare kula och fiende kula klasser om du föredrar, och jag föreslår att du försöker göra det som en övning på egen hand. 

Slutligen finns det öar. Dessa har ingen specifik funktionalitet utan att flytta ner på skärmen. I det här fallet, eftersom de bara är dekorationer, tycker jag att det också är okej att inte skapa en klass och istället bara skapa dem i huvudet GameScene.

Skapa Spelare Klass

Från Xcode-menyn, välj Fil > Ny > Fil.  Se till iOS är markerat och välj Kakao Touch Class.

Se till att Klass är satt till Spelare, Underklass av: är satt till SKSpriteNode, och Språk är satt till Snabb.

Lägg till följande till Player.swift.

importera UIKit import SpriteKit klass Player: SKSpriteNode privat var canFire = true private var invincible = felaktigt privat var liv: Int = 3 didSet if (lives < 0) kill() else respawn()    init()  let texture = SKTexture(imageNamed: "player") super.init(texture: texture, color: .clear, size: texture.size()) self.physicsBody = SKPhysicsBody(texture: self.texture!,size:self.size) self.physicsBody?.isDynamic = true self.physicsBody?.categoryBitMask = PhysicsCategories.Player self.physicsBody?.contactTestBitMask = PhysicsCategories.Enemy | PhysicsCategories.EnemyBullet self.physicsBody?.collisionBitMask = PhysicsCategories.EdgeBody self.physicsBody?.allowsRotation = false generateBullets()  required init?(coder aDecoder: NSCoder)  super.init(coder: aDecoder)  func die () if(invincible == false) lives -= 1   func kill() let newScene = StartGameScene(size: self.scene!.size) newScene.scaleMode = self.scene!.scaleMode let doorsClose = SKTransition.doorsCloseVertical(withDuration: 2.0) self.scene!.view?.presentScene(newScene, transition: doorsClose)  func respawn() invincible = true let fadeOutAction = SKAction.fadeOut(withDuration: 0.4) let fadeInAction = SKAction.fadeIn(withDuration: 0.4) let fadeOutIn = SKAction.sequence([fadeOutAction,fadeInAction]) let fadeOutInAction = SKAction.repeat(fadeOutIn, count: 5) let setInvicibleFalse = SKAction.run  self.invincible = false  run(SKAction.sequence([fadeOutInAction,setInvicibleFalse]))  func generateBullets() let fireBulletAction = SKAction.run [weak self] in self?.fireBullet()  let waitToFire = SKAction.wait(forDuration: 0.8) let fireBulletSequence = SKAction.sequence([fireBulletAction,waitToFire]) let fire = SKAction.repeatForever(fireBulletSequence) run(fire)  func fireBullet() let bullet = SKSpriteNode(imageNamed: "bullet") bullet.position.x = self.position.x bullet.position.y = self.position.y + self.size.height/2 bullet.physicsBody = SKPhysicsBody(rectangleOf: bullet.size) bullet.physicsBody?.categoryBitMask = PhysicsCategories.PlayerBullet bullet.physicsBody?.allowsRotation = false scene?.addChild(bullet) let moveBulletAction = SKAction.move(to: CGPoint(x:self.position.x,y:(scene?.size.height)! + bullet.size.height), duration: 1.0) let removeBulletAction = SKAction.removeFromParent() bullet.run(SKAction.sequence([moveBulletAction,removeBulletAction]))  

Inom i det() metod, vi satte upp physicsBody och åberopa generateBullets (). De generateBullets metod upprepade gånger fireBullet (), som skapar en kula, sätter sin physicsBody, och flyttar den ner på skärmen.

När spelaren förlorar ett liv, återuppstå() Metoden åberopas. Inom återuppstå metod, vi bleknar planet in och ut fem gånger, under vilken tid spelaren blir oövervinnerlig. En spelare har uttömt alla liv, den döda() Metoden åberopas. Dödsmetoden laddar helt enkelt StartGameScene.

Skapa fiendens klass

Välja Fil > Ny > Fil från Xcode-menyn. Se till iOS är markerat och välj Kakao Touch Class.

Se till att Klass är satt till FiendeUnderklass av: är satt till SKSpriteNode, och Språk är satt till Snabb.

Lägg till följande till Enemy.swift.

importera UIKit-import SpriteKit-klass Enemy: SKSpriteNode init ) let texture = SKTexture (imageNamed: "enemy1") super.init (textur: textur, färg: .klart, storlek: texture.size ()) self.name = " fiende "self.physicsBody = SKPhysicsBody (textur: self.texture !, storlek: self.size) self.physicsBody? .isDynamic = true self.physicsBody? .categoryBitMask = PhysicsCategories.Enemy self.physicsBody? .contactTestBitMask = PhysicsCategories.Player | PhysicsCategories.PlayerBullet self.physicsBody? .AllowsRotation = false move () genereraBullets () krävs init? (Kodare aDecoder: NSCoder) super.init (kodare: aDecoder) func fireBullet () let bullet = SKSpriteNode (imageNamed: " bullet ") bullet.position.x = self.position.x bullet.position.y = self.position.y - bullet.size.height * 2 bullet.physicsBody = SKPhysicsBody (rektangelOf: bullet.size) bullet.physicsBody ?. categoryBitMask = PhysicsCategories.EnemyBullet bullet.physicsBody? .allowsRotation = falsk scen? .addChild (bullet) låt moveBulletAction = SKAction.move (till: CGPoint (x: self.position.x, y: 0 - bullet.size.height), duration: 2.0) låt removeBulletAction = SKAction.removeFromParent () bullet.run (SKAction.sequence ([moveBulletAction, removeBulletAction]) func move () låt moveEnemyAction = SKAction.moveTo (y: 0 - self.size.height, varaktighet: 12,0) låt removeEnemyAction = SKAction.removeFromParent () låt moveEnemySequence = SKAction.sequence ([moveEnemyAction, removeEnemyAction]) springa (moveEn emySequence) func generateBullets () let fireBulletAction = SKAction.run [svag själv] i själv? .fireBullet () låt waitToFire = SKAction.wait (forDuration: 1.5) släppa fireBulletSequence = SKAction.sequence ([fireBulletAction, waitToFire] ) släcka = SKAction.repeatForever (fireBulletSequence) kör (eld)

Denna klass är ganska lik den Spelare klass. Vi satte det physicsBody och åberopa generateBullets (). De flytta() Flytta helt enkelt fienden ner på skärmen.

Skapa huvudspelplatsen

Ta bort allt inom GameScene.swift och lägg till följande.

importera SpriteKit import GameplayKit import CoreMotion klass GameScene: SKScene, SKPhysicsContactDelegate let player = Player () låt motionManager = CMMotionManager () var accelerationX: CGFloat = 0.0 åsidosätta func didMove (för att visa: SKView) physicsWorld.gravity = CGVector (dx: 0.0 . dy : player.size.height) addChild (spelare) setupAccelerometer () addEnemies () generateIslands () åsidosätta func touchesBegan (_ berör: Set, med händelse: UIEvent?)  func addEnemies () let generateEnemyAction = SKAction.run [svag själv] i själv? .generateEnemy () låt waitToGenerateEnemy = SKAction.wait (forDuration: 3.0) låta generateEnemySequence = SKAction.sequence [generateEnemyAction, waitToGenerateEnemy]) springa (SKAction.repeatForever (generateEnemySequence) func generateEnemy () let enemy = Enemy () addChild (fiende) enemy.position = CGPoint (x: CGFloat (arc4random_uniform (UInt32 (size.width - fiende .size.width))), y: size.height - enemy.size.height) func didBegin (_ kontakt: SKPhysicsContact) var firstBody: SKPhysicsBody var secondBody: SKPhysicsBody if (contact.bodyA.categoryBitMask < contact.bodyB.categoryBitMask) firstBody = contact.bodyA secondBody = contact.bodyB else firstBody = contact.bodyB secondBody = contact.bodyA  if((firstBody.categoryBitMask & PhysicsCategories.Player != 0) && (secondBody.categoryBitMask & PhysicsCategories.Enemy != 0)) player.die() secondBody.node?.removeFromParent() createExplosion(position: player.position)  if((firstBody.categoryBitMask & PhysicsCategories.Player != 0) && (secondBody.categoryBitMask & PhysicsCategories.EnemyBullet != 0)) player.die() secondBody.node?.removeFromParent()  if((firstBody.categoryBitMask & PhysicsCategories.Enemy != 0) && (secondBody.categoryBitMask & PhysicsCategories.PlayerBullet != 0)) if(firstBody.node != nil) createExplosion(position: (firstBody.node?.position)!)  firstBody.node?.removeFromParent() secondBody.node?.removeFromParent()   func createExplosion(position: CGPoint) let explosion = SKSpriteNode(imageNamed: "explosion1") explosion.position = position addChild(explosion) var explosionTextures:[SKTexture] = [] for i in 1… 6  explosionTextures.append(SKTexture(imageNamed: "explosion\(i)"))  let explosionAnimation = SKAction.animate(with: explosionTextures, timePerFrame: 0.3) explosion.run(SKAction.sequence([explosionAnimation, SKAction.removeFromParent()]))  func createIsland()  let island = SKSpriteNode(imageNamed: "island1") island.position = CGPoint(x: CGFloat(arc4random_uniform(UInt32(size.width - island.size.width))), y: size.height - island.size.height - 50) island.zPosition = -1 addChild(island) let moveAction = SKAction.moveTo(y: 0 - island.size.height, duration: 15) island.run(SKAction.sequence([moveAction, SKAction.removeFromParent()]))  func generateIslands() let generateIslandAction = SKAction.run  [weak self] in self?.createIsland()  let waitToGenerateIslandAction = SKAction.wait(forDuration: 9) run(SKAction.repeatForever(SKAction.sequence([generateIslandAction, waitToGenerateIslandAction])))  func setupAccelerometer() motionManager.accelerometerUpdateInterval = 0.2 motionManager.startAccelerometerUpdates(to: OperationQueue(), withHandler:  accelerometerData, error in guard let accelerometerData = accelerometerData else  return  let acceleration = accelerometerData.acceleration self.accelerationX = CGFloat(acceleration.x) )  override func didSimulatePhysics()  player.physicsBody?.velocity = CGVector(dx: accelerationX * 600, dy: 0)   

Vi skapar en förekomst av Spelare och en förekomst av CMMotionManager. Vi använder accelerometern för att flytta spelaren i det här spelet.

Inom didMove (till :) metod vi stänger tyngdkraften, sätter upp contactDelegate, lägg till en kantslinga och ställ in spelares position innan den läggs till scenen. Vi åberopar sedan setupAccelerometer (), som ställer upp accelerometern och åberopar addEnemies () och generateIslands () metoder.

De addEnemies () metod kallar upprepade gånger generateEnemy () metod som kommer att skapa en förekomst av Fiende och lägg till den på scenen.

De generateIslands () Metoden fungerar på samma sätt som addEnemies () metod genom att det upprepade gånger kallas createIsland () vilket skapar en SKSpriteNode och lägger till den på scenen. Inom createIsland (), vi skapar också en SKAction som flyttar ön ner på scenen.

Inom didBegin (_ :) metod, vi kontrollerar för att se vilka noder som kontaktar och svarar genom att ta bort lämplig nod från scenen och åberopa player.die () om nödvändigt. De createExplosion () Metoden skapar en explosionsanimering och lägger den till scenen. När explosionen är klar tas den bort från scenen.

Slutsats

Under denna serie lärde vi oss några av de viktigaste begreppen som används i nästan alla SpriteKit-spel. Vi avslutade serien genom att visa hur enkelt det är att få ett grundläggande spel igång. Det finns fortfarande några förbättringar som kan göras, som en HUB, high scores och ljud (jag inkluderade ett par MP3-filer som du kan använda för detta i repo). Jag hoppas att du lärt dig något användbart i hela serien och tack för att du läste!

.