Swift iOS - Set highscore in gameScene - ios

I'm making some great progress with my first iOS game app, currently I'm working on the high score element and if anyone would be so kind as to help guide me how to use the correct code.
I've followed a few tutorials with mixed results, so far I'm increasing the score count by 1 for each screen press (will be changed but using for the purpose for this post), this works fine however I need some help saving both the high score using NSUserDefaults and also displaying the high score.
Below is the extract of my code, currently the score works fine however the high score remains at 0 and doesn't increase. Any help would be appreciated!
Here is my code.....
Thank you.
EDITED WITH REWORKED CODE
// in gameScene:
var scoreLabelNode = SKLabelNode()
var score = NSInteger()
var highScoreLabelNode = SKLabelNode()
var highScore = NSInteger()
// in did move to view
score = 0
scoreLabelNode.fontName = "Helvetica-Bold"
scoreLabelNode.position = CGPoint(x: self.frame.size.width / 2.8, y: self.frame.size.height / 1.2 )
scoreLabelNode.fontSize = 20
scoreLabelNode.alpha = 0.2
scoreLabelNode.zPosition = -30
scoreLabelNode.text = "Score \(score)"
self.addChild(scoreLabelNode)
// start of high score
var highScoreDefault = NSUserDefaults.standardUserDefaults()
if (highScoreDefault.valueForKey("highScore") != nil){
highScore = highScoreDefault.valueForKey("highscore") as! NSInteger!
highScoreLabelNode.text = NSString(format: "highscore : %i", highScore) as String
}
// highScore = 0
// if (score > highScore) {
// highScore = score
// highScoreLabelNode.text = NSString(format: "highscore : %i", highScore) as String
// }
highScoreLabelNode.fontName = "Helvetica-Bold"
highScoreLabelNode.position = CGPoint(x: self.frame.size.width / 2.6, y: self.frame.size.height / 1.3 )
highScoreLabelNode.fontSize = 20
highScoreLabelNode.alpha = 0.2
highScoreLabelNode.zPosition = -30
highScoreLabelNode.text = "High Score \(score)"
self.addChild(highScoreLabelNode)
// in touches began
let incrementScore = SKAction.runBlock ({
++self.score
self.scoreLabelNode.text = "Score \(self.score)"
})
self.runAction(incrementScore)
// in update before each frame
// highScore = 0
if (score > highScore) {
highScore = score
highScoreLabelNode.text = NSString(format: "highscore : %i", highScore) as String
}

To me it seems you dont ever call
if (score > highScore) {
highScore = score
highScoreLabelNode.text = NSString(format: "highscore : %i", highScore) as String
}
again anywhere, which is where your highscore is set? Is it called somewhere that is not indicated in the code you have provided?

From what I can see you are not saving the highscore to User Defaults with NSUserDefaults.standardUserDefaults.setObject(), also, I'm not sure why you are setting highScore to 0

Related

NSUserDefaults Saving Score

