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?
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'm extremely new to Swift and programming in general so please be patient with me while I'm trying to get the hang of this.
I've been following a beginners course in Swift from Treehouse and managed to develop a simple app that generates random quotes. So far so good. Now before moving on to more advanced lessons I figured I'd try to update the existing app just to make sure I get some solid practice before moving on.
So here's my question: I've managed to generate a random number through the GameKit framework, but the problem is that sometimes quotes appear consecutively. How can I avoid this from happening?
Here's my code:
import GameKit
struct FactProvider {
let facts = [
"Ants stretch when they wake up in the morning.",
"Ostriches can run faster than horses.",
"Olympic gold medals are actually made mostly of silver.",
"You are born with 300 bones; by the time you are an adult you will have 206.",
"It takes about 8 minutes for light from the Sun to reach Earth.",
"Some bamboo plants can grow almost a meter in just one day.",
"The state of Florida is bigger than England.",
"Some penguins can leap 2-3 meters out of the water.",
"On average, it takes 66 days to form a new habit.",
"Mammoths still walked the Earth when the Great Pyramid was being built."
]
func randomFact() -> String {
let randomNumber = GKRandomSource.sharedRandom().nextInt(upperBound: facts.count)
return facts[randomNumber]
}
}
You can store last random number or last fact in a variable and check it in your randomFact function. Like this:
var lastRandomNumber = -1
func randomFact() -> String {
let randomNumber = GKRandomSource.sharedRandom().nextInt(upperBound: facts.count)
if randomNumber == lastRandomNumber {
return randomFact()
} else {
lastRandomNumber = randomNumber
return facts[randomNumber]
}
}
Ok, I am new to URL querying and this whole aspect of Swift and need help. As is, I have an iMessage app that contains and SKScene. For the users to take turns playing the game, I need to send the game back and forth in messages within 1 session as I learned here : https://medium.com/lost-bananas/building-an-interactive-imessage-application-for-ios-10-in-swift-7da4a18bdeed.
So far I have my scene all working however Ive poured over Apple's ice cream demo where they send the continuously-built ice cream back and forth, and I cant understand how to "query" everything in my SKScene so I can send the scene.
I'm unclear as to how URLQueryItems work as the documentation does not relate to sprite kit scenes.
Apple queries their "ice cream" in its current state like this:
init?(queryItems: [URLQueryItem]) {
var base: Base?
var scoops: Scoops?
var topping: Topping?
for queryItem in queryItems {
guard let value = queryItem.value else { continue }
if let decodedPart = Base(rawValue: value), queryItem.name == Base.queryItemKey {
base = decodedPart
}
if let decodedPart = Scoops(rawValue: value), queryItem.name == Scoops.queryItemKey {
scoops = decodedPart
}
if let decodedPart = Topping(rawValue: value), queryItem.name == Topping.queryItemKey {
topping = decodedPart
}
}
guard let decodedBase = base else { return nil }
self.base = decodedBase
self.scoops = scoops
self.topping = topping
}
}
fileprivate func composeMessage(with iceCream: IceCream, caption: String, session: MSSession? = nil) -> MSMessage {
var components = URLComponents()
components.queryItems = iceCream.queryItems
let layout = MSMessageTemplateLayout()
layout.image = iceCream.renderSticker(opaque: true)
layout.caption = caption
let message = MSMessage(session: session ?? MSSession())
message.url = components.url!
message.layout = layout
return message
}
}
But I cant find out how to "query" an SKScene. How can I "send" an SKScene back and forth? Is this possible?
You do not need to send an SKScene back and forth :) What you need to do is send the information relating to your game set up - such as number of turns, or whose turn it is, or whatever, as information that can be accessed by your app at the other end to build the scene.
Without knowing more about how your scene is set up and how it interacts with the information received for the other player's session, I can't tell you a lot in terms of specifics. But, what you need to do, if you are using URLQueryItems to pass the information, simply retrieve the list of query items in your scene and set up the scene based on the received values.
If you have specific questions about how this could be done, if you either share the full project, or post the relevant bits of code as to where you send out a message from one player and how the other player receives the information and sets up the scene, I (or somebody else) should be able to help.
Also, if you look at composeMessage in the code you posted above, you will see how in that particular code example the scene/game information was being sent to the other user. At the other end of the process, the received message's URL parameter would be decomposed to get the values for the various query items and then the scene would be set up based on those values. Look at how that is done in order to figure out how your scene should be set up.
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
}
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