SpriteKit crash: how can physics body lose association with SKNode?

107 views Asked by At

This code crashes because secondBody is not associated with a node.

func didBegin(_ contact: SKPhysicsContact) {
    // Set vars to hold bodies
    var firstBody: SKPhysicsBody
    var secondBody: SKPhysicsBody

    // Sort bodies
    if contact.bodyA.categoryBitMask < contact.bodyB.categoryBitMask {
        firstBody = contact.bodyA
        secondBody = contact.bodyB
    } else {
        firstBody = contact.bodyB
        secondBody = contact.bodyA
    }

    // Handle different contacts
    if firstBody.categoryBitMask == BallBitMask && secondBody.categoryBitMask == TileBitMask {
        didBallTileHit(tile: secondBody.node!) // Code crashes here :(
    }

    ...
}

1) Aside from setting a physics body to nil, what would cause a physics body to lose association with a node in SpriteKit?

2) How can a physics body even exist without a node in the first place?

2

There are 2 answers

1
Steve Ives On BEST ANSWER

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

0
Simone Pistecchia On

You can handle the contact in different methods.

The easier way for you is to put this:

func didBegin(_ contact: SKPhysicsContact) {

    let bodyA = contact.bodyA
    let bodyB = contact.bodyB

    guard let nodeA = bodyA.node as? SKSpriteNode,
          let nodeB = bodyB.node as? SKSpriteNode,
          let parentNodeA = nodeA.parent,
          let parentNodeB = nodeB.parent
    else {return}

    //all your stuff
}