为什么 SpriteKit 在场景之间来回转换时会崩溃

Why does SpriteKit crash when transitioning back and forth between scenes

我在这里和其他网站上检查了答案,但没有得到确定的解决方案。

我有两个场景

场景 1:

class GameMenuScene: SKScene {

override func didMoveToView(view: SKView) {

    // Add background
    var background: SKSpriteNode = SKSpriteNode(imageNamed: "Starfield")
    background.position = CGPointMake(-20, 0)
    background.size = CGSizeMake(self.size.width + 40, self.size.height)
    background.anchorPoint = CGPointZero
    background.blendMode = SKBlendMode.Replace
    self.addChild(background)

    // Add game title
    gameTitle = SKSpriteNode(imageNamed: "Title")
    gameTitle.name = "Game Title"
    gameTitle.xScale = scale
    gameTitle.yScale = scale
    gameTitle.position = CGPoint(x: CGRectGetMidX(self.frame), y: self.frame.height * 0.75)
    self.addChild(gameTitle)

    // Add scoreboard
    scoreboard = SKSpriteNode(imageNamed: "ScoreBoard")
    scoreboard.name = "Scoreboard"
    scoreboard.xScale = scale
    scoreboard.yScale = scale
    scoreboard.position = CGPoint(x: CGRectGetMidX(self.frame), y: self.frame.height * 0.50)
    self.addChild(scoreboard)

    // Add play button
    playButton = SKSpriteNode(imageNamed: "PlayButton")
    playButton.name = "PlayButton"
    playButton.xScale = scale
    playButton.yScale = scale
    playButton.position = CGPoint(x: CGRectGetMidX(self.frame), y: self.frame.height * 0.25)
    self.addChild(playButton)

    // Add menu score label
    var menuScoreLabel = SKLabelNode()
    menuScoreLabel.fontName = fontName
    menuScoreLabel.fontSize = scoreFontsize
    menuScoreLabel.text = String(score)
    menuScoreLabel.position = CGPointMake(scoreboard.position.x - (scoreboard.size.width / 4), scoreboard.position.y - (scoreboard.size.height / 4))
    menuScoreLabel.zPosition = 10
    self.addChild(menuScoreLabel)

    // Add menu top score label
    var menuTopScoreLabel = SKLabelNode()
    menuTopScoreLabel.fontName = fontName
    menuTopScoreLabel.fontSize = scoreFontsize
    menuTopScoreLabel.text = String(userDefaults.integerForKey("TopScore"))
    menuTopScoreLabel.position = CGPointMake(scoreboard.position.x + (scoreboard.size.width / 4), scoreboard.position.y - (scoreboard.size.height / 4))
    menuTopScoreLabel.zPosition = 10
    self.addChild(menuTopScoreLabel)

}

override func touchesBegan(touches: Set<NSObject>, withEvent event: UIEvent) {
    /* Called when a touch begins */

    for touch in (touches as! Set<UITouch>) {

        // Get touch location
        var touchLocation = touch.locationInNode(self)

        // Check if Play button is presssed

        if playButton.containsPoint(touchLocation) == true {

            //println("right node touched")

            // Transition to GameScene.swift
            var transition: SKTransition = SKTransition.fadeWithDuration(1)

            // Configure the view.
            let scene = GameScene()
            let skView = self.view! as SKView
            skView.showsFPS = true
            skView.showsNodeCount = true

            /* Sprite Kit applies additional optimizations to improve rendering performance */
            skView.ignoresSiblingOrder = true

            /* Set the scale mode to scale to fit the window */
            scene.size = skView.bounds.size
            scene.scaleMode = .AspectFill

            skView.presentScene(scene, transition: transition)

        }
    }
}

}

场景 2:

