'Sometimes after a collision clearly ends, didBegin contact is called last, AFTER didEnd contact - iOS / SceneKit

I have 2 nodes properly colliding with each other. One node is a set of walls and is set as dynamic. The other node is a cylinder which follows the camera in an AR view and is set as kinematic. This is using ARKit, but the collisions are between SceneKit nodes.

The collision works exactly as I want it to and the walls' node moves correctly when the cylinder collides with it.

The problem is that I need to run some code when the collision ended to re-enable a button.

In didBegin contact I hide the button as such:

func physicsWorld(_ world: SCNPhysicsWorld, didBegin contact: SCNPhysicsContact) {
    
    if contact.nodeA.physicsBody?.categoryBitMask == BodyType.wallsCategory.rawValue || contact.nodeB.physicsBody?.categoryBitMask == BodyType.wallsCategory.rawValue {
        
        freezeButton.isHidden = true
    }
}

Then in didEnd contact I re-enable the button like that:

func physicsWorld(_ world: SCNPhysicsWorld, didEnd contact: SCNPhysicsContact) {
    
    freezeButton.isHidden = false
}

And usually all of this works exactly as I wanted so the button correctly vanishes when a collision occurs and the button comes back when the collision ends.

BUT, often when the collision completes, the button does not return.

I added print statements in didBegin and didEnd and I also added similar labels on screen so I could clearly see when those functions are called. What I see is that often the last call was to didBegin even after the collision has clearly ended. I know this because I have SCNDebugOptions.showPhysicsShapes as debugOptions so I can clearly see that the cylinder is not touching the walls at all.

I then need to cause a new collision then walk back to have the didEnd function be called and get my button back.

I'm not using the didUpdate contact call as I don't need it.

How come didBegin is called last when clearly the collision has ended? What could cause this to happen like that?

And what could I do to fix this?

I am thinking of a workaround which would check if some time has passed since the last didBegin that would indicate this is an old collision. Or is there something else I could check if a collision is currently still in progress? AFAIK, SceneKit doesn't have a function similar to allContactedBodies as in SpriteKit. Is there something else I could use to check that 2 bodies are still in contact or not?

Thanks!



Solution 1:[1]

As a workaround you could probably give the End-Contact statement freezeButton.isHidden = false a small delay like with the DispatchQueue. This will re-enable the button on the next Run-Loop. This might not be the perfect solution, but it could give you better results than now. Give it a try.

DispatchQueue.main.async { self.freezeButton.isHidden = false }

In addition you could check within the Begin-Contact if the Button is not hidden already, and only hide it if needed.

if !freezeButton.isHidden { freezeButton.isHidden = true }

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 ZAY