I am trying to save a high score using Swift for my SpriteKit game. There are several good examples on StackOverflow, one of which I got to work temporarily, but could not function properly in the Swift file where all of my nodes (and actual game) is located.
*Most of the following code is from a stack overflow answer.
This code I put in a separate file called "HighScore":
import Foundation
class HighScore: NSObject {
var highScore: Int = 0
func encodeWithCoder(aCoder: NSCoder!) {
aCoder.encodeInteger(highScore, forKey: "highScore")
}
init(coder aDecoder: NSCoder!) {
highScore = aDecoder.decodeIntegerForKey("highScore")
}
override init() {
}
}
class SaveHighScore:NSObject {
var documentDirectories:NSArray = []
var documentDirectory:String = ""
var path:String = ""
func ArchiveHighScore(#highScore: HighScore) {
documentDirectories = NSSearchPathForDirectoriesInDomains(.DocumentDirectory, .UserDomainMask, true)
documentDirectory = documentDirectories.objectAtIndex(0) as String
path = documentDirectory.stringByAppendingPathComponent("highScore.archive")
if NSKeyedArchiver.archiveRootObject(highScore, toFile: path) {
println("Success writing to file!")
} else {
println("Unable to write to file!")
}
}
func RetrieveHighScore() -> NSObject {
var dataToRetrieve = HighScore()
documentDirectories = NSSearchPathForDirectoriesInDomains(.DocumentDirectory, .UserDomainMask, true)
documentDirectory = documentDirectories.objectAtIndex(0) as String
path = documentDirectory.stringByAppendingPathComponent("highScore.archive")
if let dataToRetrieve2 = NSKeyedUnarchiver.unarchiveObjectWithFile(path) as? HighScore {
dataToRetrieve = dataToRetrieve2
}
return(dataToRetrieve)
}
}
In the scene where I actually want to get an input and output for the highscore I have:
var Score = HighScore()
override func viewDidLoad() {
super.viewDidLoad()
Score.highScore = 100
SaveHighScore().ArchiveHighScore(highScore: Score)
var retrievedHighScore = SaveHighScore().RetrieveHighScore() as HighScore
println(retrievedHighScore.highScore)
}
So when a particular node passes a "block" the high score should increment accordingly, and then save the number (as long as it is higher than the current highscore.)
func blockRunner() {
Score.highScore = 0
SaveHighScore().ArchiveHighScore(highScore: Score)
var retrievedHighScore = SaveHighScore().RetrieveHighScore() as! HighScore
println(retrievedHighScore.highScore)
for(block, blockStatus) in blockStatuses {
var thisBlock = self.childNodeWithName(block)!
if blockStatus.shouldRunBlock() {
blockStatus.timeGapForNextRun = random()
blockStatus.currentInterval = 0
blockStatus.isRunning = true
}
if blockStatus.isRunning {
if thisBlock.position.x > blockMaxX {
thisBlock.position.x -= CGFloat(groundspeed)
}
else{
thisBlock.position.x = self.origBlockPositionX
blockStatus.isRunning = false
retrievedHighScore.highScore++
if ((retrievedHighScore.highScore % 5) == 0) {
groundspeed++
}
self.scoreText.text = String(retrievedHighScore.highScore++)
}
}else{
blockStatus.currentInterval++
}
}
}
For some reason, it will only increment to 1 and then just display 1 in scoreText, even if it has passed more than one block. If I just declare a normal variable and subtitute it in for retrievedHighScore.highScore++, everything works fine. When I use retrievedHighScore.highScore, it only increments to one and just displays 1 in scoreText, strangely, the 1 isn't even saved.
I really recommend using
NSUserDefaults
in this situation to persist your high score. I also recommend not creating a highscores object for the sake of simply having an Integer variable. You're creating alot of unnecessary overhead by utilizing a class to model a simple Integer.All that code for archiving highscore can be simplified to 1 line:
NSUserDefaults.standardUserDefaults().setInteger(highScore, forKey: "Highscore")
When you need to overwrite the highscore (when a new highscore is to replace the old one) you can simply overwrite the old highscore by calling the above line of code again.
You save alot of work, and your code will perform more efficiently. Using a class to store a single value type in your situation is a terrible choice. Crusty will be mad...