4
votes

in SpriteKit we detect the in the didBeginContact method. but this looks a bit stupid to do something like that: func didBeginContact(contact: SKPhysicsContact) {

    if let contactA = contact.bodyA.node?.name {

        if let contactB = contact.bodyB.node?.name {

            //now that we have safely unwrapped these nodes, we can operate on them

            if contactA == "ball" {

                collisionBetweenBall(contact.bodyA.node!, object: contact.bodyB.node!)

            } else if contactB == "ball" {

                collisionBetweenBall(contact.bodyB.node!, object: contact.bodyA.node!)

            }

        }

    }

}

is there any way I can ensure that bodyA is always a ball? is there any rules related to categorybitmask?

2

2 Answers

6
votes

If you are using simple categories, with each physics body belonging to only one category, then this alternative form of didBeginContact may be more readable:

func didBeginContact(contact: SKPhysicsContact) {
    let contactMask = contact.bodyA.categoryBitMask | contact.bodyB.categoryBitMask

switch contactMask {

   case categoryBitMask.ball | categoryBitMask.something:
       print("Collision between ball and something")
       let ballNode = contact.bodyA.categoryBitMask == categoryBitMask.ball ? contact.bodyA.node! : contact.bodyB.node!
       ballNode.pop()
       score += 10

   default :
       //Some other contact has occurred
       print("Some other contact")
   }
}

Note that if your categories are more complicated, this won't work as the simple AND tests (categoryBitMask.ball | categoryBitMask.something) won't match.

0
votes

Usually you would do this

First you would store your bit mask categories if you are not doing it already.

enum PhysicsCategory {
     static let ball: UInt32 =  0x1 << 0
     static let wall: UInt32 =  0x1 << 1
     static let enemy: UInt32 = 0x1 << 2
}

and than assign them like so to your sprites

 ball.physicsBody?.categoryBitMask = PhysicsCategory.ball
 ball.physicsBody?.contactTestBitMask = PhysicsCategory.wall | PhysicsCategory.enemy

 wall.physicsBody?.categoryBitMask = PhysicsCategory.wall
 wall.physicsBody?.contactTestBitMask = 0 

 enemy.physicsBody?.categoryBitMask = PhysicsCategory.enemy
 enemy.physicsBody?.contactTestBitMask = 0 

Note: Because ball checks for contacts with both wall and enemy in this example you would not need to tell wall or enemy to check contacts with ball. Its fine to do it on 1 physics body so you can set it to 0 on the others unless they have other specific contacts to look for.

Than you can check for them in the contact method like so without the double if statements.

func didBegin(_ 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
    }


    // Ball hit wall or wall hit ball
    if (firstBody.categoryBitMask == PhysicsCategory.ball) && (secondBody.categoryBitMask == PhysicsCategory.wall) {

          // Some code
    }

    // Ball hit enemy or enemy hit ball
    if (firstBody.categoryBitMask == PhysicsCategory.ball) && (secondBody.categoryBitMask == PhysicsCategory.enemy) {

          // Some code
    }
}

Also do not forget to set the delegate in didMoveToView of your scene for this to work.

physicsWorld.contactDelegate = self

Hope this helps