0

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?

Crashalot
  • 33,605
  • 61
  • 269
  • 439
  • I don't know how what the real cause of your crash is so I won't post an answer, but I'll try to answer your two questions as best as I can. Regarding 1), I assume assigning a different physics body to your node would cause the original physics body to be deassociated. Regarding 2), the node property is weak and optional (to avoid a reference cycle with the owning node, I assume). The initializers of SKPhysicsBody fo not take a node as an argument; you pass the created body to a node's `physicsBody` property instead. I believe doing this in turn sets the body's `node` property accordingly. – Nicolas Miari Aug 29 '17 at 01:47
  • Nevertheless., I would assume the `contact` passed to your method (is it a collision delegate of some sort?) contains no dettached physics bodies (i.e., bodies not owned by a node); What would be the point? – Nicolas Miari Aug 29 '17 at 01:49
  • @NicolasMiari precisely, what's the point of a detached body? hence the confusion over what's happening! any suggestions? – Crashalot Aug 29 '17 at 05:56
  • I see no point; it just is logically possible due to how objects are intialized and how the API is set up. – Nicolas Miari Aug 29 '17 at 06:02
  • Perhaps you can subclass `SKNode` and/or `SKPhysicsBody`, override the involved properties with observers and try to catch when node becomes `nil`? – Nicolas Miari Aug 29 '17 at 06:06
  • Your code crashed... that means it produced a crash output... where is the crash output? Without that we are just guessing. – Fogmeister Aug 29 '17 at 15:16
  • Also... the property `node` is optional. Therefore can be nil... don't ever force unwrap optionals... your app will crash if they are nil. Where are you creating the nodes and the physics bodies? That would help more than the code in the OP. – Fogmeister Aug 29 '17 at 15:20
  • `node` on a physics body is a weak reference. You do not want this to be strong because the physics engine also keeps references to the physics body. If you remove the node, then it will not delete from memory until the physics engine was finished doing it's thing, which could take several cycles, leading to bloated memory and other issues. This is why you should use `if let` or `guard let` on your optionals unless you can 100% guarantee that your optional will never be nil. – Knight0fDragon Aug 29 '17 at 16:42

2 Answers2

1

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

Steve Ives
  • 7,894
  • 3
  • 24
  • 55
1

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
}
Fogmeister
  • 76,236
  • 42
  • 207
  • 306
Simone Pistecchia
  • 2,746
  • 3
  • 18
  • 30