I am looking for some advice when it comes to saving a score in my app - I currently have a coin count working in which through my HUD class when I collide through Coins the value increases accordingly. My next step is trying to initiate a high-score that initiates, after some research I think the topic I am looking into is based around NSUserDefaults..
The current code :
import SpriteKit
class HUD: SKNode {
//An SKLabelNode to print the coin score:
let coinCountText = SKLabelNode(text: "000000")
func createHUDNodes(){
// Configure the coin text label:
coinCountText.fontName = "STHupo-Heavy-Italic"
let coinTextPosition = CGPoint(x: -cameraOrigin.x + 770, y: coinPosition.y)
coinCountText.position = coinTextPosition
coinCountText.horizontalAlignmentMode = SKLabelHorizontalAlignmentMode.left
coinCountText.verticalAlignmentMode = SKLabelVerticalAlignmentMode.center
// Add the text label and coin icon to the HUD:
self.addChild(coinCountText)
}
func setCoinCountDisplay(newCoinCount:Int) {
let formatter = NumberFormatter()
let number = NSNumber(value: newCoinCount)
formatter.minimumIntegerDigits = 6
if let coinStr =
formatter.string(from: number) {
// Update the label node with the new count:
coinCountText.text = coinStr
}
}
Working in the HUD class I think naturally I would need to create another 'SKlabelNode'for the highscore node which I can duplicate from the code that works above.. my problem is how can I implement a highscore feature in the app, as when closed and re-opening the application it will remember the highscore..
I assume the following code would need to be added but I do not understand how it ties together in the class.
var currentHighScore =
NSUserDefaults.standardUserDefaults().integerForKey("coin_highscore")
var highScoreLabel = SKLabelNode(fontNamed:"STHupo-Heavy-Italic")
highScoreLabel.text = ""
highScoreLabel.fontSize = 45
highScoreLabel.position = CGPoint(x: viewSize.width * 0.5, y: viewSize.height * 0.30)
self.addChild(highScoreLabel)
if (score > currentHighScore) {
NSUserDefaults.standardUserDefaults().setInteger(score, forKey: "player_highscore")
NSUserDefaults.standardUserDefaults().synchronize()
highScoreLabel.text = "New High Score: \(score) !"
} else {
highScoreLabel.text = "Better Luck Next Time: \(score)"
}
}
Any advice on how I could integrate this code would be much appreciated.
, AJ
Revised :
else if nodeTouched.name == "returnToMenu"{
// Transition to the main menu scene
self.view?.presentScene(MenuScene(size: self.size),transition.crossFade(withDuration: 0.6))
let currentHighScore = UserDefaults.standard.integer(forKey: "Player_highscore")
let highScoreLabel = SKLabelNode(fontNamed:"STHupo-Heavy-Italic")
highScoreLabel.text = "Value: \ (String(describing: coin.value))"
highScoreLabel.text = ""
highScoreLabel.fontSize = 45
highScoreLabel.zPosition = 100
highScoreLabel.position = CGPoint(x: 50, y: 50)
self.addChild(highScoreLabel)
if (coin.value > currentHighScore) {
UserDefaults.standard.set(coin.value, forKey: "Player_highscore")
UserDefaults.standard.synchronize()
highScoreLabel.text = "New High Value: \(String(describing: coin.value)) !"
} else {
highScoreLabel.text = "Better Luck Next Time: \(String(describing: coin.value))"
}
}
}
}
I am assuming it's the referencing to my original HUD that is causing the problem? In my coin class I declared the coin.value = 5, each time the coin is collected by coinCountDisplay increases accordingly. However still does not show - :/
Add a property to your view controller with both get and set and use it in all the places. The get method should get the value from UserDefaults and the set should update the value in UserDefaults.
var coinHighscore: Int {
get {
UserDefaults.standard.integer(forKey: "coin_highscore")
}
set {
UserDefaults.standard.set(newValue, forKey: "coin_highscore")
}
}
Extending on Frankenstein's answer, IF you want to use Swift 5 you can use Property Wrappers to avoid writing get and set every time:
#propertyWrapper struct UserDefaultsBacked<Value> {
let key: String
let defaultValue: Value
var storage: UserDefaults = .standard
var wrappedValue: Value {
get {
let value = storage.value(forKey: key) as? Value
return value ?? defaultValue
}
set {
storage.setValue(newValue, forKey: key)
}
}
}
And use it like this:
#UserDefaultsBacked(key: "coin_highscore", defaultValue: 0)
var coinHighscore: Int

Increment score in Sprite Kit to show in HUD

