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

Posted

技术标签:

【中文标题】Swift,SpriteKit:释放游戏场景并重新分配新场景【英文标题】:Swift, SpriteKit: Deallocate a Gamescene et reallocate a new one 【发布时间】:2016-08-04 18:19:46 【问题描述】:

我需要在玩家完成一个关卡后释放游戏场景,以便内存可用于加载我的游戏的另一个关卡。如果我不这样做,我的应用程序会因为内存问题而崩溃。

我已按照那里给出的指示:

Swift: Deallocate GameScene after transition to new scene?

但不幸的是,它对我不起作用。我在我的 GameViewController 类中收到错误“无法将值 'UIView' 转换为 'SKView'。这是我的整个代码:

import Foundation
import UIKit
import SpriteKit

class GameViewController: UIViewController 

    var scene1: SKScene?
    var scene2: SKScene?
    var scene3: SKScene?
    var skView: SKView?


    func nextSceneAction1() 

        scene2 = nil
        scene3 = nil

        skView! = self.view as! SKView
        skView!.showsFPS = true
        skView!.showsNodeCount = true

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

        scene1 = TransitionSigns(size: skView!.frame.size)
        scene1!.size.width = 2048
        scene1!.size.height = 1536


        /* Set the scale mode to scale to fit the window */
        scene1!.scaleMode = .AspectFill

        let transition = SKTransition.revealWithDirection(.Right, duration: 2)

        scene1!.scaleMode = .AspectFill
        skView!.presentScene(scene1!, transition: transition)
    

    func nextSceneAction2() 

        scene1 = nil
        scene3 = nil

        let skView = self.view as! SKView
        skView.showsFPS = true
        skView.showsNodeCount = true

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

        scene2 = TransitionSigns(size: skView.frame.size)
        scene2!.size.width = 2048
        scene2!.size.height = 1536


        /* Set the scale mode to scale to fit the window */
        scene2!.scaleMode = .AspectFill

        let transition = SKTransition.revealWithDirection(.Right, duration: 2)

        scene2!.scaleMode = .AspectFill
        skView.presentScene(scene2!, transition: transition)
    

    func nextSceneAction3() 

        scene1 = nil
        scene2 = nil

        let skView = self.view as! SKView
        skView.showsFPS = true
        skView.showsNodeCount = true

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

        scene3 = TransitionSigns(size: skView.frame.size)
        scene3!.size.width = 2048
        scene3!.size.height = 1536


        /* Set the scale mode to scale to fit the window */
        scene3!.scaleMode = .AspectFill

        let transition = SKTransition.revealWithDirection(.Right, duration: 2)

        scene3!.scaleMode = .AspectFill
        skView.presentScene(scene3!, transition: transition)
    


    override func viewWillLayoutSubviews() 
        // Configure the view.

        let skView = self.view as! SKView
        skView.showsFPS = true
        skView.showsNodeCount = true

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

        let scene = MainView(size: skView.frame.size)
        scene.size.width = 2048
        scene.size.height = 1536

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

        skView.presentScene(scene)
    

    override func shouldAutorotate() -> Bool 
        return true
    

    override func supportedInterfaceOrientations() ->   UIInterfaceOrientationMask 
        if UIDevice.currentDevice().userInterfaceIdiom == .Phone 
            return UIInterfaceOrientationMask.AllButUpsideDown
         else 
            return UIInterfaceOrientationMask.All
        
    

    override func didReceiveMemoryWarning() 
        super.didReceiveMemoryWarning()
        // Release any cached data, images, etc that aren't in use.
    

    override func prefersStatusBarHidden() -> Bool 
        return true
    


    override func viewDidLoad() 
        super.viewDidLoad()

    

    override func viewDidDisappear(animated: Bool) 

        self.view.removeFromSuperview()
    

    override func viewWillDisappear(animated: Bool)
        self.view.removeFromSuperview()
    

我从我的其他类中调用不同的函数nextSceneAction1()nextSceneAction2()nextSceneAction3()

let controller1 = GameViewController()
controller1.nextSceneAction1()

let controller2 = GameViewController()
controller2.nextSceneAction1()

加载新场景时,我收到此错误:“无法将 'UIView' 类型的值转换为 'SKView'。你知道这段代码有什么问题吗?

顺便说一句,我愿意在加载新场景之前从内存中清除旧场景,以便在应用程序的生命周期内不会发生内存崩溃。

【问题讨论】:

【参考方案1】:

一般来说,一旦您转换到新场景,SKScene 会自动解除分配,除非您有内存泄漏,因此通常您不需要做任何特别的事情来释放内存。

要查看您的场景是否已解除分配,您可以添加 deinit 方法

  func deinit 
      print("scene did deallocate")
  

如果这个方法在你改变场景时被调用,你就知道所有的东西都被正确释放了,如果它没有被调用,你就有内存泄漏。

您的代码也有问题,每次更改场景而不是访问当前实例时,您都会创建一个新的 GameViewController 实例。

 let controller2 = GameViewController() // creates new instance of GameViewController
 controller2.nextSceneAction1()

您通常只从 GameViewController 加载第一个场景,而所有其他场景更改都可以直接在您所在的 SKScene 中完成。不要为此使用 GameViewController。

在您当前的 SKScene 中,您可以像这样更改为新场景(例如从 GameScene 到 MenuScene)

 class GameScene: SKScene 

      ...


       func loadMenuScene() 

           let menuScene = MenuScene(size: self.size) // use size of current scene          
           let transition = Some SKTransition
           view?.presentScene(menuScene, withTransition: transition)
        
 

希望对你有帮助

【讨论】:

谢谢,看起来精灵表中的所有纹理都保留在内存中,即使场景已从屏幕上消失。我已经成功地通过将所有 2040X2048 纹理替换为 160 字节 10X10 像素的非常小的 PNG 空白纹理来减少内存使用量。当场景消失并被新场景替换时,这减少了我的游戏数百 MB 的内存使用量...... 这是正常行为。 SpriteKit 缓存纹理以提高性能。我不会释放它们,让 SpriteKit 为你做。 添加到这个。转到您的 SKScenes 并添加带有打印语句的 deinit(请参阅更新的答案)方法。如果 deinit 被调用,您就知道整个场景已正确释放,如果没有,则表示内存泄漏。这样更容易看到发生了什么,因为 SpriteKit 在背景上做了很多事情,例如纹理缓存。 感谢您的帮助,非常感谢。我想我已经确定了 Instruments 的内存泄漏:***.com/questions/38849889/…

以上是关于Swift,SpriteKit:释放游戏场景并重新分配新场景的主要内容,如果未能解决你的问题,请参考以下文章

基本的 swift/spritekit 游戏 - 麻烦重绘

Swift SpriteKit 出现场景错误

Swift Spritekit 应用旋转脉冲

使用 spritekit 检测释放滑动 swift 时的坐标 [关闭]

如何将SpriteKit中的单个游戏场景添加到普通的单视图Swift应用程序中

(Swift + Spritekit) - 完全删除节点及其数据