Построение таблицы лидеров SpriteKit/GameKit в определенной сцене
Я довольно новичок в Swift, и у меня возникли проблемы с реализацией лидеров в моей игре. Я только что смотрел учебник: "Game Center Leaderboards! (Swift 2 в Xcode)', в котором информация GameCenter все прошло через один вид приложения. В моей игре, я хочу, чтобы пользователь мог играть в игры и тогда только тогда, когда они находятся на определенном SKScene
будет ли у них доступ к GameCenter.
например, на GameOverScene
будут ли они аутентифицированы пользователем, а также будут иметь возможность загружать их высокий балл. Я думаю, что мне не хватает некоторых различий между GameViewController
(где расположены все учебники логики) и одна из моих многих сцен, которые я сделал.
вот мой код, в котором я пытаюсь использовать GKGameCenterControllerDelegate
на GameOverScene
и создайте различные функции для достижения GameCenter. Вызов выполняется, когда пользователь нажимает определенную метку в представлении: (это явно не работает, поскольку я пытаюсь получить доступ к сцене в таких строках: 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)
}
любые советы о том, как я мог бы пойти об этом, было бы здорово, я думаю, что я могу получить весь контроллер сцены/просмотра перепутал и его приводит к проблемам. Спасибо!
2 ответов
этот ответ частично переносится туда, где мы остановились в комментариях, так как вы не опубликовали весь свой код, я не могу точно сказать, где было ваше зависание, но это то, что я собрал вместе с отдельным руководством (это немного другая версия кода, который вы публикуете):
автор большинства код:
https://www.reddit.com/r/swift/comments/3q5owv/how_to_add_a_leaderboard_in_spritekit_and_swift_20/
GameViewController.Свифт:
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 (или любая сцена, которую вы хотите использовать 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)
}
}
}
обратите особое внимание на
29:
let my_leaderboard_id = "YOUR_LEADERBOARD_ID"
Я положил некоторые глупые ASCII искусства там, чтобы убедиться, что вы не пропустите его. Вы необходимо ввести фактический идентификатор таблицы лидеров из настройки GameCenter.
вы должны добавить библиотеку GameCenter, а также сделать iTunes connect, чтобы на самом деле увидеть ваши рекорды во всплывающем окне.
Я думаю, что ваши первоначальные проблемы заключались в том, что вы не понимали, как работает SpriteKit и даже iOS (что совершенно нормально, потому что Apple делает прыжки и делает вещи очень легкими). Но, как вы видите, следующие руководства / учебники могут быть трудными так как ваша реализация будет отличаться.
вот хорошая информация для начала:
диаграмма того, что происходит каждый кадр в SK:
Итак, вы видите, что SKScene-это класс со всеми забавными вещами, такими как узлы и действия, и где все (важно для вас) происходит. Вы можете сгенерировать эти сцены через редактор, но тогда вам, вероятно, нужно сделать новый .swift файл, чтобы пойти с ним (как каждая сцена может иметь свою логику).
редактор-это просто "ярлык" для инициализации кучи вещей, и, честно говоря, вы можете сделать полные игры с небольшим кодом (но вы очень быстро узнаете, что вы хочу больше)
Итак, в этом коде, где вы объявляете GameScene или PauseScreen (которые в основном являются объявлениями классов, которые наследуются от SKScene), вы быстро найдете эту строку, говорящую о чем-то, что не является сценой:
override func didMoveToView(view: SKView)
.. это вызов SKView... что это и откуда взялось?(читайте о SKView здесь, и посмотрите на его наследование):
мы находим это объявление SKView в GameViewController
file, (который является просто классом), обратите внимание, что он такой же, как и обычные приложения iOS, поскольку он наследует 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)
}
опять же, этот метод объявлен в GameViewController.swift, который в основном это:
class GameViewController: UIViewController
Итак, как все это относится к приложениям iOS и SpriteKit? Ну, они все были друг на друга:
Анатомия приложения IOS:
в основном, справа налево, у вас есть окно, которое (поправьте меня, если неправильно) AppDelegate, то ViewController, затем ваш вид, в котором есть все классные вещи (раскадровки сидят внутри вида, так же, как SKScenes сидят внутри вида.... Метки, узлы или кнопки, все сидят внутри своих соответствующих классов ((вид)))
это все большой бутерброд наследования.
Проверьте веб-сайты Apple для более информация.
https://developer.apple.com/spritekit/
https://developer.apple.com/library/ios/documentation/SpriteKit/Reference/SpriteKitFramework_Ref/
в основном, все является классом, унаследованным от класса, унаследованного от класса и так далее, и так далее... Это может стать грязным. Вы также можете увидеть эти наследства в Xcode, нажав на них CMD+, что приведет вас к исходному файлу.
Goodluck с вашими исследованиями и приключениями в SpriteKit:)
подробный ответ, который все еще работает в моей игре по состоянию на Swift 2.2 и частично Xcode 7.1, я написал некоторое время назад. Подробный ответ, но сразу переходим к самому низу. Чтобы в основном ответить на ваш вопрос, showLeaderboard() будет вызываться всякий раз, когда вы хотите, чтобы он был вызван, просто поместите конкретную функцию в правильный класс SKScene.
iTunes Connect:
1) Войдите в ваш учетная запись iTunes Connect. Перейдите в "Мои приложения" и выберите приложение, которое вы хотите лидеров с.
2) перейти к функциям, а затем Game Center. Щелкните знак плюс, чтобы создать лидеров. Если вы хотите сделать набор лидеров (сгруппированных лидеров), то перейдите направо и нажмите на кнопку "Еще".
3) После нажатия на знак "плюс", следуйте инструкциям, какой именно баннер вы хотите. Сначала сделайте одну таблицу лидеров, если вы не уверены. "Leaderboard ID", который вы назначаете ему, будет использоваться в вашем коде в качестве строки при доступе к нему, поэтому обязательно напечатай что-нибудь приятное.
теперь в xCode:
1) включить GameKit.библиотека фреймворка, выбрав знак"+".
2) добавьте строку "GameKit" в свою информацию.файл plist
3a) добавьте следующее поверх GameViewController.swift файл с другим кодом импорта.
import GameKit
3b) добавьте следующую функцию внутри класса в тот же файл swift.
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) вызов функция" authenticateLocalPlayer " внутри функции viewDidLoad ().
5a) Теперь перейдите в GameScene.swift-файл (или где бы ни происходило выполнение). А также добавьте следующее сверху.
import GameKit
5b) добавьте следующий код внутри функции класса.
//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)
}
в моей игре у меня есть кнопка для отображения лидеров, поэтому, где бы это ни было, просто вызовите функцию "showLeader" для отображения лидеров.
6) Вы должна быть функция, которая отправляет счет в таблицы лидеров. Вне объявления всего класса у меня есть следующая функция, которая принимает оценку игры пользователя в качестве параметра и отправляет ее в таблицу лидеров. Где он говорит "YOUR_LEADERBOARD_ID", вот где Ваш идентификатор лидеров, о котором я упоминал ранее, входит. как здесь на картинке.
//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) это функция, которая у меня есть, которая вызывается в игре. Он решает, если оценка больше, чем предыдущий высокий балл, и если это так, он отправит его в списки лидеров. этот код полностью зависит от вас, но убедитесь, что он вызывает функцию saveHighscore, которая отправляет данные в таблицу лидеров.
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)
}
}
обратите внимание, что функция "saveHighscore" вызывается в конце.
8) Наконец, в игре над функцией (или как бы Вы ее ни называли), вызовите функцию "overrideHighestScore", чтобы она могла проверить, достаточно ли хороша оценка для сохранения.
после выполняя оба, подождите несколько минут, пока таблицы лидеров не будут подключены к вашему приложению (или что они загружаются). Это работает на моей игре. надеюсь, я не забыл ни одного шага. скриншот моей игры лидеров которые были использованы для создания этого урока.