Search code examples
swiftsprite-kitcollision-detectionskspritenodeskphysicsbody

Game ends when player node collides with another node, when the game should only end when the player collides with the boundary


The problem: As soon as the player node comes in contact with a coin node, the game ends when the game should only end when the player collides with the boundary.

What the output should be: The player should be able to come in contact with the coin node and travel through it, adding a value to the scoreLabel. The current code:

 struct ColliderType {

static let playerCategory: UInt32 = 0x1 << 0
static let boundary: UInt32 = 0x1 << 1
static let coinCategory: UInt32 = 0x1 << 2
static let firstBody: UInt32 = 0x1 << 3
static let secondBody: UInt32 = 0x1 << 4

}

   var gameOver = false
   var coinInt = 0

    coin.physicsBody?.categoryBitMask = ColliderType.coinCategory
    coin.physicsBody?.contactTestBitMask = ColliderType.playerCategory
    coin.physicsBody?.collisionBitMask = 0


    player.physicsBody?.categoryBitMask = ColliderType.playerCategory
    player.physicsBody?.contactTestBitMask = ColliderType.boundary | ColliderType.coinCategory
    player.physicsBody?.collisionBitMask = ColliderType.boundary

func didBeginContact(contact:SKPhysicsContact) {
    var firstBody: SKPhysicsBody
    var secondBody: SKPhysicsBody

    if contact.bodyA.categoryBitMask < contact.bodyB.categoryBitMask {
        firstBody = contact.bodyA
        secondBody = contact.bodyB


    } else{

        firstBody = contact.bodyB
        secondBody = contact.bodyA

    }

    if firstBody.categoryBitMask == ColliderType.playerCategory && secondBody.categoryBitMask == ColliderType.coinCategory {
       self.coin.removeFromParent()
       coinInt += 1
       coinLabel.text = "\(coinInt)"
    }

    gameOver = true
    self.speed = 0
    timer.invalidate()

}

override func touchesBegan(touches: Set<UITouch> , withEvent event: UIEvent?) {
    if gameOver == false {

    self.player.physicsBody?.velocity = CGVectorMake(1, 3)
    self.player.physicsBody?.applyImpulse(CGVectorMake(0, 12))

    }

}

UPDATE:

    boundary.contactTestBitMask = ColliderType.playerCategory
    boundary.categoryBitMask = ColliderType.boundary
    boundary.collisionBitMask = ColliderType.playerCategory

   coin.physicsBody?.categoryBitMask = ColliderType.coinCategory
   coin.physicsBody?.contactTestBitMask = ColliderType.playerCategory
   coin.physicsBody?.collisionBitMask = 0


    player.physicsBody?.categoryBitMask = ColliderType.playerCategory
    player.physicsBody?.contactTestBitMask = ColliderType.coinCategory
    player.physicsBody?.collisionBitMask = ColliderType.boundary




func didBeginContact(contact:SKPhysicsContact) {
    let firstBody: SKPhysicsBody
    let secondBody: SKPhysicsBody


    if contact.bodyA.categoryBitMask < contact.bodyB.categoryBitMask {
        firstBody = contact.bodyA
        secondBody = contact.bodyB


    } else{

        firstBody = contact.bodyB
        secondBody = contact.bodyA

    }


    if firstBody.categoryBitMask == ColliderType.coinCategory || secondBody.categoryBitMask == ColliderType.coinCategory {

    self.coin.removeFromParent()
    self.coinInt += 1
    self.coinLabel.text = "\(self.coinInt)"

    }else if firstBody.categoryBitMask == ColliderType.boundary || secondBody.categoryBitMask == ColliderType.boundary {

    gameOver = true
    self.speed = 0
    timer.invalidate()

}
}

Solution

  • Your gameOver = true statement is outside all the ifs in didBeginContact. In other words: The moment a contact happens you set gameOver = true.

    if firstBody.categoryBitMask == ColliderType.coinCategory || secondBody.categoryBitMask == ColliderType.coinCategory {
       self.coin.removeFromParent()
       coinInt += 1
       coinLabel.text = "\(coinInt)"
    } else if firstBody.categoryBitMask == ColiderType.boundary || secondBody.categoryBitMask == ColiderType.boundary {
        gameOver = true
        self.speed = 0
        timer.invalidate()
    }
    

    Is probably closer to what you want.