0

I am trying to learn SpriteKit, following this tutorial

Everything was working fine until I wanted to add another collision between two different objects. I have Enemy nodes, Player(spaceship), Projectiles and Meteors, the Enemies come down and you shoot them from the ship with projectiles and avoid the meteors. I have tried setting up the physics category struct, setting correct physics categories for each node and creating the collision functions.

Problem is, The meteors are triggering the function that is supposed to happen when the meteors hit the spaceship character when they hit the enemies also, I tried changing things and the collisions just mix up but I can never quite get it right. I just want the projectiles to hit the enemy ships, and the meteors to damage the players ship.

Here is what I have done:

struct PhysicsCategory {
static let none      : UInt32 = 0
static let all       : UInt32 = UInt32.max
static let monster   : UInt32 = 0x2       // 1
static let projectile: UInt32 = 0x3      // 2
static let spaceship   : UInt32 = 0x4
static let meteor : UInt32 = 0x5
static let edge : UInt32 = 0x6
} 

Spaceship:

  spaceship.physicsBody = SKPhysicsBody(rectangleOf: spaceship.size) // 1
    spaceship.physicsBody?.isDynamic = true // 2
    spaceship.physicsBody?.categoryBitMask = PhysicsCategory.spaceship // 3
    spaceship.physicsBody?.contactTestBitMask = PhysicsCategory.meteor// 4
    spaceship.physicsBody?.collisionBitMask = PhysicsCategory.none // 5

Enemy:

 monster.physicsBody = SKPhysicsBody(rectangleOf: monster.size) // 1
    monster.physicsBody?.isDynamic = true // 2
    monster.physicsBody?.categoryBitMask = PhysicsCategory.monster // 3
    monster.physicsBody?.contactTestBitMask = PhysicsCategory.projectile // 4
    monster.physicsBody?.collisionBitMask = PhysicsCategory.none // 5

Projectile:

 projectile.physicsBody = SKPhysicsBody(rectangleOf: projectile.size)
    projectile.physicsBody?.isDynamic = true
    projectile.physicsBody?.categoryBitMask = PhysicsCategory.projectile
    projectile.physicsBody?.contactTestBitMask = PhysicsCategory.monster
    projectile.physicsBody?.collisionBitMask = PhysicsCategory.none
    projectile.physicsBody?.usesPreciseCollisionDetection = true

Meteor:

   monster.physicsBody = SKPhysicsBody(rectangleOf: monster.size) // 1
    monster.physicsBody?.isDynamic = true // 2
    monster.physicsBody?.categoryBitMask = PhysicsCategory.meteor // 3
    monster.physicsBody?.contactTestBitMask = PhysicsCategory.none // 4
    monster.physicsBody?.collisionBitMask = PhysicsCategory.none// 5

Finally, the physics functions:

  func projectileDidCollideWithMonster(projectile: SKSpriteNode, monster: SKSpriteNode) {
    print("Hit")
    run(SKAction.playSoundFileNamed("Dull-Impact-Hit.wav", waitForCompletion: false))

    hits += 1
    scoreLabel.text = "Score: \(hits)/25"
    if hits == 25 {
        self.removeAction(forKey: "addMonster")
        self.scoreLabel.text = "Victory!"
    }
    monster.removeAction(forKey: "spaceshipMoving")
    monster.run(SKAction.animate(with: explosionAnimation,
                                 timePerFrame: 0.1,
                                 resize: false,
                                 restore: true))
    monster.run(SKAction.animate(with: explosionAnimation,
                                 timePerFrame: 0.1,
                                 resize: false,
                                 restore: true)) {
                                    monster.removeFromParent()
    }
    projectile.removeFromParent()

}

func meteorDidCollideWithSpaceship(meteor: SKSpriteNode, spaceship: SKSpriteNode) {
    print("Ouch")
    run(SKAction.playSoundFileNamed("Dull-Impact-Hit.wav", waitForCompletion: false))

    health -= 1
    healthLabel.text = String(health)
    print("health" + String(health))
    if health == 0 {
    scoreLabel.text = "You died!"
    healthLabel.text = "☠"
    self.view?.isUserInteractionEnabled = false
    self.spaceship.removeFromParent()
    }
}
}

  extension GameScene: SKPhysicsContactDelegate {
func didBegin(_ contact: SKPhysicsContact) {

    // 1
    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
    }

    // 2
    if ((firstBody.categoryBitMask & PhysicsCategory.monster != 0) &&
        (secondBody.categoryBitMask & PhysicsCategory.projectile != 0)) {
        if let monster = firstBody.node as? SKSpriteNode,
            let projectile = secondBody.node as? SKSpriteNode {
            projectileDidCollideWithMonster(projectile: projectile, monster: monster)
        }
    }
   if ((firstBody.categoryBitMask & PhysicsCategory.spaceship != 0 ) &&
        (secondBody.categoryBitMask & PhysicsCategory.meteor != 0 )) {
        if let meteor = firstBody.node as? SKSpriteNode,
            let spaceship = secondBody.node as? SKSpriteNode {
            meteorDidCollideWithSpaceship(meteor: meteor, spaceship: spaceship)
        }
    }
}

}

How can I get my intended behaviors? Thank you.

Peter Ruppert
  • 1,067
  • 9
  • 24
  • Your categories should have only one bit set. Try `let monster:UInt32 = 1 << 0`, `let projectile:UInt32 = 1 << 1`, etc. – 0x141E Jun 19 '19 at 10:26
  • @0x141E That's perfect, thank you! This is something I don't really understand, maybe you can make an answer explaining how it works? Also my characters still are able to move outside the bounds even tho I have: ` self.scaleMode = .aspectFit` and `self.physicsBody = SKPhysicsBody(edgeLoopFrom: self.frame)` any ideas why? – Peter Ruppert Jun 19 '19 at 10:40
  • 1
    Many answers explain bitmasking, https://stackoverflow.com/questions/46494701/understanding-spritekit-collisionbitmask – Knight0fDragon Jun 19 '19 at 12:59
  • 1
    You also have collision set to none.... what did you expect to hold them in? – Knight0fDragon Jun 19 '19 at 13:01
  • Thanks that question is just what I need. I changed the collision of the ship to edge, and set the self.physicsbody category to edge ( for the edge loop) I used accelerometer to move the character and it does collide with the edge now, but then breaks through but I suspect that is to do with my accelerometer motion control right? – Peter Ruppert Jun 19 '19 at 14:01
  • 1
    @PeterRuppert My attempt at describing what the various bit masks are and what they do: A guide to collision and contactTest bit masks: https://stackoverflow.com/a/40596890/1430420 – Steve Ives Jun 19 '19 at 14:11
  • 1
    @PeterRuppert and to complete the set : Manipulating bit masks to turn individual collision and contacts off and on. https://stackoverflow.com/a/46495864/1430420 – Steve Ives Jun 19 '19 at 14:15

0 Answers0