Creating a SpriteKit / GameKit Leaderboard in a Specific Scene

I am new to Swift, and I am having trouble integrating a leader into my game. I just watched the tutorial: โ€œLeaders of the game center! (Swift 2 in Xcode), in which all GameCenter data went through one kind of application. In my game, I want the user to play the game, and only when they are on a certain SKScene , they will have access to GameCenter.

So, for example, on GameOverScene they will be authenticated by the user, and they will also be able to upload their high score. I think that I also lack some differences between the GameViewController (where the whole logic of the tutorials is located) and one of my many scenes that I made.

Here is my code where I try to use GKGameCenterControllerDelegate in GKGameCenterControllerDelegate and create various functions to achieve GameCenter. The call is made when the user types a certain label in the view: (this obviously does not work, as I try to access the scene in the following lines: self.presentViewController(view!, animated:true, completion: nil)


 class GameOverScene: SKScene, GKGameCenterControllerDelegate { init(size: CGSize, theScore:Int) { score = theScore super.init(size: size) } ... override func didMoveToView(view: SKView) { authPlayer() leaderboardLabel.text = "Tap for Leaderboard" leaderboardLabel.fontSize = 12 leaderboardLabel.fontColor = SKColor.redColor() leaderboardLabel.position = CGPoint(x: size.width*0.85, y: size.height*0.1) addChild(leaderboardLabel) ... override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) { for touch : AnyObject in touches { let location = touch.locationInNode(self) if(CGRectContainsPoint(leaderBoardLabel.frame, location)){ saveHighScore(score) showLeaderBoard() } } } func authPlayer(){ //Create a play let localPlayer = GKLocalPlayer.localPlayer() //See if signed in or not localPlayer.authenticateHandler = { //A view controller and an error handler (view,error) in //If there is a view to work with if view != nil { self.presentViewController(view!, animated:true, completion: nil) //we dont want a completion handler } else{ print(GKLocalPlayer.localPlayer().authenticated) } } } //Call this when ur highscore should be saved func saveHighScore(number:Int){ if(GKLocalPlayer.localPlayer().authenticated){ let scoreReporter = GKScore(leaderboardIdentifier: "scoreBoard") scoreReporter.value = Int64(number) let scoreArray: [GKScore] = [scoreReporter] GKScore.reportScores(scoreArray, withCompletionHandler: nil) } } func showLeaderBoard(){ let viewController = self.view.window?.rootViewController let gcvc = GKGameCenterViewController() gcvc.gameCenterDelegate = self viewController?.presentViewController(gcvc, animated: true, completion: nil) } func gameCenterViewControllerDidFinish(gameCenterViewController: GKGameCenterViewController) { gameCenterViewController.dismissViewControllerAnimated(true, completion: nil) } 

Any advice on how I could do this would be great, I think I can mix the whole scene / view controller and cause problems. Thanks!

+8
swift sprite-kit gamekit game-center-leaderboard
source share
2 answers

This answer is partially carried over to where we left off in the comments, since you did not publish all your code, I canโ€™t say exactly where exactly your hang occurred, but this is what I put together with a separate manual (this is a slightly different version the code you are posting):

The author of most of the code:

https://www.reddit.com/r/swift/comments/3q5owv/how_to_add_a_leaderboard_in_spritekit_and_swift_20/

GameViewController.swift:

 import UIKit import SpriteKit import GameKit class GameViewController: UIViewController { func authenticateLocalPlayer() { let localPlayer = GKLocalPlayer.localPlayer() localPlayer.authenticateHandler = {(viewController, error) -> Void in if (viewController != nil) { self.presentViewController(viewController!, animated: true, completion: nil) } else { print((GKLocalPlayer.localPlayer().authenticated)) } } } override func viewDidLoad() { super.viewDidLoad() /////authentication////// authenticateLocalPlayer() //... The rest of the default code } //... The rest of the default code } 

GameScene.swift (or depending on which scene you want to use the GC):


 import SpriteKit import GameKit import UIKit // Global scope (I generally put these in a new file called Global.swift) var score = 0 //sends the highest score to leaderboard func saveHighscore(gameScore: Int) { print ("You have a high score!") print("\n Attempting to authenticating with GC...") if GKLocalPlayer.localPlayer().authenticated { print("\n Success! Sending highscore of \(score) to leaderboard") //---------PUT YOUR ID HERE: // | // | // V let my_leaderboard_id = "YOUR_LEADERBOARD_ID" let scoreReporter = GKScore(leaderboardIdentifier: my_leaderboard_id) scoreReporter.value = Int64(gameScore) let scoreArray: [GKScore] = [scoreReporter] GKScore.reportScores(scoreArray, withCompletionHandler: {error -> Void in if error != nil { print("An error has occured:") print("\n \(error) \n") } }) } } // Your scene: class GameScene: SKScene, GKGameCenterControllerDelegate { // Local scope variables (for this scene): // Declare a new node, then initialize it let call_gc_node = SKLabelNode(fontNamed:"Chalkduster") let add_score_node = SKLabelNode(fontNamed: "Helvetica") override func didMoveToView(view: SKView) { // Give our GameCenter node some stuff initGCNode: do { // Set the name of the node (we will reference this later) call_gc_node.name = "callGC" // Default inits call_gc_node.text = "Send your HighScore of \(score) into Game Center" call_gc_node.fontSize = 25 call_gc_node.position = CGPoint( x:CGRectGetMidX(self.frame), y:CGRectGetMidY(self.frame)) // Self here is the instance (object) of our class, GameScene // This adds it to our view self.addChild(call_gc_node) } // Give our Add label some stuff initADDLabel: do { // Set the name of the node (we will reference this later) add_score_node.name = "addGC" // Basic inits add_score_node.text = "ADD TO SCORE!" add_score_node.fontSize = 25 add_score_node.position = call_gc_node.position // Align our label some add_score_node.runAction(SKAction.moveByX(0, y: 50, duration: 0.01)) // Add it to the view self.addChild(add_score_node) } } override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) { for touch in touches { // Get the position of our click let TPOINT = touch.locationInNode(self) // Get the name (string) of the node that was touched let node_that_was_touched: String? = nodeAtPoint(TPOINT).name // Prepare for switch statement, when we unwrap the optional, we don't want nil guard (node_that_was_touched != nil) else { print("-> before switch: found nil--not entering Switch"); return } // Find out which node we clicked based on node.name?, then do stuff: switch node_that_was_touched! { case "callGC": // We clicked the GC label: GameOver: do { print("GAME OVER!") // If we have a high-score, send it to leaderboard: overrideHighestScore(score) // Reset our score (for the next playthrough) score = 0 // Show us our stuff! showLeader() } case "addGC": // we clicked the Add label: // Update our *current score* score += 1 default: print("no matches found") } } } override func update(currentTime: CFTimeInterval) { /* Called before each frame is rendered */ call_gc_node.text = "Send your HighScore of \(score) into Game Center" } // Gamecenter func gameCenterViewControllerDidFinish(gameCenterViewController: GKGameCenterViewController) { gameCenterViewController.dismissViewControllerAnimated(true, completion: nil) } //shows leaderboard screen func showLeader() { let viewControllerVar = self.view?.window?.rootViewController let gKGCViewController = GKGameCenterViewController() gKGCViewController.gameCenterDelegate = self viewControllerVar?.presentViewController(gKGCViewController, animated: true, completion: nil) } // Your "game over" function call func overrideHighestScore(gameScore: Int) { NSUserDefaults.standardUserDefaults().integerForKey("highscore") if gameScore > NSUserDefaults.standardUserDefaults().integerForKey("highscore") { NSUserDefaults.standardUserDefaults().setInteger(gameScore, forKey: "highscore") NSUserDefaults.standardUserDefaults().synchronize() saveHighscore(gameScore) } } } 

Pay particular attention to

29: let my_leaderboard_id = "YOUR_LEADERBOARD_ID"

I added some stupid ASCII art to make sure you don't miss it. You must enter your actual leaderboard identifier from the GameCenter setup.

You also need to add the GameCenter library and connect iTunes to see your highscores in the pop-up window.

I think your initial problems were that you donโ€™t understand how SpriteKit and even iOS help works (which is completely normal because Apple makes jumps and makes things very easy). But as you can see, the following guides / guides can be difficult as your implementation will be different.

Here is some good information to start with:

Scheme of each frame in SK:

enter image description here


So, you see that SKScene is a class with all interesting things like Nodes and Actions, and where everything (important to you) happens. You can generate these scenes through the editor, but then you probably need to create a new .swift file to go with it (since each scene can have its own logic).

The editor is just a shortcut to initialize a bunch of things, and frankly, you can create complete games with a little code (but you will know very quickly what you want more)

So, in this code, where you declare a GameScene or PauseScreen (which are basically just class declarations that inherit from SKScene), you will quickly find this line talking about something that isnt:

override func didMoveToView(view: SKView) .. it calls SKView ... what is it and where did it come from?

(Read about SKView here and look at its inheritance):

https://developer.apple.com/library/ios/documentation/SpriteKit/Reference/SKView/index.html#//apple_ref/occ/cl/SKView


We find this SKView declaration in the GameViewController file (this is just a class), note that it is similar to regular iOS apps, as it inherits from the UIViewController:

 override func viewDidLoad() { super.viewDidLoad() if let scene = GameScene(fileNamed:"GameScene") { // Configure the view. let skView = self.view as! SKView skView.showsFPS = true skView.showsNodeCount = true /* Sprite Kit applies additional optimizations to improve rendering performance */ skView.ignoresSiblingOrder = true /* Set the scale mode to scale to fit the window */ scene.scaleMode = .AspectFill skView.presentScene(scene) } 

Again, this method is declared in GameViewController.swift, which is basically the following: class GameViewController: UIViewController


So how does all this relate to iOS and SpriteKit apps? Well, they are all scattered over each other:

IOS app anatomy:

anatomy

Basically, from right to left you have a Window that (correct me, if not) AppDelegate, then a ViewController, and then your View, which has all the cool stuff (Storyboards sit inside the View, just like SKScenes sit inside the view. ... Labels, nodes or buttons, all sit inside their respective classes ((view)))

All of this is a great inheritance sandwich.


Check out the Apple websites for more information.

https://developer.apple.com/library/safari/documentation/UserExperience/Conceptual/MobileHIG/ContentViews.html#//apple_ref/doc/uid/TP40006556-CH13-SW1

https://developer.apple.com/spritekit/

https://developer.apple.com/library/ios/documentation/SpriteKit/Reference/SpriteKitFramework_Ref/

https://developer.apple.com/library/safari/documentation/UserExperience/Conceptual/MobileHIG/Anatomy.html

Basically, this is all a class inherited from a class inherited from a class, etc., and so on ... It can become messy. You can also see these inheritances in Xcode on CMD + by clicking on them, which will lead you to the source file.

Goodluck with your exploration and adventure at SpriteKit :)

+10
source share

A detailed answer, which still works in my game with Swift 2.2 and partially Xcode 7.1, I wrote a while ago. Detailed answer, but just skip below. To answer your question, showLeaderboard () will be called whenever you want it to be called, just put a specific function in the desired SKScene class.

iTunes Connect:

1) Log in to your iTunes Connect account . Go to My Applications and select the application in which you want to see leaders.

2) Go to the Features section and then to Game Center . Click the plus sign to create a leaderboard. If you want to create a set of leaders (grouped leaderboards, then go right and click "Advanced".

3) After clicking the plus sign, follow the instructions for what type of leaderboard you want. Make a single leaderboard first if you're not sure. The Leaderboard identifier that you assign to it will be used in your code as a string when accessing it, so make sure you write something nice.

Now in xCode:

1) Turn on the GameKit.framework library by selecting the โ€œ+โ€ sign.

2) Add the line "GameKit" to your info.plist

3a) Add the following import code on top of the GameViewController.swift file.

 import GameKit 

3b) Add the following function inside the class to the same fast file.

  func authenticateLocalPlayer() { let localPlayer = GKLocalPlayer.localPlayer() localPlayer.authenticateHandler = {(viewController, error) -> Void in if (viewController != nil) { self.presentViewController(viewController!, animated: true, completion: nil) } else { print((GKLocalPlayer.localPlayer().authenticated)) } } } 

