1

I was programming this simple space game in Swift until I encountered the problem of detecting collisions. After looking around forums, tutorials, etc, I tried to implement collisions by declaring bitmasks like so:

object 1

    enemy?.physicsBody = SKPhysicsBody(circleOfRadius: ((enemy?.size.width)!/2))
    enemy?.physicsBody?.categoryBitMask = enemyBitMask
    enemy?.physicsBody?.contactTestBitMask = bulletBitMask
    enemy?.physicsBody?.collisionBitMask = 0

object 2

    bullet?.physicsBody? = SKPhysicsBody(rectangleOf: (bullet?.size)!)
    bullet?.physicsBody?.categoryBitMask =  bulletBitMask
    bullet?.physicsBody?.contactTestBitMask =  enemyBitMask
    bullet?.physicsBody?.collisionBitMask = 0
    bullet?.physicsBody?.usesPreciseCollisionDetection = true

I also put a print statement in the

func didBegin(_ contact: SKPhysicsContact) { print("Hello") }

here is how I configure my sprites:

    func CreateNewEnemy() {
    var enemy : SKSpriteNode?

       let moveEnemyDown = SKAction.repeatForever(SKAction.moveBy(x: 0, y: -1, duration: 0.01))
       let rotateEnemy = SKAction.repeatForever(SKAction.rotate(byAngle: 25, duration: 5))

      let enemyXpos = randomNum(high:  self.frame.size.width/2, low: -1 * self.frame.size.width/2)
      let enemyYpos = randomNum(high:  2.5*self.frame.size.height, low: self.frame.size.height/2)
      let enemyOrigin : CGPoint = CGPoint(x: enemyXpos, y: enemyYpos)

      enemy = SKSpriteNode(imageNamed: possibleEnemyImage[Int(arc4random_uniform(4))])
      print(enemy?.size.height)
      enemy?.scale(to: CGSize(width: player.size.height, height: player.size.height))
      print(enemy?.size.height)
      enemy?.position = enemyOrigin
      enemy?.run(moveEnemyDown)
      enemy?.run(rotateEnemy)
      let enemyRadius : CGFloat = (enemy?.size.width)!/2
      print(enemyRadius)

      enemy?.physicsBody? = SKPhysicsBody(circleOfRadius: enemyRadius)

      enemy?.physicsBody?.categoryBitMask = enemyCategory
      enemy?.physicsBody?.contactTestBitMask = bulletCategory
      enemy?.physicsBody?.collisionBitMask = 0
      enemy?.zPosition = 1

      enemiesArray.append(enemy!)
      self.addChild(enemy!)
}

to create enemy (Called in did move to view function)

    func CreateAllEnemies(amountOfEnemies : UInt8) {
    for _ in 0...amountOfEnemies {
        CreateNewEnemy()
    }
}

and the other sprite

    func CreateNewBullet() {
     let bulletOrigin : CGPoint = CGPoint(x: player.position.x, y: player.position.y+player.size.height/2)
     let moveBulletUp = SKAction.repeatForever(SKAction.moveBy(x: 0, y: 3, duration: 0.01))

     var bullet : SKSpriteNode?
     bullet = SKSpriteNode(imageNamed: "bulletImage")
     bullet?.position = bulletOrigin
     bullet?.run(moveBulletUp)

     bullet?.physicsBody? = SKPhysicsBody(rectangleOf: (bullet?.size)!)
     bullet?.physicsBody?.categoryBitMask =  bulletCategory
     bullet?.physicsBody?.contactTestBitMask = enemyCategory
     bullet?.physicsBody?.collisionBitMask = 0
     bullet?.physicsBody?.isDynamic = true
     bullet?.physicsBody?.usesPreciseCollisionDetection = true
     bullet?.zPosition = 1

     bulletsArray.append(bullet!)
     self.addChild(bullet!)
}

this one is created with a timer

