After trying to submit my game to the app store, my app got rejected because of some bug that's working on an iPad 2. I tried to find the problem, and it was because of some high score bug. This happens when the player gets 0 for the first time, right on the scene that shows your score and high score. Here's the code:
var highScoreDefault = NSUserDefaults.standardUserDefaults()
//Right below is the problem
highScore = highScoreDefault.valueForKey("highScore") as NSInteger
Here's the error I get if I get 0 for the first time:
EXC_BAD_INSTRUCTION (code=EXC_1386_INVOP,subcode=0x0)
I keep trying to find a different but a simple way to add the high score, but I can't find it. Please help!
Note: I'm running on Xcode 6.2 in Swift, and this happened on all iOS simulators.
Without more code... Try just make if let statement
something like:
if let value = highScoreDefault.valueForKey("highScore") as? NSInteger {
highScore = value
} else {
highScore = 0
}
Related
So I couldn't find a good answer that works for my problem. I am creating a SpriteKit game on iOS. Im just going to give you guys a quick overview of my setup and then go over the problem. I think that's the best way to explain this.
The Setup:
Basically in my didMove(to view:) method I call two helper functions: setupNodes() and setupLevel(). What these functions do is load the different scenes and SKSpriteNodes into memory and then add them to the scene. Here is the code I use to do this:
func setupNodes(){
doubleVent = (loadForegroundOverlayTemplate("DoubleVent").copy() as! SKSpriteNode)
}
func loadForegroundOverlayTemplate(_ fileName: String) -> SKSpriteNode{
let overlayScene = SKScene(fileNamed: fileName)!
let overlayTemplate = overlayScene.childNode(withName: "ForegroundOverlay")!.copy() as! SKSpriteNode
return overlayTemplate
}
This now stores the scene which is a child of an SKNode called "ForegroundOverlay". I then pass this to a function called "createForegroundOverlay()" like so:
let overlaySprite = doubleVent
createForegroundOverlay(doubleVent)
and then "createForegroundOverlay" adds the SKSpriteNode stored in doubleVent to the scene like so:
func createForegroundOverlay(_ overlayTemplate: SKSpriteNode) {
let foregroundOverlay = overlayTemplate.copy() as? SKSpriteNode
lastOverlayPosition.x = lastOverlayPosition.x + (lastOverlayWidth + ((foregroundOverlay?.size.width)!)/2)
lastOverlayWidth = (foregroundOverlay?.size.width)!/2.0
foregroundOverlay?.position = lastOverlayPosition
//TESTING PURPOSES
if TESTING_NODE_POS{
print("The last overlay position x:\(lastOverlayPosition.x), y:\(lastOverlayPosition.y)")
print("The last overlay width is \(lastOverlayWidth)")
print("Adding a foreground overlay \(count)")
count += 1
}
//TESTING PURPOSES
if TESTING_BOX_OUTLINE {
let foregroundPos = foregroundOverlay?.position
let boxLocation = foregroundOverlay?.childNode(withName: "Box1")?.position
if boxLocation != nil {
print("The location of the box \(String(describing: boxLocation))")
print("The foregroundLocation is \(String(describing: foregroundPos))")
print("last overlay position is \(lastOverlayPosition)")
}
}
//add the foreground overlay as a child of the foreground node
fgNode.addChild(foregroundOverlay!)
}
the variables for positioning - lastOverlayPosition, lastOverlayWidth etc.. - are just properties of my GameScene class used to know where and when to add the overlay that was passed.
The fgNode is a node that I stored from my GameScene.sks file like so:
let worldNode = self.childNode(withName: "World")!
bgNode = worldNode.childNode(withName: "Background")!
bgCityNode = worldNode.childNode(withName: "BackgroundCity")
cityBGOverlayTemplate = (bgCityNode.childNode(withName: "Overlay")!.copy() as! SKNode)
cityOverlayWidth = bgCityNode.calculateAccumulatedFrame().width
backgroundOverlayTemplate = (bgNode.childNode(withName: "Overlay")!.copy() as! SKNode)
backgroundOverlayWidth = backgroundOverlayTemplate.calculateAccumulatedFrame().width
fgNode = worldNode.childNode(withName: "Foreground")!
This was also done in my setupNodes() method.
The problem:
So maybe some of you guys have already seen the problem, but the problem is that when I launch my game it crashes and I get the message:
"Thread 1: EXC_BAD_ACCESS: (code = 1, address = 0x78)"
It is the exact same message every single time a crash occurs. I think I understand what the error is saying. Basically there is a pointer pointing to some location in memory (0x78) that has nothing there. So dereferencing that pointer obviously causes a fault. Here is where I get confused... This only happens about 50% of the time. when I run and build the project it builds successfully every time and then crashes 50% of the time with that error message. Secondly, this occurs at the very beginning. This is odd to me because how can some memory already be freed resulting in a bad pointer at the very beginning of the game. Also if the crash doesn't occur at launch then a crash never occurs except for when I reload the scene after the game over scene is displayed, which is basically the same as the game being relaunched.
With some time I narrowed the problem to one line of code:
fgNode.addChild(foregroundOverlay!)
if I comment this line out, no crash ever occurs (I tried building and running 50 times). The problem must be with the foregroundOverlay variable, which was setup using the code in the setup section of this discussion. So I have no idea how to fix this... Any Ideas??
P.S. it might also be worth noting that adding a child to the scene in this way only noticeably became a problem when I upgraded to xCode 10.0 and that I have considered using zombies, but I don't think that would help since the crash only happens at the launch of the game.
If I was unclear anywhere please let me know.
I have a spritekit game which I have a highscore in. It uses NSUser Default. But I get the highscore 2, and then I close the app completely, and then open it it shows my highscore 2, and then get one as a score. It remains as 2. However, I close the app again and open it, it shows the highscore 1. Why does it do this? This is my code. Does the if condition not work? Note: This is just narrowed down to Highscore code.
import SpriteKit
//In the DidMoveToView function
if let Highscore1 = defaults.stringForKey("Highscore"){
HighScoreLabel.text = "HIGHSCORE: \(Highscore1)"
}
//In the touches began func
//Making what happens when the User Fails and a new highscore is achieved
if Score > highscore {
defaults.setObject("\(Score)", forKey: "Highscore")
}
Thank you in advance
The issue is you are reading the highscore from NSUserDefaults and showing it in the HighScoreLabel. But you didn't assigned/stored the value in highscore variable, because of that it remains at 0. That makes the following condition true when you open the app and plays for the first time:
if Score > highscore {
defaults.setObject("\(Score)", forKey: "Highscore")
}
You need to change the high score reading part like:
if let Highscore1 = defaults.stringForKey("Highscore") {
HighScoreLabel.text = "HIGHSCORE: \(Highscore1)"
// Storing current high score to variable
highscore = Int(Highscore1)
}
currently I'm working on a game project and I've run into an issue where the game's highscore doesn't update unless I quit the app (completely close) and run the game again. In terms of code I've got this written down.
var data = NSUserDefaults.standardUserDefaults()
//this is when the level is over and the highscore is saved
if score > level1HS{
data.setInteger(score, forKey: "level1HS")
data.synchronize()
}
//and this is in the main menu where the highscore is shown
level1text.text = "\(String(level1HS))"
I've also tried replacing the level1text.text = "\(String(level1HS)" with this level1text.text = "\(level1HS)"
Can somebody please help me find a way to have the game update the highscore as soon as it is achieved ? Thank you!
Wherever you're trying to update the high score, you'll need to listen to the notification for when StandardUserDefaults() gets updated. It would look like this:
let defaults = NSUserDefaults.standardUserDefaults()
let center = NSNotificationCenter.defaultCenter()
center.addObserverForName(NSUserDefaultsDidChangeNotification, object: nil, queue: nil) { (notification) -> Void in
let newScore = defaults.objectForKey("level1HS")
}
Have you tried doing the following?
var level1HS = 10
NSUserDefaults.standardUserDefaults().setObject(highestScore, forKey:"level1HS")
NSUserDefaults.standardUserDefaults().synchronize()
Your problem is probably that you are not using the .synchronize() method.
Also, check out this question:
SpriteKit High Score
my NSUserDefaults doesn't save my game high score properly. When am playing(testing) the game it works ok but when i quit(terminate) and restart or when i transition from scene to scene my high score goes back to 0. can anyone help me fix this issue? thank in advance.
var score = 6
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")
The problem is that this statement, which appears twice, does nothing at all:
NSUserDefaults.standardUserDefaults().integerForKey("highscore")
It fetches the "highscore" key's value from user defaults, but immediately throws it away, because you are not assigning the result to anything, like this:
let highscore = NSUserDefaults.standardUserDefaults().integerForKey("highscore")
Thus, even though you are successfully storing the high score in user defaults, you are never retrieving it - so there is no basis for your claim that it is not being saved, because you have given yourself no way of knowing whether or not is has been saved.
Question:
When loading scores from a combined leaderboard, how do I tell which source leaderboard the score originated from?
Background:
I am new to using iOS Game Center. I currently have three leaderboards; let's call them "level1", "level2" and a combined one called "aggregate". I submit scores like this, and it seems to work:
let score = GKScore(leaderboardIdentifier: "level1", player: localPlayer)
score.value = // Score here...
score.context = // Context here...
GKScore.reportScores([gcScore]) { error in
// Error handling...
}
When I query to find a list of the top ten scores on all levels, I do this:
var leaderboard = GKLeaderboard()
leaderboard.identifier = "aggregate"
leaderboard.range = NSRange(location: 1, length: 10)
leaderboard.loadScoresWithCompletionHandler() { objects, error in
// Error handling removed...
let scores = objects as? [GKScore]
// Examine the scores, looking at each score.leaderboardIdentifier to
// determine where it originally came from.
}
Unfortunately, the leaderboardIdentifier on each score has the value "aggregate". I'm not sure if I'm doing something wrong or if I'm missing something, but my understanding of the leaderboard documentation makes it sound like this should be set to the original leaderboard value.
Any thoughts?