swift sprite-kit gamekit game-center-leaderboard

swift - Construyendo una tabla de clasificación SpriteKit/GameKit dentro de una escena específica



sprite-kit game-center-leaderboard (2)

Esta respuesta se lleva a cabo en parte donde lo dejamos en los comentarios, ya que no publicaste tu código completo. No puedo decir exactamente dónde estuvo tu hangup, pero esto es lo que armé junto con una guía por separado (es un poco versión diferente del código que publica):

Autor de la mayor parte del código:

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 (o cualquiera que sea la escena que desee usar 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) } } }

Presta especial atención a

29: let my_leaderboard_id = "YOUR_LEADERBOARD_ID"

Puse un poco de arte ASCII para asegurarme de que no te lo pierdas. Debes ingresar tu ID de marcador real desde la configuración de GameCenter.

También debe agregar la biblioteca de GameCenter y conectarse a iTunes para ver sus mejores puntuaciones en la ventana emergente.

Creo que tus problemas iniciales fueron no entender algunos de los aspectos de fondo de cómo funcionan las vistas de SpriteKit e incluso de iOS (lo cual está muy bien, porque Apple hace que saltar y hacer cosas sea muy fácil). Pero, como puede ver, las siguientes guías / tutoriales pueden ser difíciles ya que su implementación puede variar.

Aquí hay una buena información para comenzar:

Diagrama de lo que sucede en cada fotograma en SK:

Como puede ver, SKScene es la clase con todas las cosas divertidas como Nodos y Acciones, y es donde sucede todo (importante para usted). Puede generar estas escenas a través del Editor, pero luego probablemente necesite crear un nuevo archivo .swift para ir con él (ya que cada escena puede tener su propia lógica).

El editor es solo un ''atajo'' para inicializar un montón de cosas, y honestamente, puedes hacer juegos completos con poco código (pero rápidamente descubres que quieres más )

Así que en este código, donde declaras GameScene o PauseScreen (que básicamente son solo declaraciones de clase, que heredan de SKScene), encuentras rápidamente esta línea hablando de algo que ISNT no es una escena:

override func didMoveToView(view: SKView) ... está llamando a un SKView ... ¿qué es eso y de dónde viene?

(Lea sobre SKView aquí, y mire su herencia):

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

Encontramos esta declaración SKView en el archivo GameViewController , (que es solo una clase), notamos que es lo mismo que las aplicaciones normales de iOS en su mayoría, ya que hereda 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) }

De nuevo, ese método se declara en GameViewController.swift, que básicamente es esto: class GameViewController: UIViewController

Entonces, ¿cómo se relaciona todo esto con las aplicaciones de iOS y SpriteKit? Bueno, están todos aplastados uno encima del otro:

IOS app anatomy:

Básicamente, de derecha a izquierda, tienes la ventana, que es (corrígeme si está mal) AppDelegate, luego ViewController, luego tu View, que tiene todas las cosas geniales (Storyboards se sientan dentro de View, igual que SKScenes se sientan dentro de la Vista .... Etiquetas, Nodos o Botones, todos se sientan dentro de sus respectivas clases ((la vista)))

Es todo un gran sándwich de herencia.

Consulte los sitios web de Apple para obtener más información.

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

Básicamente, todo es una clase heredada de una clase heredada de una clase y así sucesivamente, etc. puede ... se vuelve desordenada. También puede ver estas herencias en Xcode mediante CMD + haciendo clic en ellas, lo que lo llevará al archivo fuente.

Goodluck con tus estudios y aventuras en SpriteKit :)

Soy bastante nuevo para Swift y tengo problemas para implementar una tabla de líderes en mi juego. Acabo de ver un tutorial: ''Game Center Leaderboards! (Swift 2 en Xcode) ''en el que la información de GameCenter pasó por la única vista de la aplicación. En mi juego, quiero que el usuario pueda jugar el juego y solo cuando estén en un SKScene específico tendrán acceso a GameCenter.