bulletTimer = Timer.scheduledTimer(timeInterval: 0.25, target: self, selector: #selector(CreateNewBullet) , userInfo: nil, repeats: true)

Unfortunately it does not print anything in the console after I see the two objects touching.

Arjun
  • 358
  • 5
  • 14

1 Answers1

3
  1. Define unique categories, ensure your class is a SKPhysicsContactDelegate and make yourself the physics contact delegate:

//Physics categories

let enemyCategory:    UInt32 = 1 << 1
let bulletCategory:   UInt32 = 1 << 2

class GameScene: SKScene, SKPhysicsContactDelegate {
   self.physicsWorld.contactDelegate = self
  1. Assign the categories (usually in didMove(to view:) :

enemy.physicsBody.categoryBitMask = enemyCategory bullet.physicsBody.categoryBitMask = bulletCategory

(Make sure you've created physics bodies for each node)

  1. Set up collisions:

enemy.physicsBody?.collisionBitMask = 0 // enemy collides with nothing bullet.physicsBody?.collisionBitMask = 0 // bullet collides with nothing

or even:

for node in [enemy, bullet] {
   node.physicsBody?.collisionBitMask = 0 //  collides with nothing
   }
  1. Set up contacts

    bullet.physicsBody?.collisionBitMask = enemyCategory // bullet contacts enemy

Make sure that at least one of the objects involved in each potential contact has the isDynamic property on its physics body set to true, or no contact will be generated. It is not necessary for both of the objects to be dynamic.

You should now get didBegin called when the bullet and the enemy make contact. You could code didBegin like this:

  func didBegin(_ contact: SKPhysicsContact) {

     print("didBegin entered for \(String(describing: contact.bodyA.node.name)) and \(String(describing: contact.bodyB.node.name))")

     let contactMask = contact.bodyA.categoryBitMask | contact.bodyB.categoryBitMask

     switch contactMask {
     case bulletCategory | enemyCategory:

        print("bullet and enemy have contacted.")

        let bulletNode = contact.bodyA.categoryBitMask == bulletCategory ? contact.bodyA.node : contact.bodyB.node
        enemyHealth -= 10
        bulletNode.removeFromParent
     default:
        print("Some other contact occurred")
     }

}

Steve Ives
  • 7,894
  • 3
  • 24
  • 55
  • I did what you said, but it still wont print anything – Arjun Jun 26 '18 at 17:42
  • @ArjunBemarkar then you need to provide better context to your question. – Knight0fDragon Jun 26 '18 at 17:45
  • It sounds like ‘didBegin’ isn’t getting called at all. This code works, but isn’t not a complete program so if you put the snippets in the wrong order, it still won’t work. Can you post the code where you initialise your sprites and place them in the scene? – Steve Ives Jun 26 '18 at 18:49
  • Ok I put how I create and add the sprites to the scene – Arjun Jun 26 '18 at 21:04
  • I don't think 'bullet' has a physics body, because you are using optional chaining in `bullet?.physicsBody? = SKPhysicsBody...` and if there's no physicBody, then there's no collisions or contacts. Try `bullet?.physicsBody = SKPhysicsBody(rectangleOf: (bullet?.size)!)` – Steve Ives Jun 26 '18 at 22:49
  • @ArjunBemarkar Notice how you didn't use `physicsBody?` when you created the enemy's physicsBody? – Steve Ives Jun 26 '18 at 22:53
  • @SteveIves tried putting `physicsBody?`, but it did not work. – Arjun Jun 26 '18 at 23:22
  • Set `showPhysicsbodies` to 'true'; in your GameScene and see if the physics bodies look normal. – Steve Ives Jun 26 '18 at 23:24
  • @SteveIves I did `view.showsPhysics = true` in GameScene, but that does not change anything – Arjun Jun 27 '18 at 04:08
  • @Arun what do you mean by "tried putting physicsBody?"? You should be using `bullet.physicsBody = SKPhysicsBody...` and `enemy.physicsBody = SKPhysicsBody...` – Steve Ives Jun 27 '18 at 17:13
  • @Steve I had already put `bullet?.physicsBody? = SKPhysicsBody(rectangleOf: (bullet?.size)!)` and `enemy?.physicsBody? = SKPhysicsBody(circleOfRadius: enemyRadius)` – Arjun Jun 27 '18 at 17:41
  • You want to remove the ‘?’ after `physicsBody` at this point physicsBody is still optional so physicsBody? will cause the rest of the statement to be skipped. If you tried to print `enemy.physicsbody` afterwards, I’d suspect you’d get nil – Steve Ives Jun 27 '18 at 17:44
  • @Steve after turning on `view.showsPhysics = true` I can only see the physics body outline for a ship sprite, the physics body outline is not showing on the enemy or the bullet, I will post a new question for that. – Arjun Jun 27 '18 at 18:34
  • Enemy and bullet don’t have physics bodies because you’ve used ‘physicsBody?‘ instead of ‘physicsBody‘ judging by the code you’ve posted. – Steve Ives Jun 27 '18 at 20:38
  • Yes, that was it !! it works when I make the SKSpriteNode not an optional, it prints hello on the console – Arjun Jun 27 '18 at 20:58
  • yep. Because whilst the Swift run time is processing the statement! Before the assignment is done the physicsBody doesn’t exist, so Swift doesn’t bother with the rest of the statement. I should update my checklist but glad I could help. – Steve Ives Jun 27 '18 at 21:37