SpriteKit:为什么要等待一轮才能更新分数?(迅速) [英] SpriteKit: Why does it wait one round for the score to update? (Swift)

查看:28
本文介绍了SpriteKit:为什么要等待一轮才能更新分数?(迅速)的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在一个场景中,我有这个代码:

In one scene, I have this code:

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

defaults.synchronize()

当用户接触到间隙时,代码运行:

When the user makes contact with the gap, the code runs:

 score++

如果用户碰到障碍物,GameOverScene 会接管.这是我为 GameOverScene 将分数移动到场景的代码:

If the user hits an obstacle, the GameOverScene takes over. Here's the code I have for the GameOverScene to move the score to scene to scene:

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

但是,我的代码中有一个错误,即 scoreLabel 没有更新其文本.例如,假设用户得分 1 并死亡.当他死时,gameOverScene 会出现并说得分为 1.然后,假设用户单击重新启动,得分 5 然后死亡.在 GameOverScene 中, scoreLabel 会显示 1.

However, there's a bug in my code where the scoreLabel doesn't update its text. For example, let's say a user scores 1 and dies. When he dies, the gameOverScene will come up and say that the score was 1. Then, lets say the user clicks restart, and scores 5 and then dies. In the GameOverScene, the scoreLabel will say 1.

请帮帮我!

推荐答案

如果你使用 iOS 8 或更高版本,你不再需要真正调用 synchronize.这是Apple推荐的,但很多人仍然这样做.因此,如果您仍然使用它,请摆脱那条线.

You don't need to really call synchronise anymore if you use iOS 8 or above. This is recommended by Apple, yet a lot of people still do it. So get rid of that line if you still use it.

我对游戏数据的首选方式是使用带有 NSCoding 的单例 GameData 类.无需到处添加变量,而且更干净.我建议你阅读这篇文章.

My preferred way for game data is using a singleton GameData class with NSCoding. No need to add variables all over the place and much cleaner. I advise you reading this.

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

您也可以通过这种方式集成 iCloud 键值存储,这非常简单,因为它类似于用户默认设置(请参阅下面的示例)

You can also integrate iCloud key value storage that way, it is very easy as its similar to user defaults (see my example below)

无论如何,从这里开始是一个简单的例子,说明它的外观.

Anyway to start you off here is a simple example of how this could look.

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()
}

现在在任何场景中,如果您想使用 score 或保存的 highScore 属性,例如您可以说

Now in any scene if you want to use the score or saved highScore property you for example could say

GameData.shared.score++

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

如果您转换到新场景或更新 .text 属性,您的所有文本标签都会立即更新.不需要 userDefault 同步等.

All your text labels will be updated immediately if you transition to a new scene or update the .text property. No need for userDefault sync etc.

调用 ...shared... 也会初始化助手.如果您想在游戏启动后立即加载 gameData,您可以调用

Calling ...shared... will also initialise the helper. If you want to load gameData as soon as your game has launched you could call

GameData.shared

在您的 appDelegate 或 viewController 中.它可能并不真正需要,但您仍然可以这样做,以确保在游戏启动后立即初始化助手.

in your appDelegate or viewController. Its probably not really needed but your could still do it just to ensure the helper is initialised as soon as the game is launched.

如果你想保存你的电话

GameData.shared.save()

请记住在您的 gameScene.swift 中的 ViewDidLoad 方法中将分数重置为 0.

Just remember to reset the score back to 0 in your gameScene.swift in the ViewDidLoad method.

GameData.shared.score = 0

这应该会让您的生活更轻松.如果你想使用 iCloud,你所要做的就是找到你的目标和能力,打开 iCloud 并勾选 keyValueStorage(不是核心数据).完成.

This should make your life much easier. If you want to use iCloud all you have to do is go to your target and capabilities and turn on iCloud and tick keyValueStorage (not core data). Done.

注意:更进一步,您可以从 gitHub 上的 JRendel 获得 KeychainWrapper 助手.而不是使用 NSUserDefaults 来存储您使用钥匙串编码的游戏数据,它使用起来非常简单.

Note: To take it even a step further you could get the KeychainWrapper helper from JRendel on gitHub. Than instead of using NSUserDefaults to store the encoded gameData you use keychain, its dead simple to use.

这篇关于SpriteKit:为什么要等待一轮才能更新分数?(迅速)的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