1

I'm trying to create a simple game for my Kids. I'm trying to implement light and emitter nodes. However the game crashes on collision which should result to gameOverScene. The strange thing is that the code completes ok if I add a brakepoint. Here's the code:

   override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
            touch = touches.first!
            turnOnLightNode(touch.locationInNode(self))   
        }

        override func touchesMoved(touches: Set<UITouch>, withEvent event: UIEvent?) {
            touch = touches.first!
            turnOnLightNode(touch.locationInNode(self))
    }
        override func touchesEnded(touches: Set<UITouch>, withEvent event: UIEvent?) {
            removeLightNode()
        }
        func removeLightNode(){
            var nodeToRemove = self.childNodeWithName("light")
            while(nodeToRemove != nil && endOfSceneCollision == false) {
                nodeToRemove?.removeFromParent()
                nodeToRemove = self.childNodeWithName("light")
            }
            nodeToRemove = self.childNodeWithName("touchEmitterNode")
            while(nodeToRemove != nil && endOfSceneCollision == false) {
                nodeToRemove?.removeFromParent()
                nodeToRemove = self.childNodeWithName("touchEmitterNode")
            }
        }
        override func touchesCancelled(touches: Set<UITouch>?, withEvent event: UIEvent?) {
            removeLightNode()
        }
        func turnOnLightNode(point: CGPoint){

            removeLightNode()

            light.name = "light"
            light.categoryBitMask = 3
            light.position = point
            light.zPosition = 19.0
            light.falloff = 0.5
            light.enabled = true
            light.lightColor = UIColor(red:  161/255, green: 218/255, blue: 237/255, alpha: 0.5)
            light.shadowColor = UIColor(red:  161/255, green: 218/255, blue: 237/255, alpha: 0.5)
            light.ambientColor = UIColor(red:  220/255, green: 220/255, blue: 220/255, alpha: 0.3)

            addChild(light)

            touchEmitter!.name = "touchEmitterNode"
            touchEmitter!.position = point
            touchEmitter!.zPosition = 100//gameFieldParticlesZPosition
            addChild(touchEmitter!)   
        }

func didBeginContact(contact: SKPhysicsContact) {        
        var ballBody: SKPhysicsBody?
        var lineBody: SKPhysicsBody?
        var collidedBallNode: SKSpriteNode?

        if contact.bodyA.categoryBitMask == 2 && contact.bodyB.categoryBitMask == 1 {
            print("didBeginContactAB")

            lineBody = contact.bodyA
            ballBody = contact.bodyB
            collidedBallNode = contact.bodyB.node as? SKSpriteNode
        }
        if contact.bodyA.categoryBitMask == 1 && contact.bodyB.categoryBitMask == 2 {
            print("didBeginContactBA")

            lineBody = contact.bodyB
            ballBody = contact.bodyA
            collidedBallNode = contact.bodyA.node as? SKSpriteNode
        }
        if contact.bodyA.categoryBitMask == 1 && contact.bodyB.categoryBitMask == 4 {
            //audioController.playSound(electricBounceSound, volume: 1.0)
        }
        if contact.bodyA.categoryBitMask == 4 && contact.bodyB.categoryBitMask == 1 {
            //audioController.playSound(electricBounceSound, volume: 1.0)
        }
        if contact.bodyA.categoryBitMask == 1 && contact.bodyB.categoryBitMask == 1 {
            //audioController.playSound(electricNoiseSound, volume: 1.0)
        }

        if(collidedBallNode != nil){
            gotoGameOverScene(collidedBallNode!)

        }
    }
func gotoGameOverScene(explodingBallNode: SKSpriteNode){
        print("step 1") //IF BREAKPOINT HERE, ALL EXECUTES OK           
        self.runAction(SKAction.waitForDuration(2.5), completion: {
            print("step 2")
            let gameOverScene = GameOverScene(size: self.size) 
            print("step 3")
            self.view?.presentScene(gameOverScene, transition: reveal)
            print("step 4")
        })
    }

And it results to this:

    didBeginContactAB
    step 1
Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: 'Attemped to add a SKNode which already has a parent: <SKLightNode> name:'light' position:{296.99994, 191.49995} scale:{1.00, 1.00} accumulatedFrame:{{297, 191.5}, {0, 0}}'
    *** First throw call stack:
    (
        0   CoreFoundation                      0x01384a14 __exceptionPreprocess + 180
        1   libobjc.A.dylib                     0x00b5de02 objc_exception_throw + 50
        2   CoreFoundation                      0x0138493d +[NSException raise:format:] + 141

BUT if I put debugger breakpoint before the following line:

print("step 1")

Code executes succesfully. Must be a thread / synchronization issue but this beats my knowhow over Spritekit / Swift. Could someone help me with fixing this?

user594883
  • 1,329
  • 2
  • 17
  • 36
  • So you are saying that if you completely remove `gotoGameOverScene(collidedBallNode!)` from your code, your game works without this crash (everything works no matter what you do, eg. tapping on screen etc) ? Are you absolutely positive about that? – Whirlwind Apr 21 '16 at 14:49
  • if I comment out the following: if(collidedBallNode != nil){ //gotoGameOverScene(collidedBallNode!) } The game doesn't crash – user594883 Apr 21 '16 at 15:04
  • Perhaps worth mentioning that I'm running in simulator not on device. – user594883 Apr 21 '16 at 15:05
  • Okay, if so, then it would be useful if you update your question in that way, that I can reproduce what you are saying by copy / and pasting the code (for example, I don't know how `light` or `endOfSceneCollision` variables are defined). Or upload a git repo if you like with minimal example which can reproduce the issue. Also inside of `touchesBegan` you have `touch` variable, which is not defined locally... So, instead of guessing, it could be easier for everybody to have a whole picture... – Whirlwind Apr 21 '16 at 15:07
  • 1
    I do not think this is a threading issue, I have a feeling what is going on is when you hit the breakpoint, you pass the 2.5 second wait period on resume and your gameover is fired without having to go through another iteration of update, you have a timing issue going on. Once contact happens, you need something that stops contact from happening again, otherwise you will queue up multple runs of presenting game over – Knight0fDragon Apr 21 '16 at 17:32
  • Hmm...I added a bit into gotoGameOverScene to stop the gameoverscene from firing twice but still the same issue...It's something about timing but I can't figure out what since I'm trying to remove lightnodes from everywhere before I adding them again. – user594883 Apr 22 '16 at 03:12
  • Ahhh...I got it. Stupid piece of code at removeLightNode function. This caused remove not to work after collision: while(nodeToRemove != nil && endOfSceneCollision == false) – user594883 Apr 22 '16 at 03:23

1 Answers1

0

Ahhh...I got it. Stupid piece of code at removeLightNode function. This caused remove not to work after collision: while(nodeToRemove != nil && endOfSceneCollision == false)

Whirlwind
  • 14,286
  • 11
  • 68
  • 157
user594883
  • 1,329
  • 2
  • 17
  • 36