1 名詞概念
SpriteKit:提供一個(gè)圖形渲染和動(dòng)畫的基礎(chǔ),可以有效的利用圖形硬件來(lái)渲染動(dòng)畫的幀,而無(wú)須開發(fā)人員編寫繪圖指令,可以專注的解決更高層次的設(shè)計(jì)問(wèn)題。
Sprite:精靈,在游戲里的背景,人物,物品等都是精靈。
SKView:動(dòng)畫和渲染由SKView執(zhí)行,需要在一個(gè)窗口中放置該視圖,然后渲染內(nèi)容。
Scenes:場(chǎng)景,游戲中的內(nèi)容會(huì)被組織成場(chǎng)景,由SKScene對(duì)象表示。包含了精靈和其它需要渲染的內(nèi)容。一個(gè)游戲,可能需要?jiǎng)?chuàng)建一個(gè)或多個(gè)SKScene類或其子類。
SKNode:節(jié)點(diǎn),實(shí)際上SKScene是SKNode的子類,場(chǎng)景對(duì)象是一個(gè)節(jié)點(diǎn)對(duì)象的根節(jié)點(diǎn),決定子類哪個(gè)內(nèi)容被繪制以及渲染。所有節(jié)點(diǎn)對(duì)象都是響應(yīng)者,可以繼承任何節(jié)點(diǎn)來(lái)創(chuàng)建接收用戶輸入的新類。
紋理:用來(lái)渲染精靈的共享圖像,比如需要多個(gè)相同圖像時(shí),使用紋理來(lái)直接創(chuàng)建,而無(wú)需再次重新讀取圖形文件。
actions:動(dòng)作,能夠讓場(chǎng)景內(nèi)容動(dòng)起來(lái),每一個(gè)動(dòng)作都是一個(gè)對(duì)象,由SKAction類表示。
2 開始
知道上方概念和大概作用后,我們開始寫第一個(gè)項(xiàng)目,使用的是Xcode8.0和Swift3.0。
1.創(chuàng)建一個(gè)空項(xiàng)目
如下圖,創(chuàng)建一個(gè)Single View Application,然后創(chuàng)建一個(gè)名詞為Game的項(xiàng)目:
可能有人會(huì)問(wèn),為什么不選Game?我的回答是,沒(méi)有寫過(guò)游戲的朋友最好別創(chuàng)建Game模板,因?yàn)闀?huì)自動(dòng)給你添加很多東西很多方法,第一次看會(huì)覺得莫名其妙的。

2.項(xiàng)目修改
創(chuàng)建好后,項(xiàng)目需要做一些修改:
1.在ViewController.swift里,添加:
import SpriteKit
2.在Main.storyboard里,修改當(dāng)前VC的View為SKView。如下:

3.修改ViewController.swift如下:
import UIKit
import SpriteKit
class ViewController: UIViewController {
var spriteView: SKView!
override func viewDidLoad() {
super.viewDidLoad()
spriteView = view as! SKView
spriteView.showsDrawCount = true//使用多少繪畫,越少越好
spriteView.showsNodeCount = true//節(jié)點(diǎn)個(gè)數(shù)
spriteView.showsFPS = true//FPS開啟
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
}
此時(shí),運(yùn)行程序,就可以看到初步的效果了。如下:

3.編寫場(chǎng)景代碼
1.創(chuàng)建一個(gè)場(chǎng)景類,HelloScene繼承自SKScene。
2.HelloScene內(nèi)導(dǎo)入SpriteKit
3.添加如下代碼:
import UIKit
import SpriteKit
class HelloScene: SKScene {
var contentCreated = false//標(biāo)記場(chǎng)景是否已經(jīng)創(chuàng)建
override func didMove(to view: SKView) {//每當(dāng)場(chǎng)景要被呈現(xiàn)時(shí),會(huì)調(diào)用該方法,并且只在第一次調(diào)用
if !contentCreated {
createSceneContents()
contentCreated = true
}
}
func createSceneContents() {//自定義的創(chuàng)建場(chǎng)景內(nèi)容的方法
backgroundColor = SKColor.blue//背景,SKColor不是一個(gè)類,是一個(gè)宏,iOS上是UIColor,OS X上是NSColor。
scaleMode = .aspectFit//縮放模式
addChild(newHelloNode())
}
func newHelloNode() -> SKLabelNode {//創(chuàng)建一個(gè)label
let helloNode = SKLabelNode(fontNamed: "Chalkduster")
helloNode.text = "你好!"
helloNode.fontSize = 42
helloNode.position = CGPoint(x: frame.width/2, y: frame.height/2)
return helloNode
}
}
4.回到ViewController.swift中,添加present代碼,呈現(xiàn)場(chǎng)景視圖:
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
let hello = HelloScene(size: CGSize(width: view.frame.width, height: view.frame.height))
spriteView.presentScene(hello)
}
運(yùn)行,就可以看到結(jié)果了。

