Swift:如何为我的游戏处理视图控制器
我有一个关于视图控制器的一般问题,以及当我开发基于SpriteKit的游戏时如何以干净的方式处理它们。
到目前为止我做了什么:
- 仅使用故事板来定义视图控制器
- 每个视图控制器(Home,LevelSelection,Game)都会通过presentScene呈现SKScene
- 在每个视图控制器中,我使用在视图控制器之间的故事板中定义的标识符i调用performSegueWithIdentifier
- 我在SKScene上使用SKSpritenode等以编程方式显示的所有内容
- 在故事板上我只有视图控制器,其中定义了segue关系和标识符
- 我在viewDidDisappear中所做的所有事情都是因为它似乎是让我的SKScene正确消失的唯一方法
我的问题是:
- 每当我看到另一个视图时,我的记忆就会升起,因为视图控制器被重新初始化,旧的控制器一直停留在堆栈中
- 我不清楚如何处理视图控制器之间的segue,在一些教程页面上我看到人们使用导航控制器,其他人使用一些视图控制器的强引用并使用单例模式为视图控制器来决定要么初始化视图控制器,要么只显示它
- 我的视图控制器没有deiniting,我理解我的主视图不能,因为它是最初的,但因为ios无论如何重新启动它,为什么然后不卸载它?
使用SpriteKit处理视图控制器的基于Swift的游戏的正确方法是什么? 下面你可以看到我的初始视图控制器(Home)显示一个带有简单播放按钮的SKScene,该按钮调用play()函数以切换到levelselection
import UIKit import SpriteKit class Home : UIViewController { private var scene : HomeScene! override func viewDidLoad() { print(self) super.viewDidLoad() self.scene = HomeScene(size: view.bounds.size) self.scene.scaleMode = .ResizeFill let skView = view as! SKView skView.showsFPS = true skView.showsNodeCount = true skView.ignoresSiblingOrder = true NSNotificationCenter.defaultCenter().addObserver(self, selector: #selector(play), name: Constants.Events.Home.play, object: nil) skView.presentScene(self.scene) } override func viewDidDisappear(animated: Bool) { super.viewDidDisappear(animated) let v = view as! SKView self.scene.dispose() v.presentScene(nil) NSNotificationCenter.defaultCenter().removeObserver(self) self.scene = nil self.view = nil print("home did disappear") } func play() { self.performSegueWithIdentifier("home_to_levelselection", sender: nil) } deinit { print("Home_VC deinit") } }
你的方式似乎非常复杂,基本上呈现3个场景。 它不是你应该为SpriteKit游戏做的,你只需要一个视图控制器(GameViewController)。
从GameViewController(例如HomeScene)加载你的第一个场景,没有别的。 直接在HomeScene中创建playButton和其他UI。 为您的UI使用SpriteKit API(SKLabelNodes,SKNodes,SKSpriteNodes等)。
你永远不应该在SpriteKit中真正使用UIKit(UIButtons,UILabels)。 这有一些例外,例如可能使用UICollectionViews进行大规模级别选择菜单,但基本UI应该使用SpriteKit API完成。
有很多关于如何创建精灵工具包按钮,如何使用SKLabelNodes等的教程.Xcode有一个SpriteKit级别编辑器,所以你可以做所有在视觉上类似于故事板。
而不是从HomeScene过渡到LevelSelect场景而不是GameScene,反之亦然。 它超级容易做到。
/// Home Scene class HomeScene: SKScene { ... func loadLevelSelectScene() { // Way 1 // code only, no XCode/SpriteKit visual level editor used let scene = LevelSelectScene(size: self.size) // same size as current scene // Way 2 // with xCode/SpriteKit visual level editor // fileNamed is the LevelSelectScene.sks you need to create that goes with your LevelSelectScene class. guard let scene = LevelSelectScene(fileNamed: "LevelSelectScene") else { return } let transition = SKTransition.SomeTransitionYouLike view?.presentScene(scene, withTransition: transition) } } /// Level Select Scene class LevelSelectScene: SKScene { .... func loadGameScene() { // Way 1 // code only, no XCode/SpriteKit visual level editor used let scene = GameScene(size: self.size) // same size as current scene // Way 2 // with xCode/SpriteKit visual level editor // fileNamed is the GameScene.sks you need to create that goes with your GameScene class. guard let scene = GameScene(fileNamed: "GameScene") else { return } let transition = SKTransition.SomeTransitionYouLike view?.presentScene(scene, withTransition: transition) } } /// Game Scene class GameScene: SKScene { .... }
我强烈建议你抓住你的故事板和ViewController方法,只使用不同的SKScenes和1个GameViewController。
希望这可以帮助
转到segues并在您不希望将前一个视图控制器保留在堆栈中的任何位置使用Show Detail Segues。 请记住,只要您返回它们,就必须在这些视图控制器中重新初始化所有内容。
如果注意, viewDidAppear
会在每次看到视图时加载,而在当前设置中, viewDidLoad
最初只会被调用,如果返回到viewController
,则只会调用viewDidAppear
。
当你使用segue转换出viewController
,会调用prepareForSegue
,但只有当你使用show detail segue(或自定义segue及其特定属性deinit()
时才调用deinit()
),因为视图就像你说的那样,被加载到内存中,因此可以更容易地检索它。