2016-01-19 9 views
1

Вот дилемма.Использование NSCoding для получения результата и оценки LifeTime в Swift/SpriteKit

У меня есть класс, который управляет рекордами для игры в Sprite Kit. Функциональность «высокая оценка» работает, однако «вычисление всех баллов».

В игре необходимо собрать Ягоды. Я хочу следить за пожизненным количеством ягод.

Вот мой класс для управления высокими баллами.

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 

Это действительно сбивает с толку, и я на самом деле на остановке. Вот класс GameModel, который вызывает класс Score. Когда мне нужно создать экземпляр 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()?

Спасибо.

ответ

3

Причина вы получаете сбой, потому что декодирования объектов для ключей не обеспечивает значения по умолчанию, если не будет найден ключ (например decodeBoolForKey по умолчанию ложно)

Поэтому вам нужно настроить свой код. Как другой член также сообщил, что изменить код

let lifeTimeScore = aDecoder.decodeIntegerForKey(PropertyKey.lifeTimeScoreKey) 

, который автоматически устанавливается значение по умолчанию, если ключ не найден, потому что он знает, что вы пытаетесь расшифровать целое.

Если вы хотите использовать decodeObjectForKey, вам нужно сначала выполнить проверку nil, потому что значение по умолчанию отсутствует, потому что вы декодируете «AnyObject». Линия

... as! Int 

заставит разворачивать objectForKey как Int, но она не может быть там, и, следовательно, ноль и чем вы получите аварии. Либо изменить его слишком

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, используйте ObjectForKey, когда вы тоже.

+0

Как я могу задать вопрос, который позволяет (если вам интересно) выполнить процесс сохранения и получения некоторых игровых данных? – Confused

+0

Что значит разрешения? Мне нравится делать это с помощью класса Singleton, используя NSCoding точно так же, как этот вопрос. Посмотрите здесь сначала и google о NSCoding. Также проверьте 2 ссылки ниже. Если вы все еще застряли, задайте вопрос с тем, что вы хотите достичь, и какой код у вас есть на данный момент http://stackoverflow.com/questions/36725052/how-to-store-user-data-in-game-swift-spritekit/36746969 # 36746969 http://stackoverflow.com/questions/33684540/spritekit-why-does-it-wait-one-round-for-the-score-to-update-swift/33895778#33895778 – crashoverride777

+0

Я был прочитав, что я могу о NSCoding за 2 полных дня, и прохожу через него с кем-то по электронной почте. И я гониг назад. – Confused

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) 

}