Por ejemplo, en GameOverScene serán autenticados por el usuario y también podrán subir su puntaje más alto. Creo que también me faltan algunas de las diferencias entre GameViewController (donde se encuentra toda la lógica de los tutoriales) y una de las muchas escenas que hice.

Aquí está mi código en el que intento usar GKGameCenterControllerDelegate en GameOverScene y creo las diversas funciones para llegar a GameCenter. La llamada se realiza cuando el usuario toca una determinada etiqueta en la vista: (Esto claramente no funciona, ya que estoy tratando de acceder a una escena en líneas como esta: 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) }

Cualquier consejo sobre cómo podría hacer esto sería genial, creo que puedo estar confundiendo todo el controlador de escena / vista y está dando lugar a problemas. ¡Gracias!


Respuesta detallada que todavía funciona en mi juego a partir de Swift 2.2 y parcialmente Xcode 7.1 que escribí hace un tiempo. Respuesta detallada, pero solo salte al final. Para responder básicamente a su pregunta, se llamará a showLeaderboard () cuando quiera que se llame, simplemente ponga la función específica en la clase SKScene correcta.

iTunes Connect:

1) Inicie sesión en su cuenta de iTunes Connect . Ve a Mis aplicaciones y selecciona la aplicación con la que deseas tablas de clasificación.

2) Ve a Funciones y luego a Game Center . Haga clic en el signo más para crear una tabla de clasificación. Si desea crear un conjunto de tablas de clasificación (tablas de clasificación agrupadas, vaya a la derecha y haga clic en "Más").

3) Después de hacer clic en el signo más, siga las instrucciones de qué tipo de tabla de clasificación desea. Al principio, haz una sola tabla de clasificación si no estás seguro. La "ID del marcador" que le asigne se usará en su código como una cadena al acceder a ella, así que asegúrese de escribir algo agradable.

Ahora en xCode:

1) Incluye la biblioteca GameKit.framework seleccionando el signo "+".

2) Agregue la cadena "GameKit" en su info.plist

3a) Agregue lo siguiente en la parte superior del archivo GameViewController.swift con el otro código de importación.

import GameKit

3b) Agregue la siguiente función dentro de la clase en el mismo archivo rápido.

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) Llame a la función "authenticateLocalPlayer" desde dentro de la función viewDidLoad ().

5a) Ahora, ve al archivo GameScene.swift (o dondequiera que se lleve a cabo la ejecución). Y también agregue lo siguiente en la parte superior.

import GameKit

5b) Agregue el siguiente código dentro de la función de clase.

//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) }

En mi juego, tengo un botón para mostrar las tablas de clasificación, así que donde sea que esté, simplemente llame a la función "showLeader" para mostrar las tablas de clasificación.

6) Debe tener una función que envíe el puntaje a las tablas de clasificación. Fuera de toda la declaración de clase, tengo la siguiente función que acepta el puntaje del juego del usuario como parámetro y lo envía a la tabla de clasificación. Donde dice "YOUR_LEADERBOARD_ID", ahí es donde entra su ID de Leaderboard que mencioné anteriormente. Como se muestra aquí .

//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) Esta es la función que tengo que se llama en un juego terminado. Decide si el puntaje es mayor que el puntaje más alto anterior, y si lo es, lo enviará a las tablas de clasificación. Este código es totalmente de usted, pero asegúrese de que llame a la función saveHighscore que envía los datos a la tabla de clasificación.

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) } }

Observe que la función "saveHighscore" se llama al final.

8) Por último, en la función de juego por encima (o como lo llames), llama a la función "overrideHighestScore" para que pueda verificar si el puntaje es lo suficientemente bueno como para guardar.

Después de hacer ambas cosas, espere unos minutos hasta que las tablas de clasificación estén conectadas a su aplicación (o que se carguen). Funciona en mi juego. Con suerte, no he olvidado ningún paso. Captura de pantalla de las tablas de clasificación de mis juegos que se usaron para hacer este tutorial.