In deze post bouwen we een eenvoudig spel helemaal opnieuw. Onderweg komen we enkele van de belangrijkste aspecten van de SpriteKit-bibliotheek tegen.
Dit bericht bouwt voort op wat we eerder in de SpriteKit Basics-serie hebben geleerd. Als je je kennis van SpriteKit wilt opfrissen, bekijk dan eens een paar van mijn andere berichten.
Open Xcode en start een nieuw project vanuit het menu het dossier > nieuwe > project. Zorg ervoor dat iOS is geselecteerd en kies Spel als uw sjabloon.
Geef je project een naam en zorg ervoor dat dat zo is Taal ingesteld op Snel, Game-technologie ingesteld op SpriteKit, en apparaten ingesteld op iPad.
Een van de eerste dingen die ik graag doe, is bepalen hoeveel scènes ik nodig heb voor het project. Ik zal meestal ten minste drie scènes hebben: een introscène, een hoofdscènescène en een scène om hoge scores te tonen, enz.
Voor dit voorbeeld hebben we alleen een intro- en hoofdscènespel nodig, omdat we geen levens, scores enz. Bijhouden. SpriteKit wordt al bij één scène geleverd wanneer je een nieuw project maakt, dus we hebben alleen een intro-scène nodig.
Kies in het menu van Xcode het dossier > nieuwe > het dossier. Zorg ervoor dat iOS is geselecteerd en kies Cocoa Touch Class.
Geef de klas een naam StartGameScene, en zorg ervoor dat Subklasse van ingesteld op SKScene en Taal ingesteld op Snel.
Open GameViewController.swift. Verwijder alles in dat bestand en vervang het door het volgende.
import UIKit import SpriteKit import GameplayKit class GameViewController: UIViewController override func viewDidLoad () super.viewDidLoad () laat scène = StartGameScene (size: view.bounds.size) laat skView = self.view as! SKView skView.showsFPS = false skView.showsNodeCount = false skView.ignoresSiblingOrder = false scene.scaleMode = .aspectFill skView.presentScene (scène) overschrijven var preferersStatusBarHidden: Bool return true
Wanneer u een nieuw project aanmaakt, GameViewController.swift is ingesteld om te laden GameScene.sks van schijf. GameScene.sks wordt gebruikt in combinatie met de ingebouwde scèneditor van SpriteKit, waarmee u uw projecten visueel kunt indelen. We zullen niet gebruiken GameScene.sks, en zal in plaats daarvan alles maken van code, dus hier initiëren we een nieuw exemplaar van StartGameScene en presenteer het.
Voeg het volgende toe aan het nieuw gecreëerde StartGameScene.swift.
import UIKit import SpriteKit-klasse StartGameScene: SKScene override func didMove (om te bekijken: SKView) scene? .backgroundColor =. blue let logo = SKSpriteNode (imageNamed: "bigplane") logo.position = CGPoint (x: size.width / 2 , y: size.height / 2) addChild (logo) laat newGameBtn = SKSpriteNode (imageNamed: "newgamebutton") newGameBtn.position = CGPoint (x: size.width / 2, y: size.height / 2 - 350) newGameBtn. name = "newgame" addChild (newGameBtn) override func raaktBegan (_ raakt: instellen, met evenement: UIEvent?) guard let touch = touches.first else return let touchLocation = touch.location (in: self) let touchedNode = self.atPoint (touchLocation) if (touchedNode.name == "newgame") let newScene = GameScene (size: size) newScene.scaleMode = scaleMode view? .presentScene (newScene)
Deze scène is vrij eenvoudig. In de didMove
methode, we voegen een logo en een knop toe. Dan in touchesBegan
, we detecteren aanrakingen op de nieuwe spelknop en reageren door de hoofdscène te laden GameScene
.
Het volgende dat ik leuk vind om te doen bij het maken van een nieuw spel, is beslissen welke klassen ik nodig zal hebben. Ik kan meteen zien dat ik een a nodig heb Speler
klasse en een Vijand
klasse. Beide klassen zullen uitbreiden SKSpriteNode
. Ik denk dat we voor dit project alleen de kogels van spelers en vijanden binnen hun respectievelijke klassen zullen maken. Je kunt afzonderlijke bullet-klassen en bullet-bullet klassen maken als je dat wilt, en ik raad je aan dat te proberen als een oefening in je eentje.
Ten slotte zijn er de eilanden. Deze hebben geen specifieke functionaliteit maar bewegen over het scherm. In dit geval, omdat het gewoon decoraties zijn, denk ik dat het ook goed is om geen klasse te maken en in plaats daarvan gewoon in de hoofdtekst te maken GameScene
.
Speler
KlasseKies in het menu van Xcode het dossier > nieuwe > het dossier. Zorg ervoor dat iOS is geselecteerd en kies Cocoa Touch Class.
Zeker weten dat Klasse ingesteld op Speler, Subklasse van: ingesteld op SKSpriteNode, en Taal ingesteld op Snel.
Voeg nu het volgende toe aan Player.swift.
import UIKit import SpriteKit class Speler: SKSpriteNode private var canFire = true private var onoverwinnelijk = false private var lives: 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]))
Binnen de in het()
methode, we zetten de physicsBody
en aanroepen generateBullets ()
. De generateBullets
methode herhaaldelijk oproepen fireBullet ()
, die een kogel creëert, zet die op physicsBody
, en beweegt deze over het scherm.
Wanneer de speler een leven verliest, de respawnen ()
methode wordt aangeroepen. Binnen de respawn
methode, we vervagen het vliegtuig vijf keer in en uit, gedurende welke tijd de speler onoverwinnelijk zal zijn. Een van de spelers heeft alle levens uitgeput, de doden()
methode wordt aangeroepen. De kill-methode laadt gewoon de StartGameScene
.
Kiezen het dossier > nieuwe > het dossier uit het Xcode-menu. Zorg ervoor dat iOS is geselecteerd en kies Cocoa Touch Class.
Zeker weten dat Klasse ingesteld op Vijand, Subklasse van: ingesteld op SKSpriteNode, en Taal ingesteld op Snel.
Voeg het volgende toe aan Enemy.swift.
import UIKit import SpriteKit class Enemy: SKSpriteNode init () let texture = SKTexture (imageNamed: "enemy1") super.init (texture: texture, colour: .clear, size: texture.size ()) self.name = " vijand "self.physicsBody = SKPhysicsBody (texture: self.texture !, size: self.size) self.physicsBody? .isDynamic = true self.physicsBody? .categoryBitMask = PhysicsCategories.Enemy self.physicsBody? .contactTestBitMask = PhysicsCategories.Player | PhysicsCategories.PlayerBullet self.physicsBody? .AllowsRotation = false move () generateBullets () required 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 (rectangleOf: bullet.size) bullet.physicsBody ?. categoryBitMask = PhysicsCategories.EnemyBullet bullet.physicsBody? .allowsRotation = false scene? .addChild (bullet) laat moveBulletAction = SKAction.move (to: CGPoint (x: self.position.x, y: 0 - bullet.size.height), duur: 2.0) laat removeBulletAction = SKAction.removeFromParent () bullet.run (SKAction.sequence ([moveBulletAction, removeBulletAction])) func move () let moveEnemyAction = SKAction.moveTo (y: 0 - self.size.height, duur: 12.0) laat removeEnemyAction = SKAction.removeFromParent () let moveEnemySequence = SKAction.sequence ([moveEnemyAction, removeEnemyAction]) run (moveEn emySequence) func generateBullets () let fireBulletAction = SKAction.run [weak self] in self? .fireBullet () let waitToFire = SKAction.wait (forDuration: 1.5) laat fireBulletSequence = SKAction.sequence ([fireBulletAction, waitToFire] ) laat vuur = SKAction.repeatForever (fireBulletSequence) uitvoeren (brand)
Deze klasse lijkt veel op de Speler
klasse. We hebben zijn physicsBody
en aanroepen generateBullets ()
. De bewegen ()
verplaatst gewoon de vijand over het scherm.
Alles binnen verwijderen GameScene.swift en voeg het volgende toe.
import importeren SpriteKit importeren GameplayKit importeren CoreMotion-klasse GameScene: SKScene, SKPhysicsContactDelegate let player = Player () laat motionManager = CMMotionManager () var acceleratieX: CGFloat = 0.0 override func didMove (om te bekijken: SKView) physicsWorld.gravity = CGVector (dx: 0.0 , dy: 0.0) self.physicsWorld.contactDelegate = self scene? .backgroundColor =. blue physicsBody = SKPhysicsBody (edgeLoopFrom: frame) physicsBody? .categoryBitMask = PhysicsCategories.EdgeBody player.position = CGPoint (x: size.width / 2, y : player.size.height) addChild (player) setupAccelerometer () addEnemies () generateIslands () override func touchesBegan (_ touches: Set, with event: UIEvent?) func addEnemies () let generateEnemyAction = SKAction.run [weak self] in self? .generateEnemy () let waitToGenerateEnemy = SKAction.wait (forDuration: 3.0) laat generateEnemySequence = SKAction.sequence ( [generateEnemyAction, waitToGenerateEnemy]) run (SKAction.repeatForever (generateEnemySequence)) func generateEnemy () let enemy = Enemy () addChild (vijand) vijand.position = CGPoint (x: CGFloat (arc4random_uniform (UInt32 (size.width - enemy .size.width))), y: size.height - enemy.size.height) func didBegin (_ contact: 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)
We maken een instantie van Speler
en een instantie van CMMotionManager
. We gebruiken de versnellingsmeter om de speler in dit spel te verplaatsen.
Binnen de didMove (naar :)
methode zetten we de zwaartekracht uit, zetten de contactDelegate
, voeg een randlus toe en stel de speler
positie voordat deze aan de scène wordt toegevoegd. We roepen vervolgens aan setupAccelerometer ()
, die de accelerometer instelt en de addEnemies ()
en generateIslands ()
methoden.
De addEnemies ()
methode herhaalt herhaaldelijk de generateEnemy ()
methode, die een instantie van maakt Vijand
en voeg het toe aan de scène.
De generateIslands ()
methode werkt op dezelfde manier als de addEnemies ()
methode omdat het herhaaldelijk roept createIsland ()
welke een creëert SKSpriteNode
en voegt het toe aan de scène. Binnen createIsland ()
, we maken ook een SKAction
die het eiland van het toneel beweegt.
Binnen de didBegin (_ :)
methode controleren we om te zien welke knooppunten contact maken en reageren door het juiste knooppunt uit de scène te verwijderen en aan te roepen player.die ()
indien nodig. De createExplosion ()
methode maakt een explosie-animatie en voegt deze toe aan de scène. Zodra de explosie is voltooid, wordt deze verwijderd van de scène.
Tijdens deze serie leerden we enkele van de belangrijkste concepten die in bijna alle SpriteKit-spellen worden gebruikt. We hebben de serie beëindigd door te laten zien hoe eenvoudig het is om een eenvoudig spel te krijgen. Er zijn nog steeds enkele verbeteringen mogelijk, zoals een HUB, hoge scores en geluiden (ik heb een aantal MP3's meegeleverd die je hiervoor in de repo kunt gebruiken). Ik hoop dat je in deze serie iets nuttigs hebt geleerd, en bedankt voor het lezen!
.