在特定场景中构建 SpriteKit/GameKit 排行榜 [英] Building a SpriteKit/GameKit leaderboard within a specific scene

查看:20
本文介绍了在特定场景中构建 SpriteKit/GameKit 排行榜的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我对 Swift 很陌生,在我的游戏中实现排行榜时遇到了一些麻烦.我刚刚看了一个教程:'Game Center 排行榜!(Xcode 中的 Swift 2)',其中 GameCenter 信息全部通过应用程序的一个视图.在我的游戏中,我希望用户能够玩游戏,然后只有当他们在特定的 SKScene 上时,他们才能访问 GameCenter.

因此,例如,在 GameOverScene 上,他们将通过用户身份验证,并且还能够上传他们的高分.我想我还遗漏了 GameViewController(所有教程逻辑所在的位置)与我制作的众多场景之一之间的一些差异.

这是我尝试在 GameOverScene 上使用 GKGameCenterControllerDelegate 并创建各种功能以到达 GameCenter 的代码.当用户点击视图中的某个标签时进行调用:(这显然不起作用,因为我试图在这样的行上访问场景: self.presentViewController(view!, animated:true, completion:无)

<小时>

类 GameOverScene: SKScene, GKGameCenterControllerDelegate {初始化(大小:CGSize,theScore:Int){分数 = theScore超级初始化(大小:大小)}...覆盖函数 didMoveToView(视图:SKView){授权播放器()leaderboardLabel.text = "点击排行榜"leaderboardLabel.fontSize = 12leaderboardLabel.fontColor = SKColor.redColor()leaderboardLabel.position = CGPoint(x: size.width*0.85, y: size.height*0.1)addChild(排行榜标签)...override func touchesBegan(touches: Set, withEvent event: UIEvent?) {for touch : AnyObject in touches {让 location = touch.locationInNode(self)如果(CGRectContainsPoint(leaderBoardLabel.frame,位置)){saveHighScore(分数)showLeaderBoard()}}}功能 authPlayer(){//创建一个播放让 localPlayer = GKLocalPlayer.localPlayer()//查看是否登录localPlayer.authenticateHandler = {//一个视图控制器和一个错误处理程序(查看,错误)在//如果有视图可以使用如果视图 != nil {self.presentViewController(view!, animated:true, completion: nil)//我们不想要完成处理程序}别的{打印(GKLocalPlayer.localPlayer().authenticated)}}}//当你的高分应该被保存时调用这个func saveHighScore(数字:Int){if(GKLocalPlayer.localPlayer().authenticated){让 scoreReporter = GKScore(leaderboardIdentifier: "scoreBoard")scoreReporter.value = Int64(数字)让 scoreArray: [GKScore] = [scoreReporter]GKScore.reportScores(scoreArray, withCompletionHandler: nil)}}func showLeaderBoard(){让 viewController = self.view.window?.rootViewController让 gcvc = GKGameCenterViewController()gcvc.gameCenterDelegate = 自我viewController?.presentViewController(gcvc,动画:真,完成:无)}func gameCenterViewControllerDidFinish(gameCenterViewController: GKGameCenterViewController) {gameCenterViewController.dismissViewControllerAnimated(真,完成:无)}

<小时>

任何关于我如何解决这个问题的建议都会很棒,我认为我可能会混淆整个场景/视图控制器并导致问题.谢谢!

解决方案

这个答案部分延续了我们在评论中留下的地方,因为你没有发布你的整个代码我不能确切地告诉你挂断的地方,但这是我与单独的指南一起整理的(它与您发布的代码版本略有不同):

大部分代码的作者:

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

GameViewController.swift:

导入 UIKit导入 SpriteKit导入 GameKit类 GameViewController: UIViewController {func authenticateLocalPlayer() {让 localPlayer = GKLocalPlayer.localPlayer()localPlayer.authenticateHandler = {(viewController, error) ->无效if (viewController != nil) {self.presentViewController(viewController!,动画:真,完成:无)}别的 {打印((GKLocalPlayer.localPlayer().authenticated))}}}覆盖 func viewDidLoad() {super.viewDidLoad()/////验证//////验证本地播放器()//... 其余默认代码}//... 其余默认代码}

<小时>

GameScene.swift(或任何你想使用 GC 的场景):

<小时>

import SpriteKit导入 GameKit导入 UIKit//全局作用域(我一般将它们放在一个名为 Global.swift 的新文件中)变量分数 = 0//将最高分发送到排行榜func saveHighscore(gameScore: Int) {print ("你的分数很高!")print("
 正在尝试使用 GC 进行身份验证...")如果 GKLocalPlayer.localPlayer().authenticated {print("
 成功!发送 (score) 的高分到排行榜")//---------把你的ID放在这里://|//|//V让 my_leaderboard_id = "YOUR_LEADERBOARD_ID"让 scoreReporter = GKScore(leaderboardIdentifier: my_leaderboard_id)scoreReporter.value = Int64(gameScore)让 scoreArray: [GKScore] = [scoreReporter]GKScore.reportScores(scoreArray, withCompletionHandler: {error -> Void in如果错误!= nil {print("发生错误:")print("
 (错误)
")}})}}//你的场景:类游戏场景:SKScene,GKGameCenterControllerDelegate {//局部范围变量(对于这个场景)://声明一个新节点,然后初始化它让 call_gc_node = SKLabelNode(fontNamed:"Chalkduster")让 add_score_node = SKLabelNode(fontNamed: "Helvetica")覆盖函数 didMoveToView(视图:SKView){//给我们的 GameCenter 节点一些东西初始化GCNode:做{//设置节点的名称(我们稍后会引用它)call_gc_node.name = "callGC"//默认初始化call_gc_node.text = "将你的 (score) 的 HighScore 发送到 Game Center"call_gc_node.fontSize = 25call_gc_node.position = CGPoint(x:CGRectGetMidX(self.frame),y:CGRectGetMidY(self.frame))//这里的Self是我们类GameScene的实例(对象)//这会将它添加到我们的视图中self.addChild(call_gc_node)}//给我们的 Add 标签一些东西初始化ADDLabel:做{//设置节点的名称(我们稍后会引用它)add_score_node.name = "addGC"//基本初始化add_score_node.text = "加分!"add_score_node.fontSize = 25add_score_node.position = call_gc_node.position//对齐我们的标签add_score_node.runAction(SKAction.moveByX(0, y: 50, duration: 0.01))//添加到视图中self.addChild(add_score_node)}}override func touchesBegan(touches: Set, withEvent event: UIEvent?) {触摸触摸{//获取我们点击的位置让 TPOINT = touch.locationInNode(self)//获取被触摸节点的名称(字符串)让node_that_was_touched:字符串?= nodeAtPoint(TPOINT).name//准备 switch 语句,当我们解包可选时,我们不想要 nil守卫(node_that_was_touched != nil)else { print("->switch before: found nil--not into Switch");返回}//根据 node.name 找出我们点击了哪个节点,然后做一些事情:切换 node_that_was_touched!{案例callGC"://我们点击了 GC 标签:游戏结束:做{打印(游戏结束!")//如果我们有高分,将其发送到排行榜:overrideHighestScore(分数)//重置我们的分数(用于下一次播放)分数 = 0//向我们展示我们的东西!显示领导者()}案例addGC"://我们点击了添加标签://更新我们的*当前分数*分数 += 1默认值:打印(未找到匹配项")}}}覆盖函数更新(当前时间:CFTimeInterval){/* 在每帧渲染之前调用 */call_gc_node.text = "将你的 (score) 的 HighScore 发送到 Game Center"}//游戏中心func gameCenterViewControllerDidFinish(gameCenterViewController: GKGameCenterViewController) {gameCenterViewController.dismissViewControllerAnimated(真,完成:无)}//显示排行榜屏幕函数显示领导者(){让 viewControllerVar = self.view?.window?.rootViewController让 gKGCViewController = GKGameCenterViewController()gKGCViewController.gameCenterDelegate = selfviewControllerVar?.presentViewController(gKGCViewController, 动画: true, 完成: nil)}//你的游戏结束"函数调用func overrideHighestScore(gameScore: Int) {NSUserDefaults.standardUserDefaults().integerForKey("highscore")如果游戏得分 >NSUserDefaults.standardUserDefaults().integerForKey("highscore"){NSUserDefaults.standardUserDefaults().setInteger(gameScore, forKey: "highscore")NSUserDefaults.standardUserDefaults().synchronize()saveHighscore(gameScore)}}}

<小时>

特别注意

<块引用>

29: 让 my_leaderboard_id = "YOUR_LEADERBOARD_ID"

我在里面放了一些愚蠢的 ASCII 艺术作品,以确保你不会错过它.您必须输入您在 GameCenter 设置中的实际排行榜 ID.

您还必须添加 GameCenter 库,并连接 iTunes 才能在弹出窗口中实际看到您的高分.

我认为您最初的问题是不了解 SpriteKit 甚至 iOS 视图如何工作的一些后端(这完全没问题,因为 Apple 让参与进来变得非常容易).但是,如您所见,遵循指南/教程可能会很困难,因为您的实施会有所不同.

这里有一些很好的信息:

SK 中每一帧发生的情况示意图:

<小时>

所以你看,SKScene 是一个包含所有有趣的东西的类,比如节点和动作,并且是所有(对你很重要)发生的地方.您可以通过编辑器生成这些场景,但是您可能需要创建一个新的 .swift 文件来配合它(因为每个场景都可以有自己的逻辑).

编辑器只是初始化一堆东西的捷径",老实说,你可以用很少的代码制作完整的游戏(但你很快就会发现你想要更多)p>

因此,在这段代码中,您声明 GameScene 或 PauseScreen(它们基本上只是类声明,继承自 SKScene),您很快就会发现这一行在谈论 ISNT 场景:

<块引用>

覆盖函数 didMoveToView(view: SKView).. 它正在调用 SKView... 那是什么,它是从哪里来的?

(在此处了解 SKView,并查看其继承):

基本上,从右到左,你有一个窗口,它是(如果错了,请纠正我)AppDelegate,然后是 ViewController,然后是你的视图,其中包含所有很酷的东西(故事板位于视图内,就像 SKScenes 位于 View 内部一样......标签、节点或按钮,都位于它们各自的类((视图))内部)

这都是继承的大三明治.

<小时>

查看 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/图书馆/safari/documentation/UserExperience/Conceptual/MobileHIG/Anatomy.html

基本上,一切都是从类继承的类,等等,等等......它可能会变得混乱.您还可以通过 CMD+单击它们在 Xcode 中查看这些继承,这将跳转到源文件.

祝你在 SpriteKit 的学习和冒险顺利:)

I'm pretty new to Swift and I'm having some trouble implementing a leaderboard into my game. I just watched a tutorial: 'Game Center Leaderboards! (Swift 2 in Xcode)' in which the GameCenter information all went through the one view of the app. In my game, I want the user to be able to play the game and then only when they are on a specific SKScene will they have access to GameCenter.

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

Here is my code in which I attempt to use the GKGameCenterControllerDelegate on the GameOverScene and create the various functions to reach GameCenter. The call is made when the user taps a certain label in the view: (this clearly doesnt work as I'm trying to access a scene on lines like this: 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 go about this would be great, I think that I may be getting the whole scene/view controller mixed up and its leading to problems. Thanks!

解决方案

This answer partially carries off on where we left off in the comments, since you didn't post your entire code I can't tell exactly where your hangup was, but this is what I put together along with a separate guide (it's a slightly different version of the code you post):

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 whichever scene you want to use 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("
 Attempting to authenticating with GC...")

    if GKLocalPlayer.localPlayer().authenticated {
        print("
 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("
 (error) 
")
            }
        })
    }
}

// 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 special attention to

29: let my_leaderboard_id = "YOUR_LEADERBOARD_ID"

I put some silly ASCII art in there to make sure you don't miss it. You have to put in your actual leaderboard ID from the GameCenter set-up.

You have to add the GameCenter library as well, and do the iTunes connect to actually see your highscores in the pop-up window.

I think your initial problems were with not understanding some of the back-end of how SpriteKit and even iOS views work (which is totally fine, because Apple makes jumping in and making stuff very easy). But, as you see, following guides / tutorials can be difficult since your implementation will vary.

Here is some good info to start with:

Diagram of what happens each frame in SK:


So you see, the SKScene is the class with all of the fun stuff like Nodes and Actions, and is where everything (important to you) happens. You can generate these scenes through the Editor, but then you probably need to make a new .swift file to go with it (as each scene can have its own logic).

The editor is just a 'shortcut' to initializing a bunch of stuff, and honestly, you can make complete games with little code (but you very quickly find out that you want more)

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

override func didMoveToView(view: SKView) .. it's calling a SKView... what is that, 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, (which is just a class), notice that it's the same as the regular iOS apps mostly, as it inherits 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, that method is declared in GameViewController.swift, which is basically just this: class GameViewController: UIViewController


So how does all of this relate to iOS apps and SpriteKit? Well, they are all mashed on top of each other:

IOS app anatomy:

Basically, from right to left, you have the Window, which is (correct me if wrong) the AppDelegate, then the ViewController, then your View, which has all of the cool stuff in it (Storyboards sit inside of the View, just as SKScenes sit inside of the View.... Labels, Nodes, or Buttons, all sit inside of their respective classes ((the view)))

It's all a big sandwich of inheritance.


Check out the Apple websites for more info.

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, everything is an Class inherited from a class inherited from a class and so on, so on... It can get messy. You can also see these inheritances in Xcode by CMD+clicking on them, which will jump you to the source file.

Goodluck with your studies and adventures in SpriteKit :)

这篇关于在特定场景中构建 SpriteKit/GameKit 排行榜的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