DidBeginContact called multiple times for the same SKPhysicsBody

func didBeginContact(contact: SKPhysicsContact) { if ( contact.bodyA.categoryBitMask & BodyType.shield.rawValue ) == BodyType.shield.rawValue { contact.bodyB.node?.removeFromParent() counter++ println(counter) } else if ( contact.bodyB.categoryBitMask & BodyType.shield.rawValue ) == BodyType.shield.rawValue { contact.bodyA.node?.removeFromParent() counter++ println(counter) } } 

One physical body consists of the texture shield.physicsBody = SKPhysicsBody(texture: shieldTexture, size: shieldTexture.size())

another from the circle sand.physicsBody = SKPhysicsBody(circleOfRadius: sand.size.width/2)

When towing objects are in contact with each other, sometimes sand.physicsBody = SKPhysicsBody(circleOfRadius: sand.size.width/2) is called several times. How can I get it only to be called once for each object, even if I remove it from the parent as soon as it contacts.

+5
source share
4 answers

I figured out how to get func didBeginContact(contact: SKPhysicsContact) only once. This allows physical bodies with the SKPhysicsBody(texture: size:) count collisions once, even if in reality (due to the nature of the physical texture body) this function will be called several times.

Step 1:

Create a name property for SKSpriteNode (in this example we will use the ball) and set it to a unique name. We can do this by creating int

 var number = 0 ball.name = "ball \(number)" 

This allows you to create a unique evertime name that creates the object.

Step 2:

Create an array to store them, add a ball to the array, increase the number

  var array: [String] = [] var number = 0 ball.name = "ball \(number)" array.append(ball.name!) number ++ 

Step 3: Now in func didBeginContact(contact: SKPhysicsContact) find if the name is in the array. If it increases the score, remove the node and remove the name from the array. If the name is not in the array, do nothing.

Removing the name from the array allows us to only count the function call once.

 func didBeginContact(contact: SKPhysicsContact) { if ( contact.bodyA.categoryBitMask & BodyType.shield.rawValue ) == BodyType.shield.rawValue { var name = contact.bodyB.node?.name! let index = find(array, name!) if contains(array, name!) { score++ contact.bodyB.node?.removeFromParent() array.removeAtIndex(index!) } } } 
+2
source

there is a very simple solution if you use contactbitmask to determine which conflicts to capture.

just update the categoryBitMask object you do not want to re-discover to a new value that is not used, so the system no longer considers subsequent function calls.

+1
source

LearnCocos2D is right, SKPhysicsbody didBeginContact will call continuously while the SKphysicsbody of the two objects is in contact, because the shape we allowed in SKPhysicsBody(texture:xxx, size:xx) can have several shapes and forms.

For those who need it to detect only once, we just need to use the boolean flag as a flag to check if the detection is complete and ends with.

Here is how I do it:

  • Declare var boolean:

     var contactDone = Bool() 
  • Initialize it when the program starts (for example, below didMoveToView )

     contactDone = false 
  • Perform a check in didBeginContact :

     func didBeginContact(contact:SKPhysicsBody){ if((contact.bodyA.categoryBitMask) == scoreCategory || (contact.bodyB.categoryBitMask) == scoreCategory){ if (contactDone == false){ // Increment score score++ // Set flag to disable multiple calls by checking in didEndContact contactDone = true } } } 
  • Clear the flag so that it checks it again in didEndContact :

     func didEndContact(contact: SKPhysicsContact) { if((contact.bodyA.categoryBitMask) == scoreCategory || (contact.bodyB.categoryBitMask) == scoreCategory){ if(contactDone == true){ contactDone = false } } } 

It worked the same way as when using SKPhysicBody(circleOfRadius: object.size.height/2) .

0
source

In this case, you can work without an array. Instead of this:

 contact.bodyA.node?.removeFromParent() counter++ 

Use something like this:

 if let node = contact.bodyA.node as? SKSpriteNode { if node.parent != nil { node.removeFromParent() counter++ } } 

In the first contact, you remove the node from the parent; in subsequent calls, the code in the if statement will be skipped.

0
source

Source: https://habr.com/ru/post/1211131/


All Articles