1

SITUTATION:

Already searched extensively, didn't find a solution.

For example, this does not help: Swift/SpriteKit Multiple Collision Detection?

PROBLEM:

My code works fine except when the player hits two different PhysicsCategory Objects at the same time (falls and hits exactly the ground and a wall at the same time for example). This makes my app crash.


CODE:

struct PhysicsCategory {
    static let player: UInt32 = 0x1 << 1
    static let ground: UInt32 = 0x1 << 2
    static let wall: UInt32 = 0x1 << 3
    static let scoreWall: UInt32 = 0x1 << 4
}

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

    if firstBody.categoryBitMask == PhysicsCategory.scoreWall && secondBody.categoryBitMask == PhysicsCategory.player || firstBody.categoryBitMask == PhysicsCategory.player && secondBody.categoryBitMask == PhysicsCategory.scoreWall {

        score += 1
        scoreLabel.text = "\(score)"

        updatePlayerColor()

    }

    else if firstBody.categoryBitMask == PhysicsCategory.wall && secondBody.categoryBitMask == PhysicsCategory.player || firstBody.categoryBitMask == PhysicsCategory.player && secondBody.categoryBitMask == PhysicsCategory.wall {

        lost = true

        self.scene?.speed = 0

        gameVC?.dismissViewControllerAnimated(false, completion: nil)

        //playDeathAnimation with completion block self.dismiss(VC)
    }

    else if firstBody.categoryBitMask == PhysicsCategory.ground && secondBody.categoryBitMask == PhysicsCategory.player || firstBody.categoryBitMask == PhysicsCategory.player && secondBody.categoryBitMask == PhysicsCategory.ground {

        lost = true

        self.scene?.speed = 0

        gameVC?.dismissViewControllerAnimated(false, completion: nil)

        //playDeathAnimation with completion block self.dismiss(VC)
    }
}

Debugger Error:

Fatal Error: Index out of range.

What I am looking for:

I just want to know if the double collision is accounted for in my code already or not. The debugger error refers to some other part of my code that is influenced by the collisions. But this would be too much code to add here. So please just answer on the following: how to account for double collisions in my SpriteKit game between ScoreWall and Wall ?

Community
  • 1
  • 1
Coder1000
  • 4,071
  • 9
  • 35
  • 84
  • I would suggest sorting your bit masks before any further processing to avoid code duplication – michaelsnowden May 26 '16 at 07:44
  • @michaelsnowden What do you mean ? – Coder1000 May 26 '16 at 07:45
  • Check out my answer – michaelsnowden May 26 '16 at 07:54
  • show error to understand – Simone Pistecchia May 26 '16 at 08:49
  • What line is the error occurring on? Can you add a stack trace? – Ashley Mills May 26 '16 at 08:53
  • I gave you the debugger error, but it won't help in this case because it refers to some other part of my code (and I would have to add too much code for this error to be interpreted). The only thing I am asking here is the following: is the double collision the cause of this (as I saw in my playtests, so must be the case) and how to solve this double collision case ? This is why I didn't give the debugger error. No stack trace here sadly :/ If the double collision is not the cause of this, I will know I need to look somewhere else in my code, but that is out of the scope of this question. – Coder1000 May 26 '16 at 08:54
  • 1
    You can control the traffic in your didBeginCounter like explained in this post http://stackoverflow.com/a/26688404/1894067 (using a counter or a boolean flag) to be absolutely sure you had just only one collision for example and use this flag to dismissViewControllerAnimated launched in a protocol/delegate method or in the update method (class basic method). – Alessandro Ornano May 27 '16 at 07:22

3 Answers3

1

Try something like this? It should remove your code duplication and prevent your game over logic from occurring twice.

func didBeginContact(contact: SKPhysicsContact) {
    if (lost) {
        return
    }
    var a = contact.bodyA.categoryBitMask
    var b = contact.bodyB.categoryBitMask
    if a > b {
        swap(&a, &b)
    }

    if a == PhysicsCategory.player && b == PhysicsCategory.scoreWall {

        score += 1
        scoreLabel.text = "\(score)"

        updatePlayerColor()

    } else if a == PhysicsCategory.player && (b == PhysicsCategory.ground || b == PhysicsCategory.wall) {

        lost = true

        self.scene?.speed = 0

        gameVC?.dismissViewControllerAnimated(false, completion: nil)

        //playDeathAnimation with completion block self.dismiss(VC)
    }
}
michaelsnowden
  • 6,031
  • 2
  • 38
  • 83
1

What kind of error do you have?

Anyway, you may have to wait the end of physics simulation before removing important parts (don't know the whole logic of your app). So setting the lost boolean on condition in collision detection is good, but the remaining may must be done either in didSimulatePhysics or didFinishUpdate.

Jean-Baptiste Yunès
  • 34,548
  • 4
  • 48
  • 69
  • When the player hits scoreWall and wall at the same time (in the middle between them), the app crashes ! – Coder1000 May 26 '16 at 08:40
  • Ok I think we understood that your app crash, but where exactly, what is the error, etc. Use the debugger and give us some more information. *My app crashes* is not a good information to help you. – Jean-Baptiste Yunès May 26 '16 at 08:42
  • I gave you the debugger error, but it won't help in this case because it refers to some other part of my code (and I would have to add too much code for this error to be interpreted). The only thing I am asking here is: is the double collision the cause of this (as I saw in my playtests, so must be the case) and how to solve this double collision case ? This is why I didn't give the debugger error. – Coder1000 May 26 '16 at 08:52
  • 2
    You don't have a double collision. You'll have 2 single collisions i.e. didBeginContact will be called once for the collision between the player and the ground and again for the collision between the player and the wall. An SKPhysicsContact can only have 2 bodies. Your crash is caused by something else, perhaps by the removal of the gameVC? – Steve Ives May 26 '16 at 09:32
  • @SteveIves Bingo ! It's not the GameVC, but you answered my question. Please provide your comment as an answer so I can accept it :) I now know where to look for the crash and will ask a different question about this. – Coder1000 May 26 '16 at 10:09
  • @Jean-BaptisteYunès Yes, but you didn't explain why it was certain there was no double collision :) Do not worry, everyone will get an upvote regardless to thank you for taking the time to help me :) – Coder1000 May 26 '16 at 10:12
  • @Coder1000 - answer added. – Steve Ives May 26 '16 at 10:25
1

You don't have a double collision. You'll have 2 single collisions i.e. didBeginContact will be called once for the collision between the player and the ground and again for the collision between the player and the wall. An SKPhysicsContact can only have 2 bodies. Your crash is caused by something else, perhaps by the removal of the gameVC?

For an example of a 'cleaner' version of didBeginContact, try this:

 func didBeginContact(contact: SKPhysicsContact) {

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

        switch contactMask {
        // If the player and the scoreWall have collided:
        case .player | .scoreWall :
           score += 1
           scoreLabel.text = "\(score)"
           updatePlayerColor()

        // If the player and the wall have collided:
        case .player | .wall :
           lost = true
           self.scene?.speed = 0
           gameVC?.dismissViewControllerAnimated(false, completion: nil)

        // If the player and the ground have collided:
        case .player | .ground :
           lost = true
           self.scene?.speed = 0
           gameVC?.dismissViewControllerAnimated(false, completion: nil)

        default :
           print("Some other contact has occurred")
        }
Steve Ives
  • 7,894
  • 3
  • 24
  • 55