class 游戏场景:SKScene,SKPhysicsContactDelegate {

override func didMoveToView(view: SKView) {
    /* Setup your scene here */

    // Add background
    var background: SKSpriteNode = SKSpriteNode(imageNamed: "Starfield")
    background.position = CGPointMake(-20, 0)
    background.size = CGSizeMake(self.size.width + 40, self.size.height)
    background.anchorPoint = CGPointZero
    background.blendMode = SKBlendMode.Replace
    self.addChild(background)

    // Add mainlayer & labelHolderLayer
    self.addChild(mainLayer)
    self.addChild(labelHolderLayer)

    // Add cannon
    cannon = SKSpriteNode(imageNamed: "Cannon")
    cannon.name = "Cannon"
    cannon.position = CGPoint(x: CGRectGetMidX(self.frame), y: 0)
    self.addChild(cannon)

    // Add score label
    scoreLabel.name = "Score Label"
    scoreLabel.fontName = fontName
    scoreLabel.fontSize = scoreFontsize
    scoreLabel.text = String(score)
    scoreLabel.position = CGPointMake(CGRectGetMidX(self.frame), self.frame.size.height - scoreFontsize)
    scoreLabel.zPosition = 10
    self.addChild(scoreLabel)

    // Add LivesDisplay
    livesDisplay = SKSpriteNode(imageNamed: "Ammo5")
    livesDisplay.name = "livesDisplay"
    livesDisplay.position = CGPoint(x: self.size.width - livesDisplay.size.width, y: self.size.height - livesDisplay.size.height)
    self.addChild(livesDisplay)

    // Settings for new game
    newGame()

}

func gameIsOver() {

    // Set game over flag and stop movement
    gameOver = 1
    mainLayer.speed = 0
    mainLayer.paused = true

    // Add game over label
    gameOverLabel.fontName = fontName
    gameOverLabel.fontSize = gameOverFontsize
    gameOverLabel.text = "Game Over!"
    gameOverLabel.position = CGPointMake(CGRectGetMidX(self.frame), CGRectGetMidY(self.frame))
    gameOverLabel.zPosition = 10
    labelHolderLayer.addChild(gameOverLabel)

    // Set main menu top score
    if score > userDefaults.integerForKey("TopScore") {

        userDefaults.setInteger(score, forKey: "TopScore")
        userDefaults.synchronize()

    }

    // Run acton sequence (wait a few seconds before transitioning)
    runAction(SKAction.sequence([SKAction.waitForDuration(1), SKAction.runBlock({ () -> Void in

        // Transition back to GameMenuScene
        var transition: SKTransition = SKTransition.fadeWithDuration(1)

        // Configure the view.
        let scene = GameMenuScene()
        let skView = self.view! as SKView
        skView.showsFPS = true
        skView.showsNodeCount = true

        /* Sprite Kit applies additional optimizations to improve rendering performance */
        skView.ignoresSiblingOrder = true

        /* Set the scale mode to scale to fit the window */
        scene.size = skView.bounds.size
        scene.scaleMode = .AspectFill

        skView.presentScene(scene, transition: transition)

    })]))

}

}

当我最初启动应用程序时,我可以从场景 1 转换到场景 2。当游戏结束时,应用程序转换回场景 1。但是当我再次尝试转到场景 2 时,应用程序崩溃了.这只发生在设备上,在模拟器上,我可以毫无问题地来回走动。

我得到的错误是添加了一个已经存在的 SKNode。我知道这是什么意思,当我尝试

时错误开始了
    // Add mainlayer & labelHolderLayer
    self.addChild(mainLayer)
    self.addChild(labelHolderLayer)

但我不明白为什么它可以毫无问题地添加背景,但从那里开始崩溃。还有为什么它在模拟器上工作而不是在设备上工作?如果节点已经创建,我真的需要检查我的 didMoveToView 吗?

我发现了我犯的错误。

我在场景 2 class 之外实例化节点,使它们成为全局节点。因此,当我从场景 1 -> 场景 2 -> 场景 1 进入时没有问题,但随后再次进入场景 2 导致崩溃,因为节点是全局创建的。

解法:

在 class 中移动以下代码解决了问题。

var mainLayer = SKSpriteNode()
var labelHolderLayer = SKSpriteNode()
var livesDisplay = SKSpriteNode()
var scoreLabel = SKLabelNode()