Этот ответ частично продолжается там, где мы остановились в комментариях, поскольку вы не разместили весь свой код, я не могу точно сказать, где было ваше зависание, но это то, что я собрал вместе с отдельным руководством (это немного другая версия кода, который вы публикуете):
Автор большей части кода:
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 (или любую другую сцену, которую вы хотите использовать 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, чтобы увидеть свои рекорды во всплывающем окне.
Я думаю, ваши первоначальные проблемы были из-за того, что вы не понимали некоторые аспекты работы SpriteKit и даже представлений iOS (что совершенно нормально, потому что Apple позволяет очень легко прыгать и создавать вещи). Но, как вы видите, следовать руководствам/учебникам может быть сложно, поскольку ваша реализация будет различаться.
Вот хорошая информация для начала:
Схема того, что происходит каждый кадр в SK:
Итак, вы видите, SKScene — это класс со всеми забавными вещами, такими как узлы и действия, и именно здесь происходит все (важное для вас). Вы можете сгенерировать эти сцены через редактор, но тогда вам, вероятно, потребуется создать для них новый файл .swift (поскольку каждая сцена может иметь свою собственную логику).
Редактор — это просто «ярлык» для инициализации кучи вещей, и, честно говоря, вы можете создавать полноценные игры с небольшим количеством кода (но вы очень быстро обнаружите, что вам нужно больше)
Итак, в этом коде, где вы объявляете GameScene или PauseScreen (которые в основном являются просто объявлениями классов, унаследованными от SKScene), вы быстро обнаруживаете эту строку, говорящую о чем-то, что НЕ ЯВЛЯЕТСЯ сценой:
override func didMoveToView(view: SKView)
.. он вызывает SKView... что это такое и откуда оно взялось?
(Прочитайте о SKView здесь и посмотрите на его наследование):
https://developer.apple.com/library/ios/documentation/SpriteKit/Reference/SKView/index.html#//apple_ref/occ/cl/SKView
Мы находим это объявление SKView в файле GameViewController
(который является просто классом), обратите внимание, что оно в основном такое же, как и в обычных приложениях 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/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
По сути, все является классом, унаследованным от класса, унаследованного от класса, и так далее, так далее... Это может запутать. Вы также можете увидеть эти наследования в Xcode, щелкнув по ним CMD+, что перенесет вас к исходному файлу.
Удачи в учебе и приключениях в SpriteKit :)
person
Fluidity
schedule
25.08.2016
GameViewController
(как в учебнике Джареда с одним представлением) и размещением логики в одном из многихSKScene
. Разве это не должно проходить черезGameViewController
, и это управляет всеми сценами? В конечном счете, я пытаюсь сохранить и получить данные GC, когда пользователь нажимает на метку в данной сцене (GameOverScene
). - person muZero   schedule 25.08.2016GameViewController
, вам также необходимо опубликовать его. Я думаю, что это может быть просто проблема с областью действия, но она должна работать, если вы правильно поместите функции в свои метки. - person Fluidity   schedule 25.08.2016GameViewController
, но я хотел знать, как я могу справиться с этим черезSKScene
, напримерGameOverScene
. Вместо его UIButtons я использую метку для вызова функций. Любые предложения были бы замечательными! - person muZero   schedule 25.08.2016GameOverScene
. Я уже передал счет пользователя этой сцене, чтобы отобразить его в метке, но счет также должен быть сохранен в таблице лидеров (GameCenter) при входе в эту сцену, и у пользователя должна быть возможность просмотреть его в этой сцене. Спасибо еще раз! - person muZero   schedule 25.08.2016