SpriteKit之第一個(gè)項(xiàng)目

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ì)覺得莫名其妙的。

屏幕快照 2017-01-24 上午11.40.42.png

2.項(xiàng)目修改

創(chuàng)建好后,項(xiàng)目需要做一些修改:
1.在ViewController.swift里,添加:

import SpriteKit

2.在Main.storyboard里,修改當(dāng)前VC的View為SKView。如下:

屏幕快照 2017-01-24 上午11.46.07.png

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)行程序,就可以看到初步的效果了。如下:

屏幕快照 2017-01-24 上午11.52.46.png

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é)果了。

屏幕快照 2017-01-24 下午2.11.45.png

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)行,如圖:

2017-01-24 15_05_19.gif

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)行,最終效果如圖:

2017-01-24 15_28_50.gif

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)行,如圖:

2017-01-24 16_01_42.gif

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)行如圖:

2017-01-24 16_48_44.gif

結(jié)語(yǔ)

這是使用SpriteKit的第一步,知道了很多基礎(chǔ)的東西,比如畫面是如何呈現(xiàn),界面的層級(jí)關(guān)系,如何跳轉(zhuǎn)等等。這只是一個(gè)開始,后面還有很多需要學(xué)的。

本文代碼可以在我的github上面找到(Game01):https://github.com/flywo/SwiftGame

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 寫在前面: 游戲開發(fā)菜鳥,本帥哥也是第一次研究SpriteKit,有很多都不懂,另外本文轉(zhuǎn)自王巍老師的博客點(diǎn)擊進(jìn)入...
    CoderZNB閱讀 4,370評(píng)論 4 19
  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理,服務(wù)發(fā)現(xiàn),斷路器,智...
    卡卡羅2017閱讀 136,711評(píng)論 19 139
  • 由于很多小伙伴要demo我就不一一發(fā)了,直接丟在github上自己下載吧:https://github.com/s...
    FKSky閱讀 24,006評(píng)論 27 99
  • 十月一號(hào)做的沙盤實(shí)驗(yàn) 這是四個(gè)跳舞的女孩子 這一周討論了一個(gè)讓我感觸很深的話題——優(yōu)秀 老師問(wèn)我,覺得這四個(gè)女孩子...
    小貓咪不吃大鯉魚閱讀 328評(píng)論 0 0
  • 豆乳布丁,這是一個(gè)既熟悉又新鮮的東西。熟悉是因?yàn)樗且粋€(gè)布丁,不熟悉是因?yàn)樗嵌谷橹谱鞯模皇莻鹘y(tǒng)的奶油或者乳酪...
    Caphintty圖圖閱讀 248評(píng)論 0 1

友情鏈接更多精彩內(nèi)容