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

前言

在上一篇文章中,咱們已經完成了使用 SpriteKit 實踐一些小的 demo 實例,對 SpriteKit 有了一個大致上的實踐體驗,在這篇文章中,咱們主要關注在剛體和剛體之間,也就是小球和方塊之間的碰撞交互,整個遊戲的核心也就在這。node

碰撞檢測

在 SpriteKit 中進行剛體和剛體之間的碰撞檢測,須要對這兩個剛體所處的 SKScene 設置對 SKPhysicsContactDelegate 協議的遵照,這樣咱們的 GameScene 就能夠接收到在其之中的各個剛體之間發生碰撞的「通知」。git

class GameScene: SKScene {
    
    override init(size: CGSize) {
        super.init(size: size)

        physicsWorld.contactDelegate = self
    }

    // ...
}

extension GameScene: SKPhysicsContactDelegate {
    func didBegin(_ contact: SKPhysicsContact) {
        
    }
    
    func didEnd(_ contact: SKPhysicsContact) {
        
    }
}
複製代碼

想要讓兩個剛體之間發生碰撞,這部份內容咱們已經在上一篇文章中學習過了,如今咱們須要進行的是如何讓兩個剛體之間發生碰撞後,咱們的 GameScene 可以接收到的碰撞通知,定一個結構體。github

struct BitMask {
    static let Ball = UInt32(0x00001)
    static let Box = UInt32(0x00002)
    static let Ground = UInt32(0x00003)
}
複製代碼

在這個結構體 BitMask 中定義了三個兩種常量值,這些常量值被稱爲「碰撞檢測掩碼」,經過使用 physicsBodycontactTestBitMask 定義物體的類型。默認狀況下,若是咱們不給剛體設置 contactTestBitMask,該值爲 0,也就是說這種剛體不屬於任何碰撞檢測的類型。編程

SKPhysicsContactDelegate 協議方法中的參數 SKPhysicsContact 對象參數包含了這次碰撞的相關信息,如碰撞點和力的大小。swift

open class SKPhysicsContact : NSObject {

    
    open var bodyA: SKPhysicsBody { get }

    open var bodyB: SKPhysicsBody { get }

    open var contactPoint: CGPoint { get }

    open var contactNormal: CGVector { get }

    open var collisionImpulse: CGFloat { get }
}
複製代碼

有了「碰撞檢測掩碼」,咱們就能夠在碰撞檢測回調中判斷當前究竟是什麼剛體和什麼剛體發生了碰撞。markdown

extension GameScene: SKPhysicsContactDelegate {
    func didBegin(_ contact: SKPhysicsContact) {
        
    }
    
    func didEnd(_ contact: SKPhysicsContact) {
        print(contact.bodyA.contactTestBitMask)
        print(contact.bodyB.contactTestBitMask)
    }
}
複製代碼

bitMaks

physicsBody 中含有三個屬性值,ide

  • categoryBitMask,定義剛體所屬的類別,供碰撞檢測進行區分,默認屬於全部類型。
  • collisionBitMask,定義剛體可與哪一種類別的其它剛體發生碰撞,默承認與全部類型發生碰撞。
  • contactTestBitMask,定義剛體可與哪一種類別的其它剛體發生接觸,默認不與全部類型發生接觸,供代理實現中調用。

這三個屬性值分別控制了 SpriteKit 中物體和物體之間的碰撞關係。在咱們的這個遊戲中,小球和小球之間是不能發生碰撞的,而小球和方塊之間是能夠發生碰撞的,而咱們須要在 SpriteKit 的接觸代理方法中判斷出當前發生碰撞的兩個對象物體分別是什麼。oop

class GameScene: SKScene {
    // ...

