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…

相关文章
相关标签/搜索