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!
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?