Brejuro Brejuro - 4 months ago 35
Swift Question

SpriteKit transition between scenes without resetting game

How can I transition between scenes without my game resetting because

didMoveToView()
was called and reinitialized all of my instance variables. For example I have a game scene and a shop scene. When I transition from my shop scene to my game, the game resets. Is there a way to prevent this or how do I keep the same state of my game when transitioning between scenes?

Answer

You have lots of options to keep persistent state across your game scenes. I've listed two approaches I've used.

Option A: Maintain a Reference to the Scene

When a scene is swapped out for a new one, the scene is often fully removed from memory. If you hold onto a reference for the scene object somewhere else, and present that reference, no data will be lost.

To maintain a reference over time (and present the scene again when needed), I recommend a scene presenter class with a static instance such as the following:

class SceneCoordinator {
    static var shared = SceneCoordinator()

    var gameScene : GameScene?
    var shopScene : ShopScene?
}

when you initialize a GameScene, also register it with your with SceneCoordinator.shared.gameScene = self. Then, when transitioning away from another scene, you can present the instance you stored in the coordinator class.

didMoveToView() will still be called on the scene when it is presented again. You could move all your initializing code to a separate function, create a new instance var such as var isInitialized = false, and only initialize your content if it is false (and set it to true after your initialize).

The issue with this approach is that scene objects are expensive, which means you could build up a large overhead by not allowing scenes to be released.

Option B: Model Struct

The better way (which would also allow for easier reinit of a scene after your app closes) would be to create a data model of your game state, and provide a function to create a GameScene from your model object.

This method is more consistent with the Model-View-Controller design pattern, and allows your scenes and data to be a lot more lightweight.

Such as:

struct GameModel {
    var coins : Int
}

class GameScene : SKScene {
    var state : GameModel

    convenience init(size: CGSize, state: GameModel) {
        self.state = state
        // set up scene content from state
    }

    // lots of fun game scene logic

    func playerCollectedCoin() {
        state.coins += 1
    }

    func moveToShopScene() {
        // init a new shop scene with the state of this scene
        let shop = ShopScene(frame: self.view!.size, state: self.state)
        (self.view as! SKView).presentScene(scene)
    }
}

class ShopScene : SKScene {
    var state : GameModel

    convenience init(size: CGSize, state: GameModel) {
        self.state = state
        // set up scene content from state
    }

    // lots of fun shop scene logic

    func playerSpentCoins(amount: Int) {
        state.coins -= amount
    }

    func moveToGameScene() {
        // init a new game scene with the updated state of this scene
        let game = GameScene(size: self.view!.size, state: self.state)
        (self.view as! SKView).presentScene(game)
    }
}