I know I'm close in getting the getting the score to increment in my game.
When I explicitly change the code to add a integer (5 for example) instead of "%d", the score shows in the HUD when a coin is touched :
func didBeginContact(contact: SKPhysicsContact) {
lblScore.text = String(format: "%d", GameState.sharedInstance.score)
}
to:
func didBeginContact(contact: SKPhysicsContact) {
lblScore.text = String(format: "5", GameState.sharedInstance.score)
}
However if I leave the "%d", then nothing happens. I'm not sure how to increment the score in the HUD or where to make changes.
Here's the rest of the code.
GameScene.swift:
struct PhysicsCategory {
static let None: UInt32 = 0
static let Player: UInt32 = 0b1
static let CoinNormal: UInt32 = 0b1000
static let CoinSpecial: UInt32 = 0b10000
}
class GameScene: SKScene, SKPhysicsContactDelegate {
// HUD
var hudNode: SKNode!
var lblScore: SKLabelNode!
var lblCoins: SKLabelNode!
override func didMoveToView(view: SKView) {
// HUD
hudNode = SKNode()
hudNode.zPosition = 1000
cameraNode.addChild(hudNode)
// Coins
// 1
let coin = SKSpriteNode(imageNamed: "powerup05_1")
coin.position = convertPoint(CGPoint(x: 300, y: self.size.height-100), toNode: cameraNode)
coin.zPosition = 1000
hudNode.addChild(coin)
// 2
lblCoins = SKLabelNode(fontNamed: "ChalkboardSE-Bold")
lblCoins.fontSize = 70
lblCoins.fontColor = SKColor.whiteColor()
lblCoins.position = convertPoint(CGPoint(x: 375, y: self.size.height-100), toNode: cameraNode)
lblCoins.horizontalAlignmentMode = SKLabelHorizontalAlignmentMode.Left
lblCoins.zPosition = 1000
// 3
lblCoins.text = String(format: "X %d", GameState.sharedInstance.coins)
hudNode.addChild(lblCoins)
// Score
// 4
lblScore = SKLabelNode(fontNamed: "ChalkboardSE-Bold")
lblScore.fontSize = 70
lblScore.fontColor = SKColor.whiteColor()
lblScore.position = convertPoint(CGPoint(x: self.size.width-325, y: self.size.height-100), toNode: cameraNode)
lblScore.horizontalAlignmentMode = SKLabelHorizontalAlignmentMode.Right
lblScore.zPosition = 1000
// 5
lblScore.text = "0"
hudNode.addChild(lblScore)
}
}
func didBeginContact(contact: SKPhysicsContact) {
lblScore.text = String(format: "%d", GameState.sharedInstance.score)
}
GameState.swift:
class GameState {
var score: Int
var highScore: Int
var coins: Int
init() {
// Init
score = 0
highScore = 0
coins = 0
// Load game state
let defaults = NSUserDefaults.standardUserDefaults()
highScore = defaults.integerForKey("highScore")
coins = defaults.integerForKey("coins")
}
func saveState() {
// Update highScore if the current score is greater
highScore = max(score, highScore)
score = max(score, highScore)
// Store in user defaults
let defaults = NSUserDefaults.standardUserDefaults()
defaults.setInteger(highScore, forKey: "highScore")
defaults.setInteger(coins, forKey: "coins")
NSUserDefaults.standardUserDefaults().synchronize()
}
class var sharedInstance: GameState {
struct Singleton {
static let instance = GameState()
}
return Singleton.instance
}
}
As I can see you are using GameState.sharedInstance.score to store a score. But you never update it. You have to increment the score each time when player scores. This part:
lblScore.text = String(format: "%d", GameState.sharedInstance.score)
only reads from a score variable.
Also this part doesn't make sense:
highScore = max(score, highScore)
score = max(score, highScore) //remove this
score variable is not the same as highscore variable.
I was able to increment the score by this line:
GameState.sharedInstance.score += 1
Or replace 1 with another number by how many points you want to increment.

How do I increment and save a int value in Swift?

Im trying to keep track of how many times the user loses in my game. So for every loss it goes up by 1. I also want to save it too so the user could see how many total times they lost. Right now the code I have it works the first time and goes to one but if I lose in the game after that it just stays at 1. What am I doing wrong? Thanks!
class level1: SKScene, SKPhysicsContactDelegate, GKGameCenterControllerDelegate {
var deathScore = 0
override func didMoveToView(view: SKView) {
var deathLabel = SKLabelNode()
deathLabel = SKLabelNode(fontNamed: "LadyIce-3D")
deathLabel.text = "100"
deathLabel.zPosition = 14
deathLabel.fontSize = 100
deathLabel.fontColor = SKColor.darkTextColor()
deathLabel.position = CGPointMake(self.size.width / 1.1, self.size.height / 1.4)
deathLabel.hidden = true
self.addChild(deathLabel)
}
if firstBody.categoryBitMask == HeroCategory && fourthBody.categoryBitMask == GameOverCategory {
deathScore++
deathLabel.hidden = false
let defaults = NSUserDefaults()
let saveDeaths = NSUserDefaults().integerForKey("saveNumberOfDeaths")
if(deathScore > saveDeaths)
{
NSUserDefaults().setInteger(saveDeaths, forKey: "saveNumberOfDeaths")
}
var showNumberOfDeaths = defaults.integerForKey("saveNumberOfDeaths")
deathLabel.text = String(showNumberOfDeaths)
}
}
You are declaring a new var deathScore everytime, initialized with 0 and incrementing it. It will always be 1.
UserDefaults.standard.set(UserDefaults.standard.integer(forKey: "saveNumberOfDeaths")+1, forKey: "saveNumberOfDeaths")
deathLabel.text = String(UserDefaults.standard.integer(forKey: "saveNumberOfDeaths"))

Saving and Loading Up A Highscore Using Swift

