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):
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/spritekit/
https://developer.apple.com/library/ios/documentation/SpriteKit/Reference/SpriteKitFramework_Ref/
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.