3

AppImage I have a wall of 4 rectangles that are different colors, to pass through the wall the color of the ball has to match that of the rectangle on the wall. The ball will pass through the wall, and a new wall will appear. However, when I detect this collision I get multiple collision readings. I have tested this by printing dead or alive, and it prints both or more many times.

func didBegin(_ contact: SKPhysicsContact) {

    if let nodeA = contact.bodyA.node as? SKShapeNode, let nodeB = contact.bodyB.node as? SKShapeNode {
        if nodeA.fillColor != nodeB.fillColor {
            print("DEAD")
        }
        else {
            print("Alive")
        }
    }
}      

please help!!!

raeman23
  • 51
  • 1

1 Answers1

3

Yep - this happens. The way to handle it (you can't get sprite-kit to NOT call didBegin multiple times in some circumstances) is to make sure that your contact code accommodates this and that handling the contract multiple times does not cause a problem (such as adding to the score multiple times, removing multiple lives, trying to access a node or physicsBody that has been removed etc).

There is a discussion here: Sprite-Kit registering multiple collisions for single contact

Some things you can do include:

  • If you remove a node that is contacted, check for it being nil before you remove it (for the duplicate contacts)
  • Add the node to a set and then remove all the nodes in the set in didFinishUpdate
  • Add an 'inactive' flag' to the node's userData
  • Make the node a subclass of SKSpriteNode and add an inactive property
  • Etc etc.
Steve Ives
  • 7,894
  • 3
  • 24
  • 55
  • Like pointed, removing the nodes outside the `didBegin(contact:)` later on in `didFinishUpdate` or `didSimulatePhysics` is a proper way to go, and will prevent from having a physics bodies without their nodes which may lead into crashes. Also, @steveives you don't need inactive flag IMO. You may have only one array to store nodes for removal. – Whirlwind Jun 06 '17 at 09:04
  • @Whirlwind Does adding the nodes to a set to be removed in `didFinishUpdate` resolve the problem of adding to a score multiple times, or loosing multiple lives, or do you haver to move that logic to `didFinishUpdate` too? – Steve Ives Jun 06 '17 at 10:03
  • Well it can solve that part I guess...You would have to check if the node is not in the array in order to take an appropriate action ( decrease / increase number of lives, add points to overall score etc.) I mean your method works as well, perfectly fine. – Whirlwind Jun 06 '17 at 11:20
  • Ok - i wasn't sure of the order but I think that I tested it and got multiple `didBegin` calls (for the same pair of contacts) within the same game loop. I guess now iOS 11 is out I should see if this is still an issue, – Steve Ives Jun 06 '17 at 12:08
  • 1
    I like using bit 31 on the categoryBitMask to represent items that are dying – Knight0fDragon Jun 06 '17 at 13:20
  • in your didBegin, if bit 31 is set, them return, because this is a phantom collision – Knight0fDragon Jun 06 '17 at 13:21
  • @SteveIves yup, a lot faster than having to do a hash grab on a dictionary, simple bitwise math (This is pseudocode): `guard(~(categoryBitMask & (1 << 31))) else return` – Knight0fDragon Jun 06 '17 at 20:51