1

Before I'm crucified with downvotes let me say I have done research into this and I still cannot understand why I am getting this error.

I have a core that the player is trying to defend and you can shoot little lasers out from it to defend against incoming meteors. Well, I have everything set up and working (most of the time anyways), but every once and while when a laser hits a meteor and my collision handling function tries to remove the shot node and meteor node, it throws this error:

fatal error: unexpectedly found nil while unwrapping an Optional value

Again I have done a lot of digging into this and I cannot seem to figure it out.

Here's my didBegin:

func didBegin(_ contact: SKPhysicsContact) {
    if (contact.bodyA.node?.name == "shot") { // shot A
        collisionBetween(first: (contact.bodyA.node)!, second: (contact.bodyB.node)!)
    } else if (contact.bodyB.node?.name == "shot") { // shot B
        collisionBetween(first: (contact.bodyB.node)!, second: (contact.bodyA.node)!)
    }
    if (contact.bodyA.node?.name == "core") { // core A
        collisionBetween(first: (contact.bodyA.node)!, second: (contact.bodyB.node)!)
    } else if (contact.bodyB.node?.name == "core") { // core B
        collisionBetween(first: (contact.bodyB.node)!, second: (contact.bodyB.node)!)
    }

}

and here's my collisionBetween function:

func collisionBetween(first: SKNode, second: SKNode) {

    if first.name == "shot" && second.name == "sMeteor" {
        first.removeFromParent()    // these two only throw the error
        second.removeFromParent()   // every once and a while
    } else if first.name == "sMeteor" && second.name == "shot" {
        first.removeFromParent()    // these two throw the error also
        second.removeFromParent()   // but only on occasion
    } else if first.name == "mMeteor" && second.name == "shot" {
        second.removeFromParent()
    } else if first.name == "shot" && second.name == "mMeteor" {
        first.removeFromParent()
    }
    if first.name == "core" && second.name == "sMeteor" {
        second.removeFromParent() //always throws the error
    } else if first.name == "sMeteor" && second.name == "core" {
        first.removeFromParent() //always throws the error
    }

}

I've been trying to figure this error out for a while now, and I feel like I have a basic understanding of optionals. These errors are only thrown when I try to remove the nodes from the parent self and I have tried many different approaches to solving this problem but cannot for the life of me figure it out. Any help appreciated, thanks!

Steve Ives
  • 7,894
  • 3
  • 24
  • 55
B. Holder
  • 37
  • 1
  • 4

1 Answers1

2

I strongly suspect that this is caused by SpriteKit generating multiple contact events for the same contact. (i.e. it's calling didBegin()) 2 or more times with the same bodyA & bodyB)

The first time it is called, everything is fine; the second time, things have been removed and some nodes and/or bodies are nil.

Check this answer to see if it helps : Sprite-Kit registering multiple collisions for single contact

If you'd put some print statements in your didBegin() e.g.

func didBegin(_ contact: SKPhysicsContact) {
    print("Contact between \(contact.bodyA.node?.name) & \(contact.bodyB.node?.name)")
    if (contact.bodyA.node?.name == "shot") { // shot A

you'd probably have seen in the log:

Contact between shot and sMeteor
Contact between shot and sMeteor
Contact between shot and sMeteor

when only 1 contact has occurred.

Also, your didBegin() and collisionBetween()functions look overly complicated (plus collisionBetween should really be called contactBetween()).

I find a neater way to code didBegin() is :

 func didBegin(contact: SKPhysicsContact) {
            let contactMask = contact.bodyA.categoryBitMask | contact.bodyB.categoryBitMask

            switch contactMask {

            case categoryBitMask.shot | categoryBitMask.sMeteor:
               print("Collision between shot and sMeteor")
               contact.bodyA.removeFromParent()
               contact.bodyB.removeFromParent()

            case categoryBitMask.shot | categoryBitMask.mMeteor:
               print("Collision between shot and mMeteor")
               let shot = contact.bodyA.categoryBitMask == categoryBitMask.shot ? contact.bodyA.node! : contact.bodyB.node!
               shot.removeFromParent()

            case categoryBitMask.core | categoryBitMask.sMeteor:
               print("Collision between core and sMeteor")
               let meteor = contact.bodyA.categoryBitMask == categoryBitMask.sMeteor ? contact.bodyA.node! : contact.bodyB.node!
               meteor.removeFromParent()

            default :
               //Some other contact has occurred
               print("Some other contact")
        }  
    }

This is really only safe if your nodes only belong to one category at a time, which is up to you to determine.

What happens if 'core' and 'mMeteor' make contact?

Community
  • 1
  • 1
Steve Ives
  • 7,894
  • 3
  • 24
  • 55
  • 1
    I suspected as much!! When I attempted to debug this I was getting multiple calls to didBegin but I did not know how to resolve it. Thank you very much I'll definitely check out the answer you linked. Also thanks for cleaning up my collisionBetween function, it was rather confusing and your method does clean it up a bunch. Thanks so much! – B. Holder Mar 17 '17 at 15:21