Jules Jules - 1 year ago 58
Swift Question

Easy SpriteKit Contact Detection (Space Shooter Game) Not Wotking Properly

I'm trying to make a simple Space Shooter game. The contact should happen either between the torpedo and the alien or the shuttle and the alien. The problem is that this second contact (shuttle vs. alien) only happens after the first kind of contact has happend (torpedo vs. alien) and further more they're not always precise. This is a struct created outside the class

struct PhysicsCategory {
static let alien : UInt32 = 1
static let torpedo : UInt32 = 2
static let shuttle : UInt32 = 3 }


shuttle.physicsBody = SKPhysicsBody(rectangleOfSize: shuttle.size)
shuttle.physicsBody?.categoryBitMask = PhysicsCategory.shuttle
shuttle.physicsBody?.contactTestBitMask = PhysicsCategory.alien
shuttle.physicsBody?.dynamic = false
shuttle.physicsBody?.affectedByGravity = false


torpedo.physicsBody = SKPhysicsBody(rectangleOfSize: torpedo.size)
torpedo.physicsBody?.categoryBitMask = PhysicsCategory.torpedo
torpedo.physicsBody?.contactTestBitMask = PhysicsCategory.alien
torpedo.physicsBody?.affectedByGravity = false
torpedo.physicsBody?.dynamic = false


alien.physicsBody = SKPhysicsBody(rectangleOfSize: torpedo.size)
alien.physicsBody?.categoryBitMask = PhysicsCategory.alien
alien.physicsBody?.contactTestBitMask = PhysicsCategory.torpedo
alien.physicsBody?.affectedByGravity = false
alien.physicsBody?.dynamic = true

Finally, here's my contact code:

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

if ((firstBody.categoryBitMask == PhysicsCategory.alien) && (secondBody.categoryBitMask == PhysicsCategory.torpedo)) ||
((firstBody.categoryBitMask == PhysicsCategory.torpedo) && (secondBody.categoryBitMask == PhysicsCategory.alien)) {
self.contactWithTorpedo(firstBody.node as! SKSpriteNode, torpedo: secondBody.node as! SKSpriteNode)
} else if ((firstBody.categoryBitMask == PhysicsCategory.shuttle) && (secondBody.categoryBitMask == PhysicsCategory.alien)) {
self.contactWithShuttle(firstBody.node as! SKSpriteNode, shuttle: secondBody.node as! SKSpriteNode)

func contactWithTorpedo (alien: SKSpriteNode, torpedo : SKSpriteNode) {
scoreLabel.text = "score: " + "\(score)"

func contactWithShuttle (alien:SKSpriteNode, shuttle:SKSpriteNode) {



I'm not really sure where the problem is, plus I've seen a couple of tutorials do the same. I don't know if it's relevant by the way, but this is not an iOS game but an OSX. Thank you in advance!

Answer Source

You might find it less confusing to restructure your didBeginContact as follows, as this avoids the firstBody/secondbody stuff and the complicated if...then conditions to see what has contacted what:

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

    switch contactMask {
    case PhysicsCategory.alien | PhysicsCategory.torpedo:
       // alien and torpedo have contacted
       score += 1
       scoreLabel.text = "score: " + "\(score)"

    case PhysicsCategory.alien | PhysicsCategory.shuttle:
       // alien and shuttle have contacted


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

You can just add as many PhysicsCategory.enemy | PhysicsCategory.player cases as you need for all the contacts that you have to take action for in your game. Code each potential contact individually and you won't loose yourself in if...then...else.

if you do need to reference only one of the nodes involved in a contact, (e.g. to remove the player after an enemy hits it), you can do it like this:

let playerNode = contact.bodyA.categoryBitMask == PhysicsCategory.player ? contact.bodyA.node! : contact.bodyB.node!