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 i
The reason why the didBeginContact is being fired multiple times is because you have multiple contact points happening on concave shapes.
If you look at the picture below, you will see I have 2 sprites, a black star and a red rectangle. When the black star hits the red rectangle, it hits it on multiple points, circled in blue. Sprite Kit will then do a call for each line intersection, so that the developer can use the contactPoint
variable for each of these contacts.
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.
I figured out easy solution:
Just change either body's categoryBitMask value to 0 or non-used value right after it detected contact.
For example:
if (firstBody.categoryBitMask == padCategory && secondBody.categoryBitMask == colorBallCategory) {
secondBody.categoryBitMask = 0;
// DO OTHER THING HERE
}
In my experience, didEndContact & didBeginContact are both called multiple times while the objects overlap. This is also happening in SceneKit using iOS 9, so I have to assume it's an intended behavior.
Here is an option that makes the player invulnerable after being hit for a set time:
A. Create a variable that makes the player invulnerable to losing a life after being hit for a few seconds.
If isInvuln is false, take a life then set isInvuln to true.
if(self.isInvuln == FALSE){
self.player.lives-=1;
self.isInvuln = True;}
Add to your updateWithCurrentTime:
if(self.isInvuln==True){
self.invulnTime += timeSinceLast;}
if (self.invulnTime > 3) {
self.isInvuln = FALSE:}
self.invulnTime= 0;
This will make it so that when an enemy and player collide, the player loses a life and becomes invulnerable 3 seconds. After that 3 seconds, the player can take damage again. If the enemy contacts the player within the 3 invulnerable seconds, the contact method does nothing. Hope this helps spark ideas to tackle your problem.
I came across the same issue. In my case the didBeginContact()
was called many times (I counted up to 5 times) for one contact of a bullet with the enemy. As the bullet is a simple circle format, I agree with @SFX that it cannot be a bug just in Texture-Bodies. The tests have shown that there was no call to update()
between the didBeginContact()
calls. So the solution is simple (Swift):
var updatesCalled = 0
...
internal update() {
updatesCalled ++
}
...
internal func didBeginContact(contact: SKPhysicsContact) {
NSLog("didBeginContact: (\(contact.contactPoint.x), \(contact.contactPoint.y)), \(updatesCalled)")
if(updatesCalled == 0) {return} // No real change since last call
updatesCalled = 0
... your code here ...
}
I tried didEndContact()
but that was not called at all. I didn't investigate further into this.
BTW: I just switched from Android, and I'm impressed by the easiness and stability of this System :-)