How to add pixel-perfect physics to an SKSpriteNode

To perfect the collision detection, basically to ignore transparency during the interest we can use SKPhysicsBody. The idea here is to have two images – one is what User sees, another is very simplified version for SpriteKit engine – simplified from color and geometry standpoint. The more complex “collision body” is the more resources it will spend during each frame to calculate collisions. Just for instance here’s the actual image and it’s “mask”

Now we are ready for some code

 
let birdMask: UInt32 = 0x1 << 0
let pipeMask: UInt32 = 0x1 << 1
//...
 
pipeImage = SKSpriteNode(imageNamed: "realImage")
//... size and position
 
let maskTexture = SKSpriteNode(imageNamed: mask)
maskTexture.size = pipeImage!.size // size of texture w/ real imageNamed
 
pipeImage!.physicsBody?.usesPreciseCollisionDetection = true
pipeImage!.physicsBody = SKPhysicsBody(texture: maskTexture.texture!, size: size)        
pipeImage!.physicsBody?.affectedByGravity = false // disable falling down...
pipeImage!.physicsBody?.allowsRotation = false
pipeImage!.physicsBody?.isDynamic = true
pipeImage!.physicsBody?.friction = 0
pipeImage!.physicsBody?.categoryBitMask = pipeMask
pipeImage!.physicsBody?.collisionBitMask = birdMask | pipeMask
pipeImage!.physicsBody?.contactTestBitMask = birdMask | pipeMask

That is it! Now you can detect collision, you just need to enable it in didMove method

physicsWorld.contactDelegate = self
// Do not forget to add SKPhysicsContactDelegate here:
// class GameScene: SKScene, SKPhysicsContactDelegate {

and here it is

// MARK: collision delegate
 
func didBegin(_ contact: SKPhysicsContact) {
    let firstNode = contact.bodyA.node as! SKSpriteNode
    let secondNode = contact.bodyB.node as! SKSpriteNode
 
    if firstNode.name == "bird" {
        collisionBetween(bird: firstNode, object: secondNode)
    } else if secondNode.name == "bird" {
        collisionBetween(bird: secondNode, object: firstNode)
    }
}

That’s it! collisionBetween is just custom code to handle it

func collisionBetween(bird: SKNode, object: SKNode) {
    if object.name == "pipe" {
        // Let's disable bird to let if fall
        self.bird.isAlive = false
        if IS_DEBUG { print("bird collide with pipe") }
 
        // Restore speed
        Pipeline.speed = Pipeline.initial_speed
 
        // Render bloody rectangle - pad'n my english here ;)
        if self.bloodyFrame.parent == nil {
            self.bloodyFrame.zPosition = 200
            self.bloodyFrame.size = self.frame.size
            self.bloodyFrame.position = CGPoint(x: self.frame.width/2, y: self.frame.height/2)
            self.addChild(bloodyFrame)
        }
    }
}

This code is from example/sample game which is very similar to flappy bird + chicken scream, you need to scream to make the bird fly, feel free to checkout how it works – scream to fly.

Social Share Toolbar