Spritekit 游戏崩溃(因为 SKLightNode 线程问题?)

Posted

技术标签:

【中文标题】Spritekit 游戏崩溃(因为 SKLightNode 线程问题?)【英文标题】:Spritekit game crashes (because of SKLightNode threading issue?) 【发布时间】:2016-04-21 14:32:22 【问题描述】:

我正在尝试为我的孩子创建一个简单的游戏。我正在尝试实现光和发射器节点。然而,游戏在碰撞时崩溃,这应该会导致 gameOverScene。奇怪的是,如果我添加一个刹车点,代码就可以完成。代码如下:

   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")
        )
    

结果如下:

    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

但是如果我在以下行之前放置调试器断点:

print("step 1")

代码成功执行。一定是线程/同步问题,但这胜过我对 Spritekit/Swift 的了解。有人可以帮我解决这个问题吗?

【问题讨论】:

所以你是说如果你从你的代码中完全删除gotoGameOverScene(collidedBallNode!),你的游戏就不会崩溃(不管你做什么,一切都正常,例如点击屏幕等)?您对此绝对肯定吗? 如果我注释掉以下内容: if(collidedBallNode != nil) //gotoGameOverScene(collidedBallNode!) 游戏不会崩溃 也许值得一提的是我是在模拟器而不是在设备上运行。 好的,如果是这样,那么如果您以这种方式更新您的问题,我可以通过复制/粘贴代码来重现您所说的内容(例如,我不知道如何定义 lightendOfSceneCollision 变量)。或者如果您喜欢可以重现问题的最小示例,请上传 git repo。同样在touchesBegan 内部,您有touch 变量,该变量未在本地定义...因此,与其猜测,每个人都可以更容易地了解全貌... 我不认为这是一个线程问题,我有一种感觉是当你到达断点时,你在恢复时通过了 2.5 秒的等待期,你的游戏结束被触发而无需进行另一次更新迭代,您遇到了时间问题。一旦发生接触,您需要一些东西来阻止再次发生接触,否则您将排队进行多次演示游戏结束 【参考方案1】:

啊……我明白了。 removeLightNode 函数中的一段愚蠢的代码。这导致碰撞后删除不起作用:while(nodeToRemove != nil &amp;&amp; endOfSceneCollision == false)

【讨论】:

以上是关于Spritekit 游戏崩溃(因为 SKLightNode 线程问题?)的主要内容,如果未能解决你的问题,请参考以下文章

Swift,SpriteKit:释放游戏场景并重新分配新场景

返回 App SpriteKit 后按播放

SpriteKit .sks文件在分配纹理时崩溃Xcode 10

Sprite Kit 游戏在 tvOS 9.1 和 iOS 9.2 上的 Game Over 中崩溃

Spritekit 应用程序在设备上运行时崩溃,在 swift 3 转换后在模拟器上工作

canDisplayBannerAds = YES 导致 Sprite Kit 应用程序崩溃