Sprite kit 在SpriteKit中的自定义类上使用可编码协议

Sprite kit 在SpriteKit中的自定义类上使用可编码协议,sprite-kit,codable,decodable,Sprite Kit,Codable,Decodable,我正在使用SpriteKit构建一个应用程序,我想保存一个复杂的自定义类,以利用可编码协议实现NSUserDefaults。当我这样做时,我会得到以下错误: Cannot automatically synthesize 'Decodable' because 'SKSpriteNode' does not conform to 'Decodable' 该类如下所示(至少是具有属性的部分): 所以,我的问题是,当需要将SKSpriteNode之类的东西作为类变量时,如何在自定义对象上使用可编码

我正在使用SpriteKit构建一个应用程序,我想保存一个复杂的自定义类,以利用可编码协议实现NSUserDefaults。当我这样做时,我会得到以下错误:

Cannot automatically synthesize 'Decodable' because 'SKSpriteNode' does not conform to 'Decodable'
该类如下所示(至少是具有属性的部分):


所以,我的问题是,当需要将SKSpriteNode之类的东西作为类变量时,如何在自定义对象上使用可编码协议?

很明显,这个问题是因为
SKSpriteNode
不符合
codable
协议。因此,以下步骤使自定义类(如
SKSpriteNode
)能够使用
Codable
协议正常工作

  • 声明自定义编码键以定义要编码或解码的属性列表
  • 实现
    所需的init(from:)
    方法,将
    数据
    转换为
    SKSpriteNode
  • 实现
    encode(to:)
    方法将
    SKSpriteNode
    转换为
    数据
  • 以下是示例:

    class TileNode: SKNode, Codable {
    
        var height = 32.0
        var width  = 32.0
        var index  = 0
        var row    = 0
        var column = 0
        var tileType = ""
    
        // These cannot be global because each tile needs its own instance of them.
        var dark = SKSpriteNode()
        var light = SKSpriteNode()
    
        enum CodingKeys: String, CodingKey {
            case height
            case width
            case index
            case row
            case column
            case tileType
            case dark
            case light
        }
    
        init(index:Int, tex: String) {
            super.init()
    
            if tileCheck {
                print("TileNode: init: creating tile with texture: \(tex).")
            }
    
            self.isUserInteractionEnabled = false
            self.index = index
            self.zPosition = 10
            self.name = "Tile \(index)"
            self.tileType = tex
        }
    
        required init(from decoder: Decoder) throws {
            super.init()
            let values = try decoder.container(keyedBy: CodingKeys.self)
    
            height = try values.decode(Double.self, forKey: .height)
            width = try values.decode(Double.self, forKey: .width)
            index = try values.decode(Int.self, forKey: .index)
            row = try values.decode(Int.self, forKey: .row)
            column = try values.decode(Int.self, forKey: .column)
            tileType = try values.decode(String.self, forKey: .tileType)
    
            let darkData = try values.decode(Data.self, forKey: .dark)
            dark = NSKeyedUnarchiver.unarchiveObject(with: darkData) as? SKSpriteNode ?? SKSpriteNode()
            let lightData = try values.decode(Data.self, forKey: .light)
            light = NSKeyedUnarchiver.unarchiveObject(with: lightData) as? SKSpriteNode ?? SKSpriteNode()
        }
    
        required init?(coder aDecoder: NSCoder) {
            // subclass of SKNode must implement this method
            fatalError("init(coder:) has not been implemented")
        }
    
        func encode(to encoder: Encoder) throws {
            var container = encoder.container(keyedBy: CodingKeys.self)
    
            try container.encode(height, forKey: .height)
            try container.encode(width, forKey: .width)
            try container.encode(index, forKey: .index)
            try container.encode(row, forKey: .row)
            try container.encode(column, forKey: .column)
            try container.encode(tileType, forKey: .tileType)
    
            let darkData = NSKeyedArchiver.archivedData(withRootObject: dark)
            try container.encode(darkData, forKey: .dark)
            let lightData = NSKeyedArchiver.archivedData(withRootObject: light)
            try container.encode(lightData, forKey: .light)
        }
    
    }
    
    用法

    对数据进行编码并将数据保存在UserDefaults中:

        let node = TileNode(index: 3, tex: "Text")
        node.height = 33.0
        let dark = SKSpriteNode()
        dark.anchorPoint = CGPoint(x: 12.3, y: 45.6)
        dark.color = UIColor.red
        node.dark = dark
        let encoder = JSONEncoder()
    
        do {
            let encoded = try encoder.encode(node)
            UserDefaults.standard.set(encoded, forKey: "Node")
        } catch {
            print(error.localizedDescription)
        }
    
    从UserDefault检索数据并解码数据:

        let docoder = JSONDecoder()
        do {
            let nodeData = UserDefaults.standard.object(forKey: "Node") as! Data
            let node = try docoder.decode(TileNode.self, from: nodeData)
        } catch {
            print(error.localizedDescription)
        }
    

    您需要了解协议是如何工作的,在任何情况下,您都不会使用协议告诉您添加的init(解码器:NSCoder)(您还需要添加编码器)。在大多数情况下,此解决方案工作得很好,但当我尝试从默认值加载时,会出现错误“无法读取数据,因为它的格式不正确。”在控制台中。错误发生在这一行
    let node=try docoder.decode(TileNode.self,from:nodeData)
    ?这是我的假设,它没有使应用程序崩溃,只是没有像我想象的那样从用户默认值加载对象。然而,我现在正在深入研究它,看看我是否能找到答案。好吧,我发现我的第一个实现有点马虎,忘记了更改一些变量:P这个解决方案按预期工作,谢谢!!!我将把我遇到的问题写在另一个问题上,因为我认为它偏离了这个问题,我认为这个问题已经得到了成功的回答。
        let docoder = JSONDecoder()
        do {
            let nodeData = UserDefaults.standard.object(forKey: "Node") as! Data
            let node = try docoder.decode(TileNode.self, from: nodeData)
        } catch {
            print(error.localizedDescription)
        }