Swift 遊戲開發之「方塊彈珠」(二)

前言

在上一篇文章中,咱們已經瞭解了經過 UIKit 能夠模擬的物理場景,相對來講比較有限,但在完成一些簡單需求的時還能稍微應付一下。git

在這篇文章中,咱們將關注 SpriteKit 的初體驗,如何從零開始搭建出符合 SpriteKit 開發哲學的物理世界。程序員

初體驗

工程建立

經過 Xcode 建立新工程時,咱們不須要使用 Xcode 提供的默認 Game 模版,由於咱們的這個遊戲本質上是基於 app 的架構去實現的,底層驅動也是 Cocoa Touch 框架,只不過咱們須要經過 SpriteKit 中幾個特殊的場景類來承載具體的遊戲邏輯實現,所以,選擇 signle View 模版工程便可。github

(爲了偷懶,我仍是選擇的 Game 模版...編程

性能監控

在進行遊戲開發時,咱們最須要關心的就是「性能」自己,不少人認爲如今設備硬件條件已經很是好了,能夠不用太關注性能,但從我我的的角度出發,若是在出發某個場景你寫的邏輯渲染耗時 500ms,而我通過優化的邏輯只須要 100ms 便可完成渲染,這應該就是程序員的追求吧~swift

在咱們新建的項目中開啓性能監控很是簡單。任何基於 SpriteKit 的「物體」想要添加到其中的物理世界中,咱們須要一個「容器」去承載,而這個容器在 SpriteKit 中就是 SKView緩存

所以,咱們對遊戲的性能監控也回落到了對某個 SKView 的性能監控上,刪除 Game 模版中的多餘代碼後,整理以下:markdown

import UIKit
import SpriteKit

class GameViewController: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()
        
        if let view = self.view as! SKView? {
            
            let scene = GameScene(size: view.frame.size)
            scene.scaleMode = .aspectFill
            view.presentScene(scene)
            view.ignoresSiblingOrder = true
            view.showsFPS = true
            view.showsNodeCount = true
        
        }
    }

    override var prefersStatusBarHidden: Bool {
        return true
    }
}
複製代碼

此時個人 GameScene 裏調整以下,經過 SKShapeNode 建立了一個小球:架構

import SpriteKit
import GameplayKit

class GameScene: SKScene {
    
    override func didMove(to view: SKView) {
        let ball = SKShapeNode(circleOfRadius: 10)
        ball.fillColor = .red
        addChild(ball)
        ball.position = CGPoint(x: size.width / 2, y: 40)
    }
}

複製代碼

SKView or SKScene

大部分初學者會對 SpriteKit 中的這兩個類感到困惑,SKView 繼承自 UIView,是 UIView 的子類,而 SKScene 的最終父類是 SKNodeSKNodeUIView 是兩個徹底不一樣的類型。app

SKScene 可能會與咱們常規的思惟不太同樣,由於它不是繼承自 UIView,所以也就沒有所謂的 viewWillxxx 等方法,取而代之的 didMove 方法。咱們能夠在這個方法中做爲初始化場景的入口:框架

import SpriteKit
import GameplayKit

class GameScene: SKScene {
    var contentCreated = false
    
    override func didMove(to view: SKView) {
        if !contentCreated {
            createContent()
            contentCreated = true
        }
    }
    
    private func createContent() {
        let ball = SKShapeNode(circleOfRadius: 10)
        ball.fillColor = .red
        addChild(ball)
        ball.position = CGPoint(x: size.width / 2, y: 40)
    }
}
複製代碼

須要注意的是 SpriteKit 會自動劃分在具有使用 Metal 渲染引擎的設備上開啓 Metal 渲染,在不具有使用的設備上使用 OpenGL ES 進行渲染。SpriteKit 至關因而一個獨立與底層硬件的 framework,只須要提供渲染接口便可工做,也就是說,咱們不須要手動管理讓 SpriteKit 和哪個渲染引擎進行關聯,這一切都是全自動的。

在上面的代碼中,我使用了一個 contentCreated 變量在 didMove 方法中進行了標記,這是由於咱們的 BGPlayScene 有可能會屢次被重複添加到某個 SKView 上,底層的渲染引擎會自動協助咱們緩存已經被渲染過的內容,這樣能夠節省提升必定的性能。

就像剛纔我所說的同樣,SpriteKit 是一個獨立的 framework,在 iOS 和 macOS 平臺上也會自動抹掉平臺差別性,好比我對 BGPlayView 設置的背景顏色,我不須要區分當前工程運行的環境究竟是哪一個平臺,由於 SpriteKit 會幫助咱們自動將 .blue 根據工程運行的平臺轉換爲對應的 UIColor 或者 NSColor

在上文的代碼中,咱們已經經過 SKShapeNode 來建立出一個「精靈」,也就是咱們的後邊會用到的小球。

加入重力

若是咱們想要給一個 SKSpriteNode 具備物理特性,須要建立一個 SKPhysicsBody 對象,而後賦給節點的 physicsBody 屬性。

class GameScene: SKScene {
    
    // ...

    private func createContent() {
        let ball = SKShapeNode(circleOfRadius: 10)
        ball.fillColor = .red
        addChild(ball)
        ball.position = CGPoint(x: size.width / 2, y: 400)
        ball.physicsBody = SKPhysicsBody(rectangleOf: ball.frame.size)
    }
}
複製代碼