3 使用Action動(dòng)作
上面做出了一個(gè)靜態(tài)的界面,我們可以通過(guò)Action動(dòng)作將場(chǎng)景內(nèi)的東西具有動(dòng)作。我們創(chuàng)建一個(gè)action對(duì)象來(lái)描述想要的改變,然后告訴一個(gè)節(jié)點(diǎn)運(yùn)行,當(dāng)場(chǎng)景渲染時(shí),動(dòng)作會(huì)被執(zhí)行。
給項(xiàng)目添加一個(gè)功能,當(dāng)點(diǎn)擊場(chǎng)景時(shí),文字淡出,背景顏色變化。
在HelloScene場(chǎng)景類的newHelloNode()方法中,給節(jié)點(diǎn)添加名稱,方便使用時(shí)查找:
helloNode.name = "LabelNode"
然后重寫touchesBegan方法:
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
let helloNode = childNode(withName: "LabelNode")
if let node = helloNode {
node.name = ""http://防止按壓事件重復(fù)觸發(fā),HelloNode被重復(fù)動(dòng)作
let moveUp = SKAction.moveBy(x: 0, y: 100, duration: 0.5)//上移
let zoom = SKAction.scale(to: 2.0, duration: 0.25)//放大
let pause = SKAction.wait(forDuration: 0.5)//暫停
let fade = SKAction.fadeIn(withDuration: 0.25)//消失
let remove = SKAction.removeFromParent()//移除
let colorChange = SKAction.colorize(with: .orange, colorBlendFactor: 1, duration: 0.5)//改變背景色
let moveSequence = SKAction.sequence([moveUp, zoom, pause, fade, remove])
//動(dòng)作執(zhí)行
node.run(moveSequence)
self.run(colorChange)
}
}
由于所有節(jié)點(diǎn)都是UIResponder的子類,所以可以直接使用touchBegan來(lái)添加交互內(nèi)容。
添加好后,運(yùn)行,如圖:

4 場(chǎng)景切換
場(chǎng)景之間的切換使用SpriteKit會(huì)非常容易。
1.創(chuàng)建新的ship場(chǎng)景繼承于SKScene類,添加如下代碼:
import UIKit
import SpriteKit
class ShipScene: SKScene {
var contentCreated = false
override func didMove(to view: SKView) {
if !contentCreated {
createScreated()
contentCreated = true
}
}
func createScreated() {
backgroundColor = .black
scaleMode = .aspectFit
}
}
2.在HelloScene類中,修改touchesBegan方法中,node.run方法,如下:
node.run(moveSequence) {
let ship = ShipScene(size: self.size)
let transition = SKTransition.doorsOpenVertical(withDuration: 2)//動(dòng)畫類型
self.view?.presentScene(ship, transition: transition)//呈現(xiàn)ship場(chǎng)景后,hello場(chǎng)景會(huì)被丟棄
}
上面代碼的作用是,當(dāng)action執(zhí)行完成后,切換場(chǎng)景。
運(yùn)行,最終效果如圖:

