在 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。
进退两难。
我有一个 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。