得益於 SpriteKit 框架的便捷,當咱們將一個 SKPhysicsBody 關聯到 SKShapeNode 上時,被關聯的精靈的運動將自動符合物理學特徵。這會致使精靈出現如下狀況:

  • SpriteKit 的物理引擎開始跟蹤施加在這個物體上的一切外力,好比重力;
  • 每一幀上都會根據這些外力對該精靈的應該處在的位置和角度進行計算;
  • 該精靈會和其它一樣關聯了 SKPhysicsBody 的經歷發生碰撞。

此時,咱們運行代碼,會發現紅色的小球直接掉出屏幕,由於咱們還未給 GameScene 中添加地面。咱們須要建立一個固定的精靈,它是靜止不動的,以便其它物體可以撞在它上面。

class GameScene: SKScene {
    
    // ...

    private func createContent() {
        let ball = SKShapeNode(circleOfRadius: 10)
        ball.fillColor = .red
        addChild(ball)
        ball.position = CGPoint(x: size.width / 2, y: 400)
        ball.physicsBody = SKPhysicsBody(rectangleOf: ball.frame.size)

        let ground = SKSpriteNode(color: .gray, size: CGSize(width: size.width, height: 200))
        ground.position = CGPoint(x: size.width / 2, y: ground.size.height / 2)
        addChild(ground)
        ground.physicsBody = SKPhysicsBody(rectangleOf: ground.size)
        ground.physicsBody?.isDynamic = false
    }
}
複製代碼

1111.png

此時運行工程,咱們能夠看見小球下落時停在了地面上。在 SpriteKit 中有兩種物體。運動物體,可以受外力影響,可以在場景中運動。靜止物體,不受外力影響,固定在一個地方,運動物體可以和它發生碰撞。

在上文的代碼中,咱們將 groundphysicsBody 屬性 isDynamic 設置爲 false,這個物體將再也不受外力的影響,同時當即中止運動和旋轉(若是以前是在運動的話),可是,咱們依然能夠經過改變 ground 的位置和角度或使用動做 SKAction 來改變它的位置。

修改精靈剛體形狀

如今,咱們小球具有了剛體屬性,但小球是圓形的,而小球的剛體外形卻不是圓形的,咱們能夠經過打開 SKView 的 showsPhysics 屬性來進行查看。

import UIKit
import SpriteKit

class GameViewController: UIViewController {

    override func viewDidLoad() {
            // ...
            view.showsPhysics = true   
        }
    }
}

複製代碼

運行工程,經過一個 for 循環來增長掉落在地面上的小球數量。能夠發現,此時衆多小球的剛體外形是矩形,咱們應該調整其剛體外形爲圓形。

import SpriteKit
import GameplayKit

class GameScene: SKScene {
    
    private func createContent() {
        for _ in 0..<10 {
            let ball = SKShapeNode(circleOfRadius: 10)
            ball.fillColor = .red
            addChild(ball)
            ball.physicsBody = SKPhysicsBody(circleOfRadius: 10)
            ball.position = CGPoint(x: size.width / 2, y: 400)
        }
        
        // ...
    }
}
複製代碼

修改精靈的速度

若是咱們想要改變小球的速度,能夠經過修改小球精靈 physicsBodyvelocity 的屬性,這是修改物體速度的最簡單方法,這是一個 CGVector 類型的屬性,以像素/秒爲單位表示移動速度。

ball.physicsBody?.velocity = CGVector(dx: 200, dy: 200)
複製代碼

注意,直接修改小球速度確實能達到一個很好的效果,咱們能夠用這種方式設置物體的初速度,這一點很是重要!!!對後續從發射臺發射小球時的幫助很是大!

建立一個牆壁

建立牆壁最有效的方式是使用「邊緣碰撞體」。

class GameScene: SKScene {
    
    private func createContent() {
        // ...
        
        let wall = SKNode()
        wall.position = CGPoint(x: 0, y: 0)
        wall.physicsBody = SKPhysicsBody(edgeLoopFrom: CGRect(x: 0, y: 0, width: size.width, height: size.height))
        addChild(wall)
    }
}
複製代碼

「邊緣碰撞體」也是一種碰撞體,可是它僅僅只是一個線條,或者多個鏈接在一塊兒的線條,它沒有體積、沒有質量,它只是靜態物體。

有兩種不一樣的邊緣碰撞體:edgeLoopedgeChainedgeChain 由鏈接在一塊兒的多條險段的集合;前者是由起點、終點以及兩點之間的鏈接線組成。

總結

在這篇文章中,咱們把思惟轉向來 SpriteKit,並經過 SpriteKit 的一些封裝好的 API 完成遊戲的開局設置,搭建好了一個初步的遊戲框架,下一篇文章中,咱們將繼續完善這個遊戲框架,往其中填充內容。

咱們如今完成的內容有:

  • 遊戲講解;
  • 熟悉 2D 編程(ing);
  • 剛體碰撞與檢測(檢測未完成);
  • 小球的發射與方塊的消除;
  • 遊戲邏輯完善。

GitHub 地址: github.com/windstormey…

相關文章
相關標籤/搜索