Swift,SpriteKit:如何保存场景的全部进度

5

我已经用“GameViewController.swift”构建了一个Swift游戏。

import UIKit
import SpriteKit

class GameViewController: UIViewController {


override func viewDidLoad() {
    super.viewDidLoad()


    if let scene = GameScene(fileNamed:"GameScene") {
        // Configure the view.
        let skView = self.view as! SKView

        /* Set the scale mode to scale to fit the window */


        scene.scaleMode = .AspectFill

        skView.presentScene(scene)
        }
    }
}

我有一个场景叫做"GameScene"(当你打开swift并创建一个新的swift游戏时,它是默认的游戏文件)

import UIKit
import SpriteKit

class GameScene: SKScene{

var building = SKSpriteNode()

override func didMoveToView(view: SKView) {

    /* Setup your scene here */

}

这里有许多事情要做,(可以放置建筑物并触发一些动作等)
但是当您离开应用程序时,所有进度都会丢失。
如何保存整个“GameScene”的进度并在开始时重新加载?


使用类或结构创建一个模型。 - El Tomato
3个回答

5
你可以在应用进入后台(_:)事件中归档游戏状态。通过NotificationCenter订阅以获取通知。
class GameScene : SKScene {
    required init?(coder decoder: NSCoder) {
        super.init(coder: decoder)

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

场景加载发生在viewDidLoad中,首先尝试定位以前存档的场景,如果不存在存档的场景则创建新的场景。

class GameViewController: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()

        if let view = self.view as! SKView? {
            guard let scene = GameScene.loadScene() ?? SKScene(fileNamed: "GameScene") as? GameScene else {
                fatalError("Scene not loaded")
            }

            scene.scaleMode = .resizeFill

            view.presentScene(scene)            
            view.ignoresSiblingOrder = true
        }
    }
}

正如其他答案所提到的那样,您还需要使游戏元素符合NSCoding标准。


4
SKScene符合NSCoding协议,因此您可以使用它来归档场景。这是一个可能有帮助的教程。 https://www.hackingwithswift.com/read/12/3/fixing-project-10-nscoding 基本思路是,将场景实例进行归档: NSKeyedArchiver.archivedData(withRootObject: scene) 然后将其保存到磁盘或其他位置。
稍后,您可以使用以下代码解档它: NSKeyedUnarchiver.unarchiveObject(with: scene) as! SKScene 现在,当您创建自定义场景类时,需要确保包含任何需要保存的自定义变量。这就是人们往往不使用的那个讨厌的required init(coder aDecoder:NSCoder)初始化器的作用。
您可以使用此初始化器解码自定义变量,如下所示:
required init(coder aDecoder: NSCoder) {
    player = aDecoder.decodeObject(forKey: "player") as! String
}

要将其保存到存档中,您需要添加一个编码方法。

func encode(with aCoder: NSCoder) {
    aCoder.encode(player, forKey: "player")
}

0
感谢Mark Brownsword在2017年的回答!到了2023年,解决方案如下:
private let kWorldScene = "kWorldScene"

class WorldScene: SKScene {

    required init?(coder decoder: NSCoder) {
        super.init(coder: decoder)

        NotificationCenter.default.addObserver(self, selector: #selector(applicationWillResignActiveNotification), name: UIApplication.willResignActiveNotification, object: nil)
    }

    deinit {
        NotificationCenter.default.removeObserver(self)
    }

    @objc func applicationWillResignActiveNotification() {
        if let sceneData = try? NSKeyedArchiver.archivedData(withRootObject: self, requiringSecureCoding: false) {
            NSLog("archive \(sceneData.count) bytes")
            UserDefaults.standard.set(sceneData, forKey: kWorldScene)
        }
    }

    static func loadScene() -> WorldScene? {
        do {
            guard let sceneData = UserDefaults.standard.object(forKey: kWorldScene) as? Data else { return nil }
            NSLog("unarchive \(sceneData.count) bytes")
            return try NSKeyedUnarchiver.unarchivedObject(ofClass: WorldScene.self, from: sceneData)
        } catch {
            NSLog("unarchive error: \(error)")
            return nil
        }
    }

    /// Class '...' has a superclass that supports secure coding, but '...' overrides -initWithCoder: and does not override +supportsSecureCoding.
    /// The class must implement +supportsSecureCoding and return YES to verify that its implementation of -initWithCoder: is secure coding compliant.
    static override var supportsSecureCoding: Bool {
        return true
    }

    ...
}

请确保更新SKNode子类的init(coder:)supportsSecureCoding:方法。

与以前一样,ViewController初始化场景:

class WorldViewController: UIViewController {

    ...

    @IBOutlet private var worldView: WorldView!

    ...

    override func viewDidLoad() {
        super.viewDidLoad()

        let scene = WorldScene.loadScene() ?? WorldScene(size: self.worldView.frame.size)
        scene.scaleMode = .resizeFill
        self.worldView.presentScene(scene)
        self.worldView.ignoresSiblingOrder = true
    }

    ...

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接