Sprite-Kit registering multiple collisions for single contact
OK - it would appear that a simple:
if bomb == nil {return}
is all that's required. This should be added as follows:
let bomb = contact.bodyA.categoryBitMask == category.bomb.rawValue ? contact.bodyA.node : contact.bodyB.node
if bomb == nil {return}
This works to prevent multiple collisions for a node that you removeFromParent
in didBeginContact
. If you don;t remove the node but are still registering multiple collisions, then use the node's userData
property to set some sort of flag indicating that the node i s'anactive' Alternately, subclass SKSPriteNode and add a custom 'isActive' property, which is what I did to solve my problem of bullets passing up the side of an invader and taking out that invader and the one above it. This never happens on a 'direct hit'.
It doesn't answer the underlying question as to why SK is registering multiple collisions, but it does mean that I can remove all the extra code concerning setting contactTestBitMasks to 0 and then back to what they should be later etc.
Edit: So it appears that if you delete a node in didBeginContact, the node is removed but the physics body isn't. So you have to be careful as Sprite-Kit appears to build an array of physicsContacts that have occurred and then calls dBC multiple times, once for each contact. So if you are manipulating nodes and also removing them in dBC, be aware that you might run into an issue if you force unwrap a node's properties.
Why are didBeginContact called multiple times?
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.
Collision detection leading to color detection?
My suggestion is that your change your "pair" of walls to become a trio of walls instead. This third wall should have a different categoryBitMask. (PhysicsCategory.wallSpace
seems to fit with your current naming scheme.)
This "wallSpace" needs to be positioned between the two existing walls. It should be given the same color as its siblings through your color-change logic, but here comes the trick: set it's alpha to 0.
This way you can check for collisions between this invisible wall and your ball and perform actions based on the color-information.
SpriteKit crash: how can physics body lose association with SKNode?
An SKSPhysicsBody is an object in itself and perfectly capable of being created yet not associated with an SKSpriteNode (although perhaps not of much use :-) )
In your didBallTileHit()
, do you removeFromParent()
any of the nodes? If so, this is probably the cause of your crash in that Sprite-Kit has generated multiple collisions between 2 objects and is calling didBegin()
several times for the 2 nodes. If you then go and remove one of the nodes in the 1st call to didBegin()
, it won't be there for the 2nd and subsequent calls.
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).
For more detail and possible solutions, see this answer : https://stackoverflow.com/a/44384390/1430420
SpriteKit's didBeginContact can't be called
Find out the reason. Need use
s1.physicsBody!.dynamic = true
s2.physicsBody!.dynamic = true
Related Topics
How to Open to a Specific View Using Home Quick Actions
Generics and Functional Programming in Swift
Scenekit Shape Between 4 Points
Creating a Subclass of Skshapenode
Way to Purge All But One Object Types (Models) in a Realm
In Swift, How to Wait Until a Server Response Is Received Before I Proceed
How to Find the Time Interval Remaining from Nstimer
How to Set Width and Height of an Image in Swiftui
Swift: Uploading Image to Firebase Cloud Storage When User Closes App
Swift: Search Bar Created at Auto Focus
Perform Migration by Adding List() and Another Model Class
Http Request Swift Providing Parameters
Make Touch-Area for Sklabelnode Bigger for Small Characters
Swift Delegate Beetween Two Vc Without Segue
Why Do We Need to Set Delegate to Self? Why Isn't It Defaulted by the Compiler
Why Is Casting a Struct to Anyobject Not a Compile Error in Swift
How to Use Uiimage(Contentsoffile:String) Method to Load Images from Images.Xcassets Folder