5 構(gòu)造復(fù)雜的內(nèi)容
新的場(chǎng)景沒(méi)有任何內(nèi)容,我可以使用很多個(gè)SKSpriteNode對(duì)象,創(chuàng)建出很復(fù)雜的內(nèi)容。
我們創(chuàng)建出一個(gè)模擬的飛船,有兩道亮光。
在ShipScene類的createScreated方法中添加飛船,代碼如下:
func createScreated() {
backgroundColor = .black
scaleMode = .aspectFit
let ship = newShip()
ship.position = CGPoint(x: frame.width/2, y: frame.height/2)
addChild(ship)
}
func newShip() -> SKSpriteNode {
//添加飛船
let node = SKSpriteNode(color: .green, size: CGSize(width: 100, height: 50))
let hover = SKAction.sequence([SKAction.wait(forDuration: 1),
SKAction.moveBy(x: 100, y: 50, duration: 1),
SKAction.wait(forDuration: 1),
SKAction.moveBy(x: -100, y: 150, duration: 1)])
node.run(hover)
//飛船添加亮光,亮光添加到飛船上后,飛船移動(dòng)后,亮光也會(huì)一起移動(dòng),旋轉(zhuǎn)等也是。
let lingt1 = newLight()
lingt1.position = CGPoint(x: 0, y: 30)
node.addChild(lingt1)
let lingt2 = newLight()
lingt2.position = CGPoint(x: 0, y: -30)
node.addChild(lingt2)
return node
}
func newLight() -> SKSpriteNode {
let lingt = SKSpriteNode(color: .white, size: CGSize(width: 50, height: 2))
let blink = SKAction.sequence([SKAction.fadeOut(withDuration: 0.25),
SKAction.fadeIn(withDuration: 0.25)])
let blinkForever = SKAction.repeatForever(blink)//一直重復(fù)動(dòng)作
lingt.run(blinkForever)
return lingt
}
添加好后,運(yùn)行,如圖:

6 添加節(jié)點(diǎn)之間的交互
通常,游戲開始后,節(jié)點(diǎn)互相之間是可以進(jìn)行交互的,SpriteKit提供了一個(gè)完整的物理模擬,可以添加行為到節(jié)點(diǎn),我們?cè)陲w船上方添加掉落的石塊來(lái)演示。
1.給飛船添加物理體
//給飛船添加物理體
node.physicsBody = SKPhysicsBody(rectangleOf: node.size)
node.physicsBody?.isDynamic = false//防止非常受物理交互影響,開啟后,飛船的速度不會(huì)受物理碰撞影響
2.當(dāng)前場(chǎng)景添加產(chǎn)生巖石的action
//產(chǎn)生巖石
let makeRocks = SKAction.sequence([SKAction.perform(#selector(addRock), onTarget: self),
SKAction.wait(forDuration: 0.1, withRange: 0.15)])
run(SKAction.repeatForever(makeRocks))
3.實(shí)現(xiàn)addRock方法:
//添加巖石
func addRock() {
let rock = SKSpriteNode(color: .white, size: CGSize(width: 10, height: 10))
rock.position = CGPoint(x: frame.width*2/3, y: frame.height)
rock.name = "rock"
rock.physicsBody = SKPhysicsBody(rectangleOf: rock.size)
rock.physicsBody?.usesPreciseCollisionDetection = true
addChild(rock)
}
//巖石離開屏幕后,移除
override func didSimulatePhysics() {
enumerateChildNodes(withName: "rock") { (node, _) in
if node.position.y < 0 {
node.removeFromParent()
}
}
}
添加完畢后,運(yùn)行如圖:

結(jié)語(yǔ)
這是使用SpriteKit的第一步,知道了很多基礎(chǔ)的東西,比如畫面是如何呈現(xiàn),界面的層級(jí)關(guān)系,如何跳轉(zhuǎn)等等。這只是一個(gè)開始,后面還有很多需要學(xué)的。
本文代碼可以在我的github上面找到(Game01):https://github.com/flywo/SwiftGame