使用NSCoding在Swift / SpriteKit中获得高分和LifeTime分数

时间:2016-01-19 06:06:06

标签: swift sprite-kit swift2 persistent nscoding

这就是困境。

我有一个管理精灵套装游戏高分的课程。 “高分”功能有效,但“计算所有分数”。

在游戏中,玩家必须收集浆果。我想跟踪一生的浆果

这是我的班级来管理高分。

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,那么一切正常。然而,一旦我尝试实施这个小傻瓜,他就会抛出一个

  

致命错误:在解包可选值时意外发现nil

当我检查调试器时,问题来自

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

真的很混乱,我真的处于停滞状态。这是调用Score类的GameModel类。当我用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()时为属性加载正确的值?

感谢。

2 个答案:

答案 0 :(得分:3)

您遇到崩溃的原因是,如果找不到密钥,解码密钥对象不会提供默认值(例如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
}

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

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

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

最有可能最好的方法是使用IntegerForKey,因为你的" lifeTimeScore"是一个Int,只有你也可以使用ObjectForKey。

答案 1 :(得分:1)

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

    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)

}