I am trying to make a high score view. I load the score and high score up and compare the two to see if the score should be set as the new high score. When the score is a negative number, it works fine, but when the number is positive but less than the current high score or bigger than the high score it seems to add the two numbers together. It also seems to subtract 1 from the two? I'm not really sure whats happening. Thanks for the help!
The high score view viewDidLoad (the only code for the view) :
override func viewDidLoad() {
//Load Score
let defaults: NSUserDefaults = NSUserDefaults.standardUserDefaults()
var score = defaults.valueForKey("Score")?.integerValue ?? 0
defaults.synchronize()
Score = score
//Load Highscore
let SecondDefaults: NSUserDefaults = NSUserDefaults.standardUserDefaults()
var highscore = SecondDefaults.valueForKey("Highscore")?.integerValue ?? 0
SecondDefaults.synchronize()
Highscore = highscore
//Set Score Text
ScoreString = String(Score)
Scorelabel.text = ScoreString
//Update Highscore if Score is bigger
if Score > Highscore {
//Set Highscore to Score
Highscore += Score
//Save Highscore
let SecondDefaults: NSUserDefaults = NSUserDefaults.standardUserDefaults()
SecondDefaults.setObject(Highscore, forKey: "Highscore")
SecondDefaults.synchronize()
//Set Highscore Text
HighscoreString = String(Highscore)
HighscoreLabel.text = HighscoreString
NewHighscoreLabel.text = "New Highscore"
}
//Set Highscore Text if Score is smaller
else if Highscore >= Score {
HighscoreString = String(Highscore)
HighscoreLabel.text = HighscoreString
}
}}
Issue is with this code:
Highscore += Score
You are adding HighScore and Score then assigning back to HighScore. Change that to:
Highscore = Score

How to reset high score for NSUserDefaults in SpriteKit with Swift

I have set a high score using NSUserDefaults where if the object makes contact with a block, the game ends. If the score is greater than the high score, it updates the high score with the new value.
How would I reset the high score back to zero with a button?
func didBeginContact(contact: SKPhysicsContact) {
if moving.speed > 0 {
if ( contact.bodyA.categoryBitMask & scoreCategory ) == scoreCategory || ( contact.bodyB.categoryBitMask & scoreCategory ) == scoreCategory {
// Balloon has contact with score entity
score++
scoreLabelNode.text = String(score)
// Add a little visual feedback for the score increment
scoreLabelNode.runAction(SKAction.sequence([SKAction.scaleTo(1.5, duration:NSTimeInterval(0.1)), SKAction.scaleTo(1.0, duration:NSTimeInterval(0.1))]))
} else {
moving.speed = 0
balloon.physicsBody?.collisionBitMask = blockCategory
NSUserDefaults.standardUserDefaults().integerForKey("highscore")
//Check if score is higher than NSUserDefaults stored value and change NSUserDefaults stored value if it's true
if score > NSUserDefaults.standardUserDefaults().integerForKey("highscore")
{
NSUserDefaults.standardUserDefaults().setInteger(score, forKey: "highscore")
NSUserDefaults.standardUserDefaults().synchronize()
}
NSUserDefaults.standardUserDefaults().integerForKey("highscore")
var highscoreShow = defaults.integerForKey("highscore")
highscoreLabelNode = SKLabelNode(fontNamed: "MarkerFelt-Wide")
highscoreLabelNode.position = CGPointMake(CGRectGetMidX(self.frame), 530)
highscoreLabelNode.zPosition = 10
highscoreLabelNode.fontSize = 18
highscoreLabelNode.text = "Highscore: \(highscoreShow)"
self.addChild(highscoreLabelNode)
gameoverLabelNode = SKLabelNode(fontNamed: "MarkerFelt-Wide")
gameoverLabelNode.position = CGPointMake(CGRectGetMidX(self.frame), CGRectGetMidY(self.frame))
gameoverLabelNode.zPosition = 10
gameoverLabelNode.text = "GameOver"
self.addChild(gameoverLabelNode)
returnBtn.position = CGPointMake(CGRectGetMidX(self.frame), 250)
returnBtn.zPosition = 5
returnBtn.setScale(0.5)
self.addChild(returnBtn)
println("end the game")
self.canRestart = true
}
}
}
To reset the high score back to zero all you need to use is:
//Sets the integer value for the key "highscore" to be equal to 0
NSUserDefaults.standardUserDefaults().setInteger(0, forKey: "highscore")
//Synchronizes the NSUserDefaults
NSUserDefaults.standardUserDefaults().synchronize()

Resources