4) Call the authenticateLocalPlayer function from the viewDidLoad () function.

5a) Now go to the GameScene.swift file (or where it will be executed). Also add from top to top.

 import GameKit 

5b) Add the following code inside the class function.

 //shows leaderboard screen func showLeader() { let viewControllerVar = self.view?.window?.rootViewController let gKGCViewController = GKGameCenterViewController() gKGCViewController.gameCenterDelegate = self viewControllerVar?.presentViewController(gKGCViewController, animated: true, completion: nil) } func gameCenterViewControllerDidFinish(gameCenterViewController: GKGameCenterViewController) { gameCenterViewController.dismissViewControllerAnimated(true, completion: nil) } 

In my game, I have a button for displaying leaders, so wherever it is, just call the showLeader function to display the leaderboards.

6) You should have a function that sends the score to the leaderboards. Outside of the whole class declaration, I have the following function, which takes the user's game rating as a parameter and sends it to the leaderboard. Where it says โ€œYOUR_LEADERBOARD_IDโ€, thatโ€™s where your Leaderboard ID I mentioned earlier comes in. As pictured here .

 //sends the highest score to leaderboard func saveHighscore(gameScore: Int) { print("Player has been authenticated.") if GKLocalPlayer.localPlayer().authenticated { let scoreReporter = GKScore(leaderboardIdentifier: "YOUR_LEADERBOARD_ID") scoreReporter.value = Int64(gameScore) let scoreArray: [GKScore] = [scoreReporter] GKScore.reportScores(scoreArray, withCompletionHandler: {error -> Void in if error != nil { print("An error has occured: \(error)") } }) } } 

7) This is the function that I call in the game. He decides whether the score exceeds a higher score, and if so, he will send it to the leaderboards. This code is completely up to you, but make sure it calls the saveHighscore function, which sends data to the leaderboard.

 func overrideHighestScore(gameScore: Int) { NSUserDefaults.standardUserDefaults().integerForKey("highscore") if gameScore > NSUserDefaults.standardUserDefaults().integerForKey("highscore") { NSUserDefaults.standardUserDefaults().setInteger(gameScore, forKey: "highscore") NSUserDefaults.standardUserDefaults().synchronize() saveHighscore(gameScore) } } 

Note that the "saveHighscore" function is called at the end.

8) Finally, in the game over the function (or what you called it), call the "overrideHighestScore" function so that it can check if there are enough points to save.

After that, go wait a few minutes until the leaderboards are connected to your application (or they do not load). He is working on my game. I hope I have not forgotten a single step. Screenshot of my game leaders that were used to create this tutorial.

+4
source share

All Articles