I dette innlegget vil vi bygge et enkelt spill fra bunnen av. Underveis berører vi noen av de viktigste aspektene av SpriteKit-biblioteket.
Dette innlegget bygger på det vi har lært tidligere i SpriteKit Basics-serien. Hvis du vil oppdatere SpriteKit-kunnskapen din, kan du se på noen av mine andre innlegg.
Åpne Xcode og start et nytt prosjekt fra menyen Fil > Ny > Prosjekt. Forsikre iOS er valgt og velg Spill som din mal.
Gi prosjektet et navn, og sørg for at Språk er satt til Fort, Spillteknologi er satt til SpriteKit, og enheter er satt til iPad.
En av de første tingene jeg liker å gjøre når jeg lager et prosjekt, er å bestemme hvor mange scener jeg trenger for prosjektet. Jeg vil vanligvis ha minst tre scener: en intro scene, en hovedspill scene og en scene for å vise høy score, etc..
For dette eksempelet trenger vi bare en intro og hovedspill scene siden vi ikke vil holde øye på liv, poeng etc. SpriteKit kommer allerede med en scene når du oppretter et nytt prosjekt, så vi trenger bare en intro scene.
Fra Xcode-menyen, velg Fil > Ny > Fil. Forsikre iOS er valgt, og velg Cocoa Touch Class.
Gi klassen navn StartGameScene, og sørg for at Underklasse av er satt til SKScene og Språk er satt til Fort.
Åpen GameViewController.swift. Slett alt i den filen og erstatt den med følgende.
importere UIKit import SpriteKit import GameplayKit klasse GameViewController: UIViewController override func viewDidLoad () super.viewDidLoad () la scene = StartGameScene (størrelse: view.bounds.size) la skView = self.view as! SKView skView.showsFPS = false skView.showsNodeCount = falskt skView.ignoresSiblingOrder = false scene.scaleMode = .aspektFill skView.presentScene (scene) overstyr var prefersStatusBarHidden: Bool return true
Når du oppretter et nytt prosjekt, GameViewController.swift er satt opp for å laste GameScene.sks fra disk. GameScene.sks brukes sammen med SpriteKits innebygde sceneditor, som lar deg visuelt legge ut prosjektene dine. Vi vil ikke bruke GameScene.sks, og vil i stedet skape alt fra kode, så her starter vi en ny forekomst av StartGameScene og presentere det.
Legg til følgende for den nylig opprettede StartGameScene.swift.
importere UIKit import SpriteKit klasse StartGameScene: SKScene override func didMove (for å se: SKView) scene? .backgroundColor = .blå letlogo = SKSpriteNode (imageNamed: "bigplane") logo.position = CGPoint (x: size.width / 2 , y: size.height / 2) addChild (logo) la newGameBtn = SKSpriteNode (imageNamed: "newgamebutton") newGameBtn.position = CGPoint (x: size.width / 2, y: size.height / 2 - 350) newGameBtn. name = "newgame" addChild (newGameBtn) overstyre func touchesBegan (_ berører: Set, med hendelsen: UIEvent?) vakt berør = berører.for det første return la touchLocation = touch.location (i: selv) la touchedNode = self.atPoint (touchLocation) hvis (touchedNode.name == "newgame") la newScene = GameScene (størrelse: størrelse) newScene.scaleMode = scaleMode view? .presentScene (newScene)
Denne scenen er ganske enkel. I didMove
metode, legger vi til en logo og en knapp. Så, i touchesBegan
, vi oppdager berøringer på den nye spillknappen og svarer ved å laste hovedspillet GameScene
.
Det neste jeg liker å gjøre når du lager et nytt spill, er å avgjøre hvilke klasser jeg trenger. Jeg kan fortell at jeg vil trenge en Spiller
klasse og an Fiende
klasse. Begge disse klassene vil strekke seg ut SKSpriteNode
. Jeg tror på dette prosjektet vil vi bare skape spiller og fiende kuler rett fra deres respektive klasser. Du kan lage separate kuler og fiendens kuletimer hvis du foretrekker det, og jeg foreslår at du prøver å gjøre det som en øvelse på egen hånd.
Til slutt er det øyene. Disse har ingen spesifikk funksjonalitet, men å bevege seg nedover skjermen. I dette tilfellet, siden de bare er dekorasjoner, tror jeg det er også greit å ikke lage en klasse, og i stedet bare lage dem i hovedsak GameScene
.
Spiller
KlasseFra Xcode-menyen, velg Fil > Ny > Fil. Forsikre iOS er valgt og velg Cocoa Touch Class.
Sørge for at Klasse er satt til Spiller, Underklasse av: er satt til SKSpriteNode, og Språk er satt til Fort.
Legg nå følgende til Player.swift.
importere UIKit import SpriteKit klasse Spiller: SKSpriteNode private var canFire = true private var invincible = feil 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]))
Innen i det()
metode, satte vi opp physicsBody
og påkalle generateBullets ()
. De generateBullets
metode kalles gjentatte ganger fireBullet ()
, som skaper en kule, setter sin physicsBody
, og beveger den ned på skjermen.
Når spilleren taper et liv, vil respawn ()
Metoden er påkalt. Innen respawn
metode, vi fade flyet inn og ut fem ganger, i hvilken tid spilleren vil være uovervinnelig. En spilleren har utmattet alle livene, den drepe()
Metoden er påkalt. Kill-metoden laster ganske enkelt StartGameScene
.
Velge Fil > Ny > Fil fra Xcode-menyen. Forsikre iOS er valgt og velg Cocoa Touch Class.
Sørge for at Klasse er satt til Fiende, Underklasse av: er satt til SKSpriteNode, og Språk er satt til Fort.
Legg til følgende til Enemy.swift.
import UIKit import SpriteKit klasse Enemy: SKSpriteNode init () let texture = SKTexture (imageNamed: "enemy1") super.init (tekstur: tekstur, farge: .clear, size: texture.size ()) self.name = " fiende "self.physicsBody = SKPhysicsBody (tekstur: self.texture !, størrelse: self.size) self.physicsBody? .isDynamic = true self.physicsBody? .categoryBitMask = PhysicsCategories.Enemy self.physicsBody? .contactTestBitMask = PhysicsCategories.Player | PhysicsCategories.PlayerBullet self.physicsBody? .AllowsRotation = false move () generereBullets () nødvendig init? (Coder aDecoder: NSCoder) super.init (coder: 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 scene? .addChild (bullet) la moveBulletAction = SKAction.move (til: CGPoint (x: self.position.x, y: 0 - bullet.size.height), varighet: 2,0) la removeBulletAction = SKAction.removeFromParent () bullet.run (SKAction.sequence ([moveBulletAction, removeBulletAction]) func move () la moveEnemyAction = SKAction.moveTo (y: 0 - self.size.height, varighet: 12,0) la removeEnemyAction = SKAction.removeFromParent () la moveEnemySequence = SKAction.sequence ([moveEnemyAction, removeEnemyAction]) kjøre (moveEn emySequence) func generateBullets () la fireBulletAction = SKAction.run self weak] i selv? .fireBullet () la waitToFire = SKAction.wait (forDuration: 1.5) la fireBulletSequence = SKAction.sequence ([fireBulletAction, waitToFire] ) la brann = SKAction.repeatForever (fireBulletSequence) kjøre (brann)
Denne klassen er ganske lik den Spiller
klasse. Vi satte det physicsBody
og påkalle generateBullets ()
. De bevege seg()
bare flytter fienden ned på skjermen.
Slett alt innenfor GameScene.swift og legg til følgende.
importere SpriteKit import GameplayKit import CoreMotion klasse GameScene: SKScene, SKPhysicsContactDelegate la spilleren = Spiller () la motionManager = CMMotionManager () var akselerasjonX: CGFloat = 0.0 override func didMove (for å se: SKView) physicsWorld.gravity = CGVector (dx: 0.0 . dy. : player.size.height) addChild (spiller) setupAccelerometer () addEnemies () generateIslands () tilsidesatte func touchesBegan (_ touches: Set, med hendelsen: UIEvent?) func addEnemies () la generateEnemyAction = SKAction.run [svakt selv] i selv? .generateEnemy () la waitToGenerateEnemy = SKAction.wait (forDuration: 3.0) la generateEnemySequence = SKAction.sequence [generateEnemyAction, waitToGenerateEnemy]) løp (SKAction.repeatForever (generateEnemySequence)) func generateEnemy () la fiende = Enemy () addChild (fiende) enemy.position = CGPoint (x: CGFloat (arc4random_uniform (UInt32 (size.width - fiende .size.width))), y: size.height - enemy.size.height) func gjordeBegin (_ 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 lager en forekomst av Spiller
og en forekomst av CMMotionManager
. Vi bruker akselerometeret til å flytte spilleren i dette spillet.
Innen didMove (i :)
Metode vi slår av tyngdekraften, setter opp contactDelegate
, legg til en kantsløyfe, og sett inn spiller
posisjon før du legger den til scenen. Vi påberoper oss da setupAccelerometer ()
, som setter opp akselerometeret, og påkaller addEnemies ()
og generateIslands ()
fremgangsmåter.
De addEnemies ()
metode kalles gjentatte ganger generateEnemy ()
metode, som vil skape en forekomst av Fiende
og legg det til scenen.
De generateIslands ()
Metoden fungerer på samme måte som addEnemies ()
metode ved at det gjentatte ganger kalles createIsland ()
som skaper en SKSpriteNode
og legger den til scenen. Innenfor createIsland ()
, vi lager også en SKAction
som beveger øya ned på scenen.
Innen didBegin (_ :)
Metode, vi sjekker for å se hvilke noder som kontakter og svarer ved å fjerne den aktuelle noden fra scenen og påberope seg player.die ()
hvis nødvendig. De createExplosion ()
Metoden skaper en eksplosjonsanimasjon og legger den til scenen. Når eksplosjonen er ferdig, blir den fjernet fra scenen.
I løpet av denne serien lærte vi noen av de viktigste konseptene som ble brukt i nesten alle SpriteKit-spill. Vi endte serien ved å vise hvor enkelt det er å få et grunnleggende spill i gang. Det er fortsatt noen forbedringer som kan gjøres, som en HUB, high scores og lyder (jeg har inkludert et par MP3-filer du kan bruke til dette i repo). Jeg håper du har lært noe nyttig gjennom hele denne serien, og takk for at du leser!
.