сохранение текущего уровня, на котором пользователь остановился

Как мне сохранить текущий уровень, на котором находится пользователь. Я попытался использовать следующий код, но он не работает. Когда пользователь покидает игру, я хочу, чтобы игра возобновлялась на том уровне, на котором он остановился.

Это в моем файле viewcontroller.swift:

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
}
}

У меня есть экран mainMenu, и когда пользователь нажимает на экран, я хочу, чтобы этому человеку был представлен самый последний уровень, на котором был пользователь.

Это в моем файле mainMenu.swift:

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) {

}
}

Я хотел бы, если кто-то может объяснить мне, как это сделать. У меня нет опыта в кодировании и декодировании.


person Matt Young    schedule 24.01.2018    source источник


Ответы (1)


Вам не нужно использовать кодирование и декодирование здесь. Вы можете просто сохранить имя текущей сцены в пользовательских настройках по умолчанию. Вам даже не нужен центр уведомлений. Просто сохраните имя сцены в пользовательских настройках по умолчанию следующим образом:

class GameScene:SKScene {

    let defaults = UserDefaults.standard

    override func didMove(to view: SKView) {

        defaults.set(currentLevel, forKey: "continuePoint")

    }

}

Теперь в вашем классе GameViewController проверьте, есть ли сохраненная continuePoint. Если это так, загрузите сцену. Если это не так, загрузите свой начальный уровень:

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)
            }
        }
    }

}

К сожалению, я не могу проверить это в данный момент, но что-то подобное определенно сработает, я тоже использую это в своей игре.

РЕДАКТИРОВАТЬ:

Я посмотрел на ваш проект и вижу, что вы каждый раз меняете свой уровень в mainMenu.swift на свой первый уровень. Вы можете сделать что-то вроде этого в вашем 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))

    }

Но это не лучший способ сделать это. Проблема в том, что у вас есть новый подкласс SKScene для каждого уровня в игре. Если вы используете файл .plist и устанавливаете там свойства для определенных уровней, вы можете проанализировать его в своем классе GameScene, и тогда у вас будет только один класс SKScene для всех ваших сцен. Однако ваша сцена меню должна быть в отдельном классе.

Вы должны узнать, как использовать файл plist в своей игре, чтобы иметь лучшую структуру. И кстати: хорошо использовать систему управления версиями, такую ​​как github или bitbucket, во всех ваших проектах. Поэтому, когда ваш проект становится больше, и вы добавляете новые функции, а ваш код не работает должным образом, вы можете легко вернуть его к более старому состоянию.

person Marcel    schedule 24.01.2018
comment
код работает нормально, и я не получаю ошибок, но когда я создаю и запускаю приложение, я получаю сообщение об ошибке Thread1: signal CIGABRT. Я протестировал код и его строку кода defaults.set(levelOne(), forKey: continuePoint), которая делает это возникает ошибка. - person Matt Young; 26.01.2018
comment
Посмотрите в консоли подробную информацию об исключении, которое выдает ваш код, и опубликуйте ее здесь. Кстати: что делает ваша функция levelOne()? - person Marcel; 26.01.2018
comment
как вы сказали, я помещаю имя сцены в это место, xcode заставляет меня поставить () или .self после него, иначе это выдаст мне ошибку. Автокоррекция заставляет меня это делать. это не функция. - person Matt Young; 26.01.2018
comment
Вам нужно сохранить имя уровня в UserDefaults как строку, поэтому попробуйте следующее: defaults.set("levelOne", forKey: "continuePoint") - person Marcel; 26.01.2018
comment
Если вы хотите использовать именно этот код, вы должны настроить переменную в вашей GameScene, которая называется currentLevel. Эта переменная должна содержать имя реальной сцены, например levelOne. Затем просто используйте эту переменную, как указано выше. - person Marcel; 26.01.2018
comment
Извините за мою ошибку, я заглянул в свой проект и вижу, что код работает, если вы просто объявите переменную в своем классе GameScene следующим образом: var currentLevel = "". Затем просто установите var initialLevel = "initialLevelName" в вашем классе GameViewController на имя вашего первого уровня. - person Marcel; 26.01.2018
comment
попробуйте то, что я упомянул выше, если это не сработает, установите оператор печати внутри вашего if defaults.object(forKey: "continuePoint") != nil{ }. Это называется? Изменяется ли начальный уровень? - person Marcel; 26.01.2018
comment
да, это было вызвано, я поставил печать (привет), и заявление было вызвано - person Matt Young; 26.01.2018
comment
Есть ли проект на гитхабе? Тогда я посмотрю на это и, возможно, найду проблему - person Marcel; 26.01.2018
comment
собираюсь создать учетную запись сейчас, опубликую ссылку, когда опубликую проект, чувак, я очень ценю твое время. - person Matt Young; 26.01.2018
comment
Я посмотрел на ваш проект, и ваша проблема в том, что вы каждый раз устанавливаете свой уровень touchesBegan() вашего файла mainMenu.swift на первый уровень. Я отредактировал свой ответ. - person Marcel; 26.01.2018
comment
так я бы поместил там initialLevel или currentLevel? потому что так я перехожу с одного уровня на другой - person Matt Young; 26.01.2018
comment
Я забыл упомянуть, что, конечно, вы должны установить пользовательские значения по умолчанию в методах didMove() ваших уровней, например: defaults.set("levelTwo", forKey: "continuePoint") или defaults.set("levelThree", forKey: "continuePoint") и т. д. Потому что, если вы этого не сделаете, вы всегда будете начинать с уровня 1. - person Marcel; 26.01.2018
comment
и в viewController.swift я сохраняю все, что вы сказали в первую очередь? или игнорировать это. - person Matt Young; 26.01.2018
comment
спасибо большое мужик!!!!!! Честно говоря, я очень ценю ваше время, это сработало для меня!!! Я не могу отблагодарить вас достаточно !!! Я принял твой ответ! - person Matt Young; 26.01.2018
comment
только что понял, почему это плохая идея, мне нужно будет добавить оператор if для каждого уровня в моем файле mainMenu.swift - person Matt Young; 26.01.2018
comment
Да, это проблема. Как я уже сказал, вам следует подумать об использовании списка свойств для вашей игры. Также не стоит создавать новый класс для каждого уровня. - person Marcel; 26.01.2018