SpriteKit 场景转换良好实践
SpriteKit scene transition good practices
我正在使用 SpriteKit 和 Swift 编写游戏,并且 运行 成为内存问题。
我的游戏布局是这样的,即 GameViewController (UIViewController) 在 viewDidLoad 屏幕中呈现第一个 SKScene (levelChooserScene)。这个场景只是显示一堆按钮。当用户 select 按下按钮时,场景然后使用 skView.presentScene 转换到正确的场景,当关卡完成时,该场景然后转换回 levelChooserScene 并且游戏已准备好供用户使用 select下一级
问题是当转换回 levelChooserScene 时,分配给游戏场景的内存没有被释放,所以在 select 只玩了几个级别后,我开始收到内存错误。
我的设计在从 SKScene 过渡到 SKScene 时是否正确,或者我应该每次都 return 到 GameViewController 然后从那里过渡到下一个 SKScene?
我在这里找到了一些帖子说我应该在场景之间调用 skView.presentScene(nil),但我对如何或在哪里实现它感到困惑。
我只是想从一个 SKScene 过渡到另一个 SKScene,并将传出场景中使用的内存 returned 到系统。
这是我如何实现 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)
}
}
好吧,造成我麻烦的事情是使用 SKAction.repeatActionForever() 调用发射器插入函数,每半秒插入一次粒子发射器,持续 5 秒。
这个 repeatAction 显然并没有因为转换到另一个场景而被杀死,而是导致整个场景的内存被保留。我改用 SKAction.repeatAction() 并指定它应该触发多少次。当我转换到新场景时,场景现在 returns 所有的记忆。
虽然我不确定我是否理解这种行为。
SpriteKit 在创建复杂游戏时并没有被详细记录。我个人遇到这样的问题好几天了,直到我设法解决它。
一些对象保留了引用,所以它不会取消初始化。 (SKActions、定时器等)
在呈现新场景之前,我调用了一个 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)]))
我正在使用 SpriteKit 和 Swift 编写游戏,并且 运行 成为内存问题。
我的游戏布局是这样的,即 GameViewController (UIViewController) 在 viewDidLoad 屏幕中呈现第一个 SKScene (levelChooserScene)。这个场景只是显示一堆按钮。当用户 select 按下按钮时,场景然后使用 skView.presentScene 转换到正确的场景,当关卡完成时,该场景然后转换回 levelChooserScene 并且游戏已准备好供用户使用 select下一级
问题是当转换回 levelChooserScene 时,分配给游戏场景的内存没有被释放,所以在 select 只玩了几个级别后,我开始收到内存错误。
我的设计在从 SKScene 过渡到 SKScene 时是否正确,或者我应该每次都 return 到 GameViewController 然后从那里过渡到下一个 SKScene?
我在这里找到了一些帖子说我应该在场景之间调用 skView.presentScene(nil),但我对如何或在哪里实现它感到困惑。
我只是想从一个 SKScene 过渡到另一个 SKScene,并将传出场景中使用的内存 returned 到系统。
这是我如何实现 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)
}
}
好吧,造成我麻烦的事情是使用 SKAction.repeatActionForever() 调用发射器插入函数,每半秒插入一次粒子发射器,持续 5 秒。
这个 repeatAction 显然并没有因为转换到另一个场景而被杀死,而是导致整个场景的内存被保留。我改用 SKAction.repeatAction() 并指定它应该触发多少次。当我转换到新场景时,场景现在 returns 所有的记忆。
虽然我不确定我是否理解这种行为。
SpriteKit 在创建复杂游戏时并没有被详细记录。我个人遇到这样的问题好几天了,直到我设法解决它。
一些对象保留了引用,所以它不会取消初始化。 (SKActions、定时器等)
在呈现新场景之前,我调用了一个 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)]))