NovumCoder NovumCoder - 3 months ago 24
Swift Question

Swift: How to handle view controllers for my game

i have a general question about view controllers and how to handle them in a clean way when i develop a SpriteKit based game.

What i did so far:


  • Use storyboard only for defining view controllers

  • SKScene's are presented in each view controller (Home, LevelSelection, Game) by presentScene

  • in each view controller i call performSegueWithIdentifier with the identifier i defined in the storyboard between the view controllers

  • all the content i show programmatically using SKSpritenode etc. on the SKScene's

  • on the storyboard i only have view controllers with segue relations and identifiers defined

  • all the stuff i do in viewDidDisappear is because it seems to be the only way to get my SKScene deinited correctly



My problems are:


  • everytime i segue to another view, my memory raises, because the view controller is re-initialized, the old one keeps staying in the stack

  • it is not clear for me how to handle the segue's between the view controllers, on some tutorial pages i see people using the navigation controller, others are using strong references of some view controllers and using the singleton pattern for the view controller in order to decide either to init the view controller or just show it

  • my view controllers are not deiniting, i understand my home view can't because it is the initial one, but since ios is reiniting it anyways, why then not unloading it?



What is the correct way for a Swift based game using SpriteKit to handle the view controller? Below you can see my initial view controller (Home) showing an SKScene with a simple play button which calls the play() function to segue to the 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")
}
}

Answer

Your way seems very complicated to essentially present 3 scenes. Its not what you are supposed to do for SpriteKit games, you only really need 1 view controller (GameViewController).

Load your first scene from GameViewController (e.g HomeScene) and nothing else. Create your playButton and other UI directly in HomeScene. Use SpriteKit APIs for your UI (SKLabelNodes, SKNodes, SKSpriteNodes etc) instead of UIKit stuff (UIButtons, UILabels).

There is plenty tutorials to google on how to create sprite kit buttons, how to use SKLabelNodes etc.

Than from HomeScene transition to the LevelSelect Scene and than to the GameScene and vice versa. Its super easy to do.

/// 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 {
   ....
}

I strongly recommend you scratch your storyboard and ViewController approach, and just use different SKScenes and 1 GameViewController.

Hope this helps

Comments