SpriteKit: Pourquoi attend-il un tour pour que le score soit mis à jour? (Rapide)

Dans une scène, j'ai ce code:

let defaults = NSUserDefaults.standardUserDefaults() defaults.setInteger(score, forKey: "scoreKey") defaults.synchronize() 

Lorsque l'user entre en contact avec l'espace, le code s'exécute:

  score++ 

Si l'user heurte un obstacle, le GameOverScene prend le relais. Voici le code que j'ai pour GameOverScene pour déplacer le score de scène en scène:

 let defaults = NSUserDefaults.standardUserDefaults() let score = defaults.integerForKey("scoreKey") scoreLabel.text = "\(score)" 

Cependant, il y a un bug dans mon code où le scoreLabel ne met pas à jour son text. Par exemple, supposons qu'un user marque 1 et meurt. Quand il meurt, le gameOverScene viendra et dira que le score était de 1. Puis, disons que l'user clique sur le redémarrage, et marque 5 puis meurt. Dans le GameOverScene, le scoreLabel dira 1.

Aidez-moi, s'il vous plaît!

Vous n'avez plus vraiment besoin d'appeler synchroniser si vous utilisez iOS 8 ou supérieur. Ceci est recommandé par Apple, mais beaucoup de gens le font encore. Alors débarrassez-vous de cette ligne si vous l'utilisez encore.

Mon moyen préféré pour datatables de jeu est d'utiliser une class GameData singleton avec NSCoding. Pas besoin d'append des variables partout et beaucoup plus propre. Je vous conseille de lire ceci.

http://www.raywenderlich.com/63235/how-to-save-your-game-data-tutorial-part-1-of-2

Vous pouvez également intégrer le stockage de valeur de key iCloud de cette façon, il est très facile car similaire aux valeurs par défaut de l'user (voir mon exemple ci-dessous)

De toute façon, pour commencer ici, voici un exemple simple de ce à quoi cela pourrait ressembler.

 import Foundation /// Keys private struct Key { static let encodedData = "encodedData" static let highScore = "highScore" } class GameData: NSObject, NSCoding { // MARK: - Static Properties /// Shared instance static let shared: GameData = { if let decodedData = UserDefaults.standard.object(forKey: Key.encodedData) as? GameData { return gameData } else { print("No data, creating new") return GameData() } } // MARK: - Properties /// Defaults private let localDefaults = UserDefaults.standard private let iCloudDefaults = NSUbiquitousKeyValueStore.default() /// Progress (not saved, no need for saving the score because of the highScore var. Still have here for global single access) var score = 0 /// Progress (saved) var highScore = 0 // MARK: - Init private override init() { super.init() print("GameData init") NotificationCenter.default.addObserver(self, selector: #selector(updateFromCloud), name: NSUbiquitousKeyValueStore.didChangeExternallyNotification, object: iCloudDefaults) iCloudDefaults.synchronize() } // MARK: - Convenience Init convenience required init?(coder decoder: NSCoder) { self.init() print("GameData convenience init") // Progress highScore = decoder.decodeInteger(forKey: Key.highScore) } // MARK: - Encode func encodeWithCoder(encoder: NSCoder) { // Progress encoder.encodeInteger(highScore, forKey: Key.highScore) // MARK: - User Methods /// Save func save() { if score > highScore { highScore = score } saveLocally() saveToCloud() } // MARK: - Internal Methods /// Save locally private func saveLocally() { let encodedData = NSKeyedArchiver.archivedDataWithRootObject(self) localDefaults.setObject(encodedData, forKey: Key.encodedData) } /// Save to icloud private func saveToCloud() { print("Saving to iCloud") // Highscores if (highScore > iCloudDefaults.objectForKey(Key.highScore) as? Int ?? Int()) { iCloudDefaults.setObject(highScore, forKey: Key.highScore) } /// Update from icloud func updateFromCloud() { print("Updating from iCloud") // Highscores highScore = max(highScore, iCloudDefaults.object(forKey: Key.highScore) as? Int ?? Int()) // Save saveLocally() } 

Maintenant dans n'importe quelle scène si vous voulez utiliser le score ou la propriété highScore sauvegardée, vous pouvez par exemple dire

 GameData.shared.score++ 

ou

 scoreLabel.text = "\(GameData.shared.score)" highScoreLabel.text = "\(GameData.shared.highScore)" 

Toutes vos labels de text seront mises à jour immédiatement si vous passez à une nouvelle scène ou mettez à jour la propriété .text. Pas besoin de synchronisation userDefault etc.

L'appel … partagé … initialisera également l'assistant. Si vous voulez charger gameData dès que votre jeu a lancé, vous pouvez appeler

 GameData.shared 

dans votre applicationDelegate ou viewController. Ce n'est probablement pas vraiment nécessaire, mais vous pouvez toujours le faire simplement pour vous assurer que l'assistant est initialisé dès que le jeu est lancé.

Si vous voulez save votre appel

 GameData.shared.save() 

N'oubliez pas de réinitialiser le score à 0 dans votre gameScene.swift dans la méthode ViewDidLoad.

 GameData.shared.score = 0 

Cela devrait rendre votre vie beaucoup plus facile. Si vous voulez utiliser iCloud, tout ce que vous avez à faire est d'aller sur votre cible et vos capacités et d'activer iCloud et de cocher keyValueStorage (pas de données de base). Terminé.

Note: Pour aller encore plus loin, vous pouvez get l'aide KeychainWrapper de JRendel sur gitHub. Au lieu d'utiliser NSUserDefaults pour stocker le gameData encodé que vous utilisez keychain, son simple mort à utiliser.

Vous pouvez sauvegarder votre score comme ci-dessous:

 NSUserDefaults.standardUserDefaults().setInteger(score, forKey: "scoreKey") 

Ensuite, vous pouvez get votre score a été enregistré comme ci-dessous le code:

  if NSUserDefaults.standardUserDefaults().objectForKey("scoreKey") != nil { score = NSUserDefaults.standardUserDefaults().objectForKey("scoreKey") as! Int } scoreLabel.text = "\(score)" 

Pour répondre à la question de votre commentaire sur l'utilisation de la structure globale …

Selon les docs:

Les variables globales sont des variables définies en dehors de toute fonction, méthode, fermeture ou context de type.

Signifie que vous devez définir votre structure juste après les instructions d'import en haut de n'importe quel file.

Vous faites une structure comme pointée depuis le lien que j'ai posté dans les commentaires, comme ceci (j'ai placé la définition de la structure dans le file GameScene.swift):

 struct GlobalData { static var gold = 0; static var coins = 0; static var lives = 0; static var score = 0; } 

Cette structure sera également disponible dans GameOverScene. Donc, avant la transition, vous ferez quelque chose comme:

 GlobalData.score = 20 GlobalData.coins = 10 //etc. 

Et dans votre GameOverScene vous y accéderez comme ceci:

 scoreNode.text = Ssortingng(GlobalData.score)//scoreNode is SKLabelNode