4

我正在使用 SpriteKit 和 Swift 编写游戏,并且遇到了内存问题。

我的游戏的布局是这样的,GameViewController (UIViewController) 在 vi​​ewDidLoad 屏幕中呈现第一个 SKScene (levelChooserScene)。这个场景只是显示了一堆按钮。当用户选择一个按钮时,场景然后使用 skView.presentScene 转换到正确的场景,当关卡完成时,该场景然后转换回 levelChooserScene 并且游戏准备好供用户选择下一个关卡。

问题是当转换回 levelChooserScene 发生时,分配给游戏场景的内存没有被释放,所以在只选择了几个级别后,我开始收到内存错误。

我的设计在从 SKScene 过渡到 SKScene 时是否正确,还是应该每次都返回到 GameViewController,然后从那里过渡到下一个 SKScene?

我在这里找到了一些帖子说我应该在场景之间调用 skView.presentScene(nil),但我对如何或在哪里实现它感到困惑。

我只是想从一个 SKScene 过渡到另一个 SKScene 并将传出场景中使用的内存返回给系统。

这是我如何实现 SKScene 的示例:

class Level3: SKScene
{
   var explodingRockTimer = NSTimer()
   var blowingUpTheRocks = SKAction()

   override func didMoveToView(view: SKView)
   {
       NSTimer.scheduledTimerWithTimeInterval(5.0, target: self, selector: "dismissTheScene:", userInfo: nil, repeats: false)
       var wait = SKAction.waitForDuration(0.5)
       var run = SKAction.runBlock{
           // your code here ...
           self.explodeSomeRocks()
       }
       let runIt = SKAction.sequence([wait,run])
       self.runAction(SKAction.repeatActionForever(runIt), withKey: "blowingUpRocks")

       var dismissalWait = SKAction.waitForDuration(5.0)
       var dismissalRun = SKAction.runBlock{
           self.removeActionForKey("blowingUpRocks")
           self.dismissTheScene()

       }
       self.runAction(SKAction.sequence([dismissalWait,dismissalRun]))
   }

   func explodeSomeRocks()
   {
       println("Timer fired")
   }

   //MARK: - Dismiss back to the level selector
   func dismissTheScene()
   {
       let skView = self.view as SKView?
       var nextScene = SKScene()

       nextScene = LevelChooserScene()
       nextScene.size = skView!.bounds.size
       nextScene.scaleMode = .AspectFill
       var sceneTransition = SKTransition.fadeWithColor(UIColor.blackColor(), duration: 1.5) //WithDuration(2.0)
       //var sceneTransition = SKTransition.pushWithDirection(SKTransitionDirection.Down, duration: 0.75) //WithDuration(2.0)
       //var sceneTransition = SKTransition.crossFadeWithDuration(1.0)
       //var sceneTransition = SKTransition.doorwayWithDuration(1.0)
       sceneTransition.pausesOutgoingScene = true

       skView!.presentScene(nextScene, transition: sceneTransition)
   }
}
4

2 回答 2

1

导致我遇到麻烦的事情是使用 SKAction.repeatActionForever() 每半秒插入粒子发射器 5 秒来调用发射器插入函数。

这个repeatAction 显然没有被转换到另一个场景而被杀死,而是导致整个场景的记忆被保留。我改用 SKAction.repeatAction() 并指定它应该触发多少次。当我过渡到新场景时,场景现在会返回其所有内存。

我不确定我是否理解这种行为。

于 2015-04-19T20:30:27.427 回答
1

SpriteKit 在创建复杂游戏时没有详细记录。我个人有几天这样的问题,直到我设法弄清楚。

一些对象保留引用,因此它不会取消初始化。(SKAction、定时器等)

在呈现一个新场景之前,我调用了一个函数,在该prepare_deinit()函数中我手动删除了通常不会被 swift 释放的强引用。

func prepare_deinit()
{
    game_timer.invalidate() // for Timer()
    removeAction(forKey: "blowingUpRocks") // for SKAction in your case

    // I usually add the specific actions to an object and then remove 
    object.removeAllActions()

    // If you create your own object/class that doesn't deinit, remove all object 
    //actions and the object itself
    custom_object.removeAllActions()
    custom_object.removeFromParent()

}

deinit
{
   print("GameScene deinited")
}

我遇到的最后一个问题是新场景的呈现速度比我的要快得多,prepare_deinit()所以我不得不稍后呈现新场景,给 prepare_deinit() 足够的时间来释放所有对象。

let new_scene =
{
   let transition = SKTransition.flipVertical(withDuration: 1.0)
   let next_scene = FinishScene(fileNamed: "FinishScene")
   next_scene?.scaleMode = self.scaleMode
   next_scene?.name = "finish"
   self.view?.presentScene(next_scene!, transition: transition)
}

run(SKAction.sequence([SKAction.run(prepare_deinit), SKAction.wait(forDuration: 0.25), SKAction.run(exit_to_finish)]))
于 2018-07-14T07:40:14.257 回答