In a game I'm developing with Swift, I want the player to be able to choose a background in a shop-like scene and thus change the background of every SKScene. I'm trying to accomplish this using UserDefaults, but for some reason it isn't working. Here's the important code of the Shop Scene (I removed the irrelevant code):
import SpriteKit
class ShopScene: SKScene {
var backNumber = 90
var backRemainder = 0
var background = SKSpriteNode()
var backName:String = "back1"
override func didMove(to view: SKView) {
background.texture = SKTexture(imageNamed: "\(backName)")
background.size = self.size
self.addChild(background)
let nextButton: NButton = NButton(defaultButtonImage: "next", activeButtonImage: "nextP", buttonAction: nextAction)
addChild(nextButton)
let selectButton: SButton = SButton(defaultButtonImage: "next", activeButtonImage: "nextP", buttonAction: selectAction)
addChild(selectButton)
}
func selectAction() {
UserDefaults.standard.set(backName, forKey: "backSaved")
let sceneToMoveTo = MainMenuScene(size: self.size)
sceneToMoveTo.scaleMode = self.scaleMode
let sceneTransition = SKTransition.fade(withDuration: 0.4)
self.view!.presentScene(sceneToMoveTo, transition: sceneTransition)
}
func nextAction() {
backNumber += 1
backRemainder = backNumber % 3
switch backRemainder {
case 0:
backName = "back1"
case 1:
backName = "back2"
case 2:
backName = "back3"
default:
backName = "back1"
}
background.texture = SKTexture(imageNamed: "\(backName)")
}
}
As you can see, when the select button is pressed backName is saved. Now, this is the relevant code of the Main Menu Scene:
Import SpriteKit
class MainMenuScene: SKScene {
var backName = UserDefaults.standard.string(forKey: "backSaved")
override func didMove(to view: SKView) {
let background = SKSpriteNode(imageNamed: "\(backName)")
background.size = self.size
self.addChild(background)
When the 'Select' button is pressed, you should transition to Main Menu Scene and see that the background is the one you selected. However, I get a red X in a white background when I run this. I've worked before with UserDefaults to save scores, but I can't figure out why it's not working in this case. Any idea on how to pass strings between SKScenes using UserDefaults? Am I doing something wrong?
NOTE: Although Knight0fDragon's answer is marked correct, his answer works for passing a string between scenes but not for saving that value permanently. In you wish to pass the value and save it, check my answer.
I would recommend not doing UserDefaults
, that is meant for preferences to your application. Instead use userData
func selectAction() {
let sceneToMoveTo = MainMenuScene(size: self.size)
sceneToMoveTo.scaleMode = self.scaleMode
sceneToMoveTo.userData = sceneToMoveTo.userData ?? NSMutableDictionary() //This lets us ensure userdata exists
sceneToMoveTo.userData!["backSaved"] = backName
let sceneTransition = SKTransition.fade(withDuration: 0.4)
self.view!.presentScene(sceneToMoveTo, transition: sceneTransition)
}
Then implement it with:
import SpriteKit
class MainMenuScene: SKScene {
lazy var backName:String = {return self.userData?["backSaved"] as? String ?? "back1"}() //This allows us to load backName at the time it is needed, or assign back1 if doesn't exist
override func didMove(to view: SKView) {
let background = SKSpriteNode(imageNamed: backName)
self.addChild(background)
Note, I am not sure if the as? String is needed, try it without it and see if Swift can infer it