Reputation: 491
In my GameScene I have a variable called "score" which I want to use in the MenuScene to display the latest score. I have tried it with the UserDefaults but I didn't really understand it and it didn't work. Could someone tell me how I have to declare the score variable so I can use it in other scenes, and it would also be nice if someone could show me a way how to save a variable so I can save the highscore.
I've tried this:
GameScene:
let userDefaults = UserDefaults.standard // Before didMove func
UserDefaults.setValue(score, forKey: "Score") // in gameOver func
MenuScene:
var score = SKLabelNode()
var scoreNumber = UserDefaults.standard.integer(forKey: "Score")
If I run the Game it loads the MenuScene and I can press on play and it goes to the GameScene. If I die in the GameScene, the game crashes and i get this error: "Terminating app due to uncaught exception 'NSUnknownKeyException', reason: '[ setValue:forUndefinedKey:]: this class is not key value coding-compliant for the key Score.'"
Upvotes: 0
Views: 490
Reputation: 2547
If you use user defaults correctly you should be able to read and write from any object as UserDefaults.standard
is a singleton. Something along these lines should work:
private let scoreUserDefaultsKey = "scoreUserDefaultsKey"
func save(score: Int)
{
let userDefaults = UserDefaults.standard
userDefaults.set(score, forKey: scoreUserDefaultsKey)
userDefaults.synchronize()
}
func read() -> Int
{
return UserDefaults.standard.integer(forKey: scoreUserDefaultsKey)
}
class GameScene1: SKScene {
func saveFromGameScene1(score: Int) {
save(score: score)
}
}
class GameScene2: SKScene {
func readFromGameScene2() -> Int {
return read()
}
}
Said that you could have a much nicer architecture implementing a ScoreManager object and inject it (instead of having hard dependencies on a singleton) into your game scenes.
UPDATE:
Just seen your code snippet. You need to use set
, not setValue
. setValue
is not a method of UserDefaults but is a method of NSObject for KVO.
func setValue(_ value: Any?, forKey key: String)
Sets the property of the receiver specified by a given key to a given value. If key identifies a to-one relationship, relate the object specified by value to the receiver, unrelating the previously related object if there was one. Given a collection object and a key that identifies a to-many relationship, relate the objects contained in the collection to the receiver, unrelating previously related objects if there were any.
The search pattern that setValue:forKey: uses is described in Accessor Search Patterns in Key-Value Coding Programming Guide.
In a reference-counted environment, if the instance variable is accessed directly, value is retained.
That is not what you're looking for ;)
Also after you call set
make sure to call synchronize
as well to be certain that the value is stored immediately.
I hope this helps.
Upvotes: 1
Reputation: 620
You could just move your var gameScore
from being global, meaning removing it from GameScene
, and make it universal by moving it in between import SpriteKit
and class GameScene
. After that, you could just use NSUserDefault
just as the others have suggested.
UPDATE
ok so for my GameOver scenes, i usually do something like this within the class
let defaults = NSUserDefaults()
var highScoreNumber = defaults.integerForKey("highScoreSaved")
If gameScore > highScoreNumber{
highScoreNumber = gameScore
defaults.setInteger(highScoreNumber, forKey: "highScoreSaved")
}
You can just do this in your MenuScene instead.
Upvotes: 0
Reputation: 55
I don't think you should use UserDefaults to do this,
first reason that I wanna ask you, if someone close the app, maybe they kill the app when they are playing, how you make the deal with the score?
Definitely, you should not keep this score, right? or you should cache all data of the game, then when the player goes into the game again, you could back to the state when the app was closed. But I think it is so complex to do this.
So I think you should put this score globally or easier to control this is just pass the score from the GameScene
to the maybe called ResultScene
.
I do suppose your game have these scenes:
LoginScene
MapScene
GameScene
ResultScene
RankScene
so you just use the score in the GameScene
and ResultScene
in real time, so you just do this in the GameScene
:
var score : Int/CGFloat = 0;
func enterFrame(){
//if they got the goal, then
// score = score + 1 or much more;
}
func gameOver(){
let resultScene = ResultScene(score: score);
// or you could just create the empty ResultScene, and pass the score via a public func of the ResultScene
}
then you could show the score in the ResultScene
.
if you are just making a server-less game, you should keep the scores into the UserDefaults
or CoreData
or SQLite
etc.
then you can show the scores in some scenes about the history records
.
Upvotes: 0
Reputation: 79
If you want a dynamic variable in difference viewcontroller. And use it all the time. You make create in Appdelegate.
In your AppDelegate.swift
var yourVariableName: Any = "Value"
When you wanna use it or change it. You
let app: AppDelegate = UIApplication.shared.delegate as! AppDelegate
print(app.youVariableName)
If you wanna observe the variable value change and do some Action. You make setting up a didSet function.
var yourVariableName: Any = "VALUE" {didSet{observeValueChangeFunction()}}
func didSet{observeValueChangeFunction() {
print("New value: \(yourVariableName)")
}
Upvotes: 0