didBeginContact is being called multiple times for the same SKPhysicsBody

吃可爱长大的小学妹 提交于 2019-11-27 03:30:31

问题


 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 physics body is from a texture shield.physicsBody = SKPhysicsBody(texture: shieldTexture, size: shieldTexture.size())

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

When the tow objects contact each other sometimes sand.physicsBody = SKPhysicsBody(circleOfRadius: sand.size.width/2) gets called multiple times. How do i get it to only get called once for each object even though i remove it from the parent as soon as it contacts.


回答1:


I have figured out how to get func didBeginContact(contact: SKPhysicsContact) to only be called once. This allows physics bodies with a texture SKPhysicsBody(texture: size:) to count collisions once even though in reality (because of the nature of the texture's physics body) this function will be called multiple times.

Step 1:

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

var number = 0 

ball.name = "ball \(number)"

This allows for a unique name evertime that object is created.

Step 2:

Create a array to hold these , append the ball to the array, increment the number

    var array: [String] = []
    var number = 0 

ball.name = "ball \(number)" 
array.append(ball.name!)
number ++

Step 3: Now in the func didBeginContact(contact: SKPhysicsContact) find out if the name is in the array. If it is increment the score, remove the node, and remove the name from the array. If the name is not in the array don't do anything.

Removing the name from the array allows us now 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:


You can make it work without the array in this case. 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++
    }
}

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




回答3:


there's a very simple solution if your using contactbitmask to determine which collisions to capture.

just update the categoryBitMask of the object you don't want to be repeatedly detected to a new value that's not being used so the system does' t consider subsequent function calls relevant anymore.




回答4:


The problem seems to occur more often when texture-based physics bodies are used. The way I got around it was to inhibit 'didBegin(_ contact: SKPhysicsContact)' from continuing if the same 'contact.bodyA.node' was indicated in the previous collision trigger. i.e.:

if lastNodeA != contact.bodyA.node {
    lastNodeA = contact.bodyA.node
    ...



回答5:


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

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

Here is how I do it:

  1. Declare a var boolean:

    var contactDone = Bool()
    
  2. Initialize it at start of program (E.g. below didMoveToView)

    contactDone = false
    
  3. Do the checking 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
    
          }
    }
    }
    
  4. Clear the flag to let it check again in didEndContact:

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

It worked just like it did when I used SKPhysicBody(circleOfRadius: object.size.height/2).



来源:https://stackoverflow.com/questions/27956637/didbegincontact-is-being-called-multiple-times-for-the-same-skphysicsbody

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!