someName someName - 1 year ago 134
iOS Question

Why are didBeginContact called multiple times?

In an iOS game that uses Sprite Kit along with the contact detection in Sprite Kit's build-in physics engine, I decrease the Hero's number lives by one each time he gets in contact with an enemy. This is done from the

However, it seems like that method is not just called once, when the contact begins, but called continuously as long as the Hero and the enemy overlaps: when I set a breakpoint in that method, I can see, that it is the exact same physics body instances that exist as
. The result is, that the Hero will lose multiple lives, even though he only passes one single enemy.

If the Hero meets the same enemy again later, he should get one more live subtracted, and therefore I cannot just maintain a
hash set to deal with the problem above.

The question is now: how would you make sure that only one live is subtracted for each Hero/enemy contact?

Answer Source

I had the same problem (score increasing multiple times for a single enemy destroyed and multiple life points being lost for a single instance of damage.) A user on the Apple forums thinks that it's a bug in [SKPhysicsBody bodyWithTexture:size:] but I don't believe that's the case, because it was happening with other constructors too.

First off, the categoryBitMask and contactTestBitMask are very important, obviously. Take a look at Apple's SpriteKit Physics Collisions sample code:

// Contacts are often a double dispatch problem; the effect you want is based on the type of both bodies in the contact. This sample this in a brute force way, by checking the types of each. A more complicated example might use methods on objects to perform the type checking.

// The contacts can appear in either order, and so normally you'd need to check each against the other. In this example, the category types are well ordered, so the code swaps the two bodies if they are out of order. This allows the code to only test collisions once.

What I did to solve it was setting a flag after handling each condition. In my case, I was testing whether bodyA.node.parent was nil in didBeginContact, because I called removeFromParent() on the missile/enemy nodes to destroy them.

I think you should expect the event to fire multiple times and your code in there has to make sure it's processed only once.