Search code examples
swiftskphysicsbodycollision

Swift: Physics Body Collision Bad Instruction Error


When I run this code the first CollisionWithplayer line gives me a bad instruction error. The error doesn't appear every time, only every once in a while with no similar conditions to identify what is causing it.

func didBeginContact(contact: SKPhysicsContact) {
    let firstBody : SKPhysicsBody = contact.bodyA
    let secondBody : SKPhysicsBody = contact.bodyB


    if ((firstBody.categoryBitMask == PhysicsCategory.Goblin) && (secondBody.categoryBitMask == PhysicsCategory.Bullet) ||
        (firstBody.categoryBitMask == PhysicsCategory.Bullet) && (secondBody.categoryBitMask == PhysicsCategory.Goblin))
    {

       CollisionWithBullet(firstBody.node as! SKSpriteNode, Bullet: secondBody.node as! SKSpriteNode)
    }
        else  if ((firstBody.categoryBitMask == PhysicsCategory.Goblin) && (secondBody.categoryBitMask == PhysicsCategory.player) ||
        (firstBody.categoryBitMask == PhysicsCategory.player) && (secondBody.categoryBitMask == PhysicsCategory.Goblin)){

        CollisionWithplayer(firstBody.node as! SKSpriteNode, player: secondBody.node as! SKSpriteNode)
            }

func CollisionWithBullet(Goblin: SKSpriteNode, Bullet:SKSpriteNode){
    Goblin.removeFromParent()
    Bullet.removeFromParent()
    score += 1
    ScoreLbl.text = "\(score)"
    var explosion = SKEmitterNode(fileNamed: "Goblin Death Animation.sks")
    explosion!.particlePosition = Goblin.position
    self.addChild(explosion!)
    var fire = SKEmitterNode(fileNamed: "Goblin Death Animation 2.sks")
    fire!.particlePosition = Goblin.position
    self.addChild(fire!)


}
func CollisionWithplayer(Goblin: SKSpriteNode, player: SKSpriteNode){
    let ScoreDefault = NSUserDefaults.standardUserDefaults()
    ScoreDefault.setValue(score, forKey: "Score")
    ScoreDefault.synchronize()


    if (score > Highscore){

        let HighscoreDefault = NSUserDefaults.standardUserDefaults()
        HighscoreDefault.setValue(score, forKey: "Highscore")

    }

    Goblin.removeFromParent()
    player.removeFromParent()
    self.view?.presentScene(EndScene())
    ScoreLbl.removeFromSuperview()
}

Solution

  • I assume you get an error because your code doesn't treat the case where 1 collision causes the didBeginContact method to fire more than once (collision happened at 2 points of same node)

    I would rewrite your code like this to avoid such a case (using optionals). Furthermore I slightly rewrote it so you don't have to write 2 if statements for each collision.

     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 == PhysicsCategory.Goblin) && (secondBody.categoryBitMask == PhysicsCategory.Bullet) {
              collisionWithBullet(firstBody.node as? SKSpriteNode, bullet: secondBody.node as? SKSpriteNode)
         }
    
         if (firstBody.categoryBitMask == PhysicsCategory.Goblin) && (secondBody.categoryBitMask == PhysicsCategory.player) {
              collisionWithPlayer(firstBody.node as? SKSpriteNode, player: secondBody.node as? SKSpriteNode)
         }
     }
    
    func collisionWithBullet(goblin: SKSpriteNode?, bullet:SKSpriteNode?){
         guard let goblin = goblin, bullet = bullet else { return }
    
         goblin.removeFromParent()
         bullet.removeFromParent()
         score += 1
         scoreLbl.text = "\(score)"
         if let explosion = SKEmitterNode(fileNamed: "Goblin Death Animation.sks") {
               explosion.particlePosition = goblin.position
               self.addChild(explosion)
          }
          if let fire = SKEmitterNode(fileNamed: "Goblin Death Animation 2.sks") {
              fire.particlePosition = goblin.position
              self.addChild(fire)
          }
      }
    
      func collisionWithPlayer(goblin: SKSpriteNode?, player: SKSpriteNode?){
         guard let goblin = goblin, player = player else { return }
    
         let scoreDefault = NSUserDefaults.standardUserDefaults()
         scoreDefault.setValue(score, forKey: "Score")
          // synchronised not needed anymore
    
    
         if (score > highscore){
             let highscoreDefault = NSUserDefaults.standardUserDefaults()
             highscoreDefault.setValue(score, forKey: "Highscore")
         }
    
         goblin.removeFromParent()
         player.removeFromParent()
         self.view?.presentScene(EndScene())
         scoreLbl.removeFromSuperview()
     }
    

    Please also follow the swift guidlines, your methods and properties should start will small letters not will capital letters.

    Hope this helps