    private func createContent() {
        for row in 0..<10 {
            let ball = Ball(circleOfRadius: 10)
            ball.fillColor = .red
            addChild(ball)
            ball.physicsBody = SKPhysicsBody(circleOfRadius: 10)
            // 設置初速度
            ball.physicsBody?.velocity = CGVector(dx: 300 + CGFloat(row) * 0.1, dy: 300)
            ball.position = CGPoint(x: size.width / 2, y: 400)

            ball.physicsBody?.categoryBitMask = BitMask.Ball
            ball.physicsBody?.contactTestBitMask = BitMask.Box
            ball.physicsBody?.collisionBitMask = BitMask.Box

            ball.physicsBody?.linearDamping = 0
            ball.physicsBody?.restitution = 1
        }
        
        let box = Box(rectOf: CGSize(width: 50, height: 50))
        box.position = CGPoint(x: 300, y: 800)
        box.physicsBody = SKPhysicsBody(rectangleOf: CGSize(width: 50, height: 50))

        box.physicsBody?.categoryBitMask = BitMask.Box
        box.physicsBody?.collisionBitMask = BitMask.Box
        box.physicsBody?.contactTestBitMask = BitMask.Ball

        box.fillColor = .blue
        // 靜態物體
        box.physicsBody?.isDynamic = false
        box.physicsBody?.restitution = 1
        addChild(box)
    }

    // ...
}
複製代碼

此時,運行工程,咱們的小球已經能夠和方塊發生碰撞了。更進一步,咱們須要當小球和方塊進行接觸時,把方塊從視圖中移除。讓 GameScene 遵照 SKPhysicsContactDelegate學習

extension GameScene: SKPhysicsContactDelegate {
    func didBegin(_ contact: SKPhysicsContact) {        
        switch contact.bodyA.categoryBitMask {
        case BitMask.Box:
            checkNodeIsBox(contact.bodyA.node)
            
        default:
            break
        }
        
        switch contact.bodyB.categoryBitMask {
        case BitMask.Box:
            checkNodeIsBox(contact.bodyB.node)
            
        default:
            break
        }
    }
}

extension GameScene {
    private func checkNodeIsBox(_ node: SKNode?) {
        guard let box = node else { return }
        
        if box.physicsBody?.categoryBitMask == BitMask.Box {
            box.removeFromParent()
        }
    }
}
複製代碼

方塊遞減

當小球撞上方塊時,咱們要給方塊設置一個關卡數。好比當方塊上關卡數爲 8 時,須要小球撞擊方塊 8 次才能將該方塊進行消除。給方塊多增長一個子節點 lableNode,用於記錄當前方塊剩餘被撞擊數。ui

class GameScene: SKScene {
    // ...
    
    private func createContent() {
        // ...

        for row in 1...5 {
            let box = Box(rectOf: CGSize(width: 50, height: 50))
            box.position = CGPoint(x: 50 + (row * 50 + 20), y: (800 - row * 50 + 20))
            box.physicsBody = SKPhysicsBody(rectangleOf: CGSize(width: 50, height: 50))
            box.physicsBody?.categoryBitMask = BitMask.Box
            box.physicsBody?.contactTestBitMask = BitMask.Ball
            box.physicsBody?.collisionBitMask = BitMask.Box
            box.physicsBody?.linearDamping = 0
            box.physicsBody?.restitution = 1.0
            box.physicsBody?.isDynamic = false
            box.fillColor = .red
            
            let label = Label(text: "\(row)")
            label.fontSize = 22
            label.typoTag = 666
            label.fontName = "Arial-BoldMT"
            label.color = .white
            label.position = CGPoint(x: 0, y: -label.frame.size.height / 2)
            box.addChild(label)
            
            addChild(box)
        }
    }
}
複製代碼

GameScene 的代理回調方法中,完善 checkNodeIsBox,使其支持對方塊剩餘撞擊數的檢測。

extension GameScene {
    private func checkNodeIsBox(_ node: SKNode?) {
        guard let box = node else { return }
        
        if box.physicsBody?.categoryBitMask == BitMask.Box {
            let label = box.children.first! as! Label
            var tag = Int(label.text!)!
            if (tag > 1) {
                tag -= 1
                label.text = "\(tag)"
            } else {
                box.removeFromParent()
            }
        }
    }
}
複製代碼

此時,運行工程,咱們發現小球已經能夠和方塊進行遞減的碰撞檢測了!!!

總結

在這篇文章中,咱們繼續上篇文章中未完成的小球與方塊的碰撞檢測,經過對 SKNode 中三個 bitMask 屬性的理解和運用,保證了 GameScene 中小球和小球之間不發生碰撞,小球和方塊、小球和牆體以及小球和地面發生碰撞,而且方塊已經具有了遞減消失的能力,遊戲的核心檢測邏輯都已經完成。在下一篇文章中咱們將完成小球的發射和回收邏輯。

咱們如今完成的內容有:

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

GitHub 地址: github.com/windstormey…

相關文章
相關標籤/搜索