在 Swift / SpriteKit 中使用 NSCoding 获得高分和终生得分

Using NSCoding to get High Score and LifeTime Score in Swift / SpriteKit

进退两难。

我有一个 class 管理 Sprite Kit 游戏的高分。 "high score" 功能有效,但是 "calculating all scores".

在游戏中,玩家必须收集浆果。我想记录一生中 浆果.

的数量

这是我的class管理高分。

class Score: NSObject, NSCoding {

var score: Int
var highScore: Int
var lifeTimeScore: Int

static let DocumentsDirectory = NSFileManager().URLsForDirectory(.DocumentDirectory, inDomains: .UserDomainMask).first!
static let ArchiveURL = DocumentsDirectory.URLByAppendingPathComponent("scores")

struct PropertyKey {
    static let highScoreKey = "high"
    static let currentScoreKey = "current"
    static let lifeTimeScoreKey = "life"
}

func encodeWithCoder(aCoder: NSCoder){
    aCoder.encodeObject(score, forKey: PropertyKey.currentScoreKey)
    aCoder.encodeObject(highScore, forKey: PropertyKey.highScoreKey)
    aCoder.encodeObject(lifeTimeScore, forKey: PropertyKey.lifeTimeScoreKey)

}

required convenience init?(coder aDecoder: NSCoder) {
    let score = aDecoder.decodeObjectForKey(PropertyKey.currentScoreKey) as! Int
    let highScore = aDecoder.decodeObjectForKey(PropertyKey.highScoreKey) as! Int
    let lifeTimeScore = aDecoder.decodeObjectForKey(PropertyKey.lifeTimeScoreKey) as! Int

    self.init(score: score, highScore: highScore, lifeTimeScore: lifeTimeScore)

}

init?(score: Int, highScore: Int, lifeTimeScore: Int!) {
    // Initialize stored properties.
    self.score = score
    self.highScore = highScore
    self.lifeTimeScore = lifeTimeScore

    super.init()
   }    
}

当我去加载乐谱时,问题就来了。如果我删除存在的 lifeTimeScore,一切正常。然而,一旦我尝试实现这个小傻瓜,他就会给我一个

fatal error: unexpectedly found nil while unwrapping an Optional value

当我检查调试器时,问题出在

let lifeTimeScore = aDecoder.decodeObjectForKey(PropertyKey.lifeTimeScoreKey) as! Int

真是一头雾水,我真的停滞不前了。这是调用 Score class 的 GameModel Class。当我用 newGame() 实例化它时。我收到错误消息。

class GameModel: NSObject {

private var highScore = Int()
private var berriesCaught: Int = NO_BERRIES_CAUGHT
private var lifeTimeBerries = Int()

var gameOver: Bool!
var damaged: Bool!

func newGame(){
    berriesCaught = NO_BERRIES_CAUGHT

    if (loadScores() != nil) {
        if let score = loadScores(){
            highScore = score.highScore
            lifeTimeBerries = score.lifeTimeScore
        }
    }

    checkGameState()
    damaged = false
}

func saveScores(){
    checkHighScore()
    lifeTimeBerries += berriesCaught
    if let scoreToken = Score(score: berriesCaught, highScore: highScore, lifeTimeScore: lifeTimeBerries){
        let isSuccessfulSave = NSKeyedArchiver.archiveRootObject(scoreToken, toFile: Score.ArchiveURL.path!)
        if !isSuccessfulSave {
            print("Failed to save scores")
        }
    }
}

func loadScores() -> Score? {
    return NSKeyedUnarchiver.unarchiveObjectWithFile(Score.ArchiveURL.path!) as? Score
}

func checkHighScore(){
    if let score = loadScores() {
        if berriesCaught > score.highScore {
            highScore = berriesCaught
        } else {
            highScore = score.highScore
        }
    } else{
        highScore = berriesCaught
    }
}

我该如何修复才能在每场比赛后正确添加 lifeTimeBerries? 我该如何修复它,以便它在我调用 loadScores() 时为属性加载正确的值?

谢谢。

使用这个更新的代码和解码器方法。

    func encodeWithCoder(aCoder: NSCoder){

    aCoder.encodeInt(score, forKey: PropertyKey.currentScoreKey)
    aCoder.encodeInt(highScore, forKey: PropertyKey.highScoreKey)
    aCoder.encodeInt(lifeTimeScore, forKey: PropertyKey.lifeTimeScoreKey)

}


required convenience init?(coder aDecoder: NSCoder) {

    let score = aDecoder.decodeIntForKey(PropertyKey.currentScoreKey)
    let highScore = aDecoder.decodeIntForKey(PropertyKey.highScoreKey
    let lifeTimeScore = aDecoder.decodeIntForKey(PropertyKey.lifeTimeScoreKey)

    self.init(score: score, highScore: highScore, lifeTimeScore: lifeTimeScore)

}

您崩溃的原因是,如果未找到密钥,则解码密钥对象不会提供默认值(例如 decodeBoolForKey 默认为 false)

因此您需要调整您的代码。正如其他成员也所说,将代码更改为

let lifeTimeScore = aDecoder.decodeIntegerForKey(PropertyKey.lifeTimeScoreKey)

如果找不到密钥,它将自动设置默认值,因为它知道您正在尝试解码整数。

如果你想使用 decodeObjectForKey,你需要先做一些 nil 检查,因为你正在解码 "AnyObject",所以没有默认值。该行

... as! Int

将强制将 objectForKey 解包为 Int,但它可能不存在,因此为 nil,然后你就会崩溃。要么也改

if aDecoder.decodeObjectForKey(PropertyKey.lifeTimeScoreKey) != nil {
     let lifeTimeScore = aDecoder.decodeObjectForKey(PropertyKey.lifeTimeScoreKey) as! Int
}

或使用这个方便的功能将其放在 1 行中

 let lifeTimeScore = aDecoder.decodeObjectForKey(PropertyKey.lifeTimeScoreKey) as? Int ?? Int()

这里它将检查一个键是否存在(如?Int),如果不存在,它将创建一个新的默认键(??Int())。

很可能最好的方法是使用 IntegerForKey,因为您的 "lifeTimeScore" 是一个 Int,只有在您也有 Int 时才使用 ObjectForKey。