Reputation: 3541
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?
Upvotes: 3
Views: 805
Reputation: 675
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(size: self.view!.bounds.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)
}
}
Upvotes: 3
Reputation: 35412
About your question I want to focus your attention to three elements:
1) Instead of repeating everytime in your SKScene
or SKNode
the same shared properties (struct, vars or custom classes..) you could create a class that contain your shared properties.
2) It's very comfortable to your project to have some shared instance manager: for example a game manager (shared instance class) that have properties of all managers (shared instance classes) of your project (an example below):
also your game properties model like settings and your game vars.
3) If you need for example a rootNode
in your scenes (to add elements to pause instead of another, to add object to the same anchored node, to make a background..) you can subclass to a customized SKScene
that have this properties.
Some code to show how could appear your project, starting with a GameManager:
class GameManager: NSObject {
static let sharedInstance = GameManager()
var allProducts = Products()
}
Your GameScene Class instance can access a single instance of GameManager like this:
class GameScene: SKScene {
let gameManager = GameManager.sharedInstance
override func didMoveToView(view: SKView) {
print("the current number of shoes is \(self.gameManager.allProducts.shoes)")
}
}
Your ShopScene Class instance can access the same single instance of GameManager like this:
class ShopScene: SKScene {
let gameManager = GameManager.sharedInstance
override func didMoveToView(view: SKView) {
print("the current number of shoes is \(self.gameManager.allProducts.shoes)")
}
}
Upvotes: 3