How would I save the current level that the user is on. I have tried using the fallowing code but it does not work. When the user leaves the game I want the game to pick back up on the level they left off at.
This is in my viewcontroller.swift file:
import UIKit
import SpriteKit
import GameplayKit
class GameViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
if let view = self.view as! SKView? {
guard let scene = GameScene.loadScene() ?? SKScene(fileNamed: "mainMenu") as? GameScene else {
fatalError("Scene not loaded")
}
scene.scaleMode = .aspectFill
view.presentScene(scene)
view.ignoresSiblingOrder = true
view.showsFPS = true
view.showsNodeCount = true
}
}
override var shouldAutorotate: Bool {
return true
}
override var supportedInterfaceOrientations: UIInterfaceOrientationMask
{
if UIDevice.current.userInterfaceIdiom == .phone {
return .allButUpsideDown
} else {
return .all
}
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Release any cached data, images, etc that aren't in use.
}
override var prefersStatusBarHidden: Bool {
return true
}
}
I have a mainMenu screen and when the user taps on the screen I want that person to be presented with the most recent level that the user was on.
This is in my mainMenu.swift file:
import SpriteKit
import GameplayKit
var welcomeLabel = SKLabelNode()
var playLabel = SKLabelNode()
class GameScene: SKScene {
required init?(coder decoder: NSCoder) {
super.init(coder: decoder)
decoder.decodeBool(forKey: "savingKey")
NotificationCenter.default.addObserver(self, selector:
#selector(applicationDidEnterBackground), name:
.UIApplicationDidEnterBackground, object: nil)
}
func applicationDidEnterBackground() {
// Save Scene
let sceneData = NSKeyedArchiver.archivedData(withRootObject: self)
UserDefaults.standard.set(sceneData, forKey: "currentScene")
}
class func loadScene() -> SKScene? {
var scene: GameScene?
if let savedSceneData = UserDefaults.standard.object(forKey:
"currentScene") as? NSData,
let savedScene = NSKeyedUnarchiver.unarchiveObject(with:
savedSceneData as Data) as? GameScene {
scene = savedScene
} else {
scene = nil
}
return scene
}
override func didMove(to view: SKView) {
welcomeLabel = SKLabelNode(fontNamed: "Courier New")
welcomeLabel.text = "Go round Go"
welcomeLabel.fontColor = SKColor.green
welcomeLabel.fontSize = 50
welcomeLabel.position = CGPoint(x: frame.midX, y: 500)
addChild(welcomeLabel)
backgroundColor = UIColor.lightGray
playLabel = SKLabelNode(fontNamed: "Courier New")
playLabel.text = "Tap anywhere to play"
playLabel.fontColor = SKColor.green
playLabel.fontSize = 50
playLabel.position = CGPoint(x: frame.midX, y: frame.midY)
addChild(playLabel)
}
override func touchesBegan(_ touches: Set<UITouch>, with event:
UIEvent?) {
welcomeLabel.isHidden = true
playLabel.isHidden = true
let nextlevel = levelOne(fileNamed: "levelOne")
nextlevel?.scaleMode = .aspectFill
self.view?.presentScene(nextlevel!, transition:
SKTransition.fade(withDuration: 0.1))
}
override func update(_ currentTime: TimeInterval) {
}
}
I would like if someone can explain to me how to do this. I am not experienced when it comes to encoding and decoding.
You don't need to use encoding and decoding here. You can just save the name of the current scene in the user defaults. You don't even need the notification center. Just save the name of the scene in the user defaults like this:
class GameScene:SKScene {
let defaults = UserDefaults.standard
override func didMove(to view: SKView) {
defaults.set(currentLevel, forKey: "continuePoint")
}
}
Now in your GameViewController class check if there is a saved continuePoint. If this is the case, load the scene. If this is not the case, load your initial level:
class GameViewController: UIViewController{
let defaults = UserDefaults.standard
override func viewDidLoad(){
super.viewDidLoad()
//Switch this with the name of your first level.
var initialLevel = "initialLevelName"
if defaults.object(forKey: "continuePoint") != nil{
initialLevel = defaults.string(forKey: "continuePoint")!
}
if let view = self.view as! SKView? {
if let scene = GameScene(fileNamed: initialLevel) {
scene.scaleMode = .aspectFill
scene.currentLevel = initialLevel
view.presentScene(scene)
}
}
}
}
Unfortunately I can't test this at the moment, but something like this will definitely work, I use this in my game too.
EDIT:
I looked at your project and I see that you change your level in your mainMenu.swift to your firstLevel everytime. You can do something like this in your touchesBegan:
var levelName = ""
var nextLevel: SKScene?
if defaults.object(forKey: "continuePoint") != nil && defaults.string(forKey: "continuePoint") != ""{
levelName = defaults.string(forKey: "continuePoint")!
}
else{
levelName = "levelOne"
}
if levelName == "levelOne"{
nextLevel = levelOne(fileNamed: levelName)
}
else if levelName == "levelTwo"{
nextLevel = levelTwo(fileNamed: levelName)
}
nextLevel?.scaleMode = .aspectFill
self.view?.presentScene(nextLevel!, transition: SKTransition.fade(withDuration: 0.1))
}
But this is not the best way to do it. The problem is that you have a new subclass of SKScene for every level that is in the game. If you use a .plist file and set the properties for the specific levels there, you can parse it in your GameScene class and then you have only one SKScene class for all of your Scenes. Your Menu Scene should be in a seperate class though.
You sould find out how to use a plist file in your game to have a better structure. And btw: It is good to use source control like github or bitbucket in all of your projects. So when your project gets bigger and you build in new features and your code doesn't work as expected, you can easily set it back to an older state.