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

时间:2018-06-01 18:07:45

标签: sprite-kit codable decodable

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

Cannot automatically synthesize 'Decodable' because 'SKSpriteNode' does not conform to 'Decodable'

该类如下所示(至少是具有属性的部分):

class TileNode: SKNode, Codable {

    // MARK: - Class Variables

    var height = 32
    var width  = 32
    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()


    // MARK: - Init

    override init() {
        super.init()

    }

    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

    }
}

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

1 个答案:

答案 0 :(得分:2)

很明显,这个问题是因为SKSpriteNode不符合Codable协议。因此,以下步骤可使自定义类SKSpriteNodeCodable协议一起使用。

  1. 声明自定义编码键以定义要编码或解码的属性列表
  2. 实施required init(from:)方法将Data转换为SKSpriteNode
  3. 实施encode(to:)方法将SKSpriteNode转换为Data
  4. 以下是样本:

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

    从UserDefaults和Decode Data中检索数据:

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