アプリがアクティブになったタイミングでGame CenterのLeaderboardからスコアを取得する

iPhone などの iOS アプリ で Game Center と連携する際に、アプリがアクティブになったタイミングで、Leaderboard に保存してあるスコア値を取得・表示するコードを紹介する。

筆者のマシン
Mac mac mini (M1, 2020)
OS Monterey バージョン 12.4
XCode バージョン 13.4
Swift バージョン 5.6.1


ここでは次の手順で説明する。

  1. はじめに
  2. アプリがアクティブになった状態を受信するための Observer をセットする
  3. Observer 処理で、Leaderboard からスコア値を取得し画面へ表示

はじめに

iPhoneアプリへ Game Center のスコアを表示する方法として、これまで下記記事を紹介してきた。

  1. Apple の Game Center スコア値を取得する
    • 画面のボタンをタップすることで、このアプリのスコアを取得し表示する方法
  2. Game CenterへログインしたタイミングでLeaderboardからスコアを取得する
    • Game Center の認証後、自動的にスコアを取得し画面に表示する方法

これらは、上記1.はプレイヤーのタップ動作が必要となり、上記2.は Game Center へのログイン時(通常、すでにログイン済みならアプリ起動時)にのみスコアを取得するものである。
そのため、ゲームアプリをバックグラウンドやインアクティブにした後に(つまりアプリを落とすことなく)、再度画面へ表示した場合には、最新のスコアを取得・表示することはできない。

そこで今回は、アプリがアクティブになったタイミングで、自動的に Game Center の Leaderboard から最新のスコアを読み込み表示する方法を紹介する。

尚、本記事で紹介するコード全体(XCodeプロジェクト)は、GitHubで公開している。

アプリがアクティブになった状態を受信するための Observer をセットする

UIApplicationDelegateプロトコルには、アプリが inactive から active になったことを通知する static なプロパティ didBecomeActiveNotification がある。
今回はこの通知を利用する。

ここでは、アプリがアクティブになったら Game Center の Leaderboard にある最新のスコアを取得するために、次のように didBecomeActiveNotification を受信する Observer として、自作の func didBecomeActive() を実行させるようにする。

// add notification observer. When the App staus becomes active, works using block
NotificationCenter.default.addObserver(
    forName: UIApplication.didBecomeActiveNotification,
    object: nil,
    queue: OperationQueue.main,
    using: { _ in
        print("App status becomes active.")
        self.didBecomeActive()
    }
)

こうすることで、バックグラウンドなどの非アクティブな状態からアクティブになった時(つまりユーザがアプリを全面に表示した時)に、func didBecomeActive()が実行される。

ここでは、アクティブになった時の通知を受信するために didBecomeActiveNotification を利用したが、UIApplicationDelegate プロトコルには、バックグラウンドになった時の通知(didEnterBackgroundNotification)やフォアグラウンドになる時の通知(willEnterForegroundNotification)が用意されているため、仕様に応じて使い分けることができる。

Observer 処理で、Leaderboard からスコア値を取得し画面へ表示

アクティブになった時の Observer である自作の func didBecomeActive() は下記である。

private func didBecomeActive() {
    print("didBecomeActive.")
    loadScore(scoreDidLoad: { local, entries in
    // If entries are loaded, set the value to worldHighScore
        if let entries = entries,
            entries.isEmpty == false {
            self.autoLoadScore2.worldScore = entries[0].score
        }
        // If local is loaded, set the value to yourHighScore
        if let local = local {
            self.autoLoadScore2.yourScore = local.score
        }
    })
}

3行目で、もう一つの自作メソッド loadScore を実行しているが、このメソッドでは、Leaderboard からスコアを取得している。
そして取得したあとに、7行目で、予め定義済みの変数に、Leaderboard から取得した全世界のトップスコアをセットしている。 また11行目で、予め定義済みの変数に、Leaderboard から取得したローカルプレイヤーのスコアをセットしている。

もう一つの自作メソッド loadScore は下記である。

private func loadScore(scoreDidLoad: @escaping (_ localPlayerEntry: GKLeaderboard.Entry?, _ entries: [GKLeaderboard.Entry]?) -> Void) {
    print("func loadScore")
    if GKLocalPlayer.local.isAuthenticated {
        print("You are logged in. Start to load scores from Game Center.")
        // Get the Leaderboard ID of this app that you have previously registered in App Store Connect
        // (only one ID is registered here).
        GKLocalPlayer.local.loadDefaultLeaderboardIdentifier(completionHandler: { (leaderboardIdentifer, error) in
            if error != nil { // Error occured
                print(error!.localizedDescription)
            } else { // Got the loaderBoarderIdentifier for this app.
                // Load scores
                if let leaderboarderId = leaderboardIdentifer {
                    print("Default leaderboardIdentifer is \(leaderboarderId).")
                    GKLeaderboard.loadLeaderboards(IDs: [leaderboarderId]) { (boards, _) in
                        boards?.first?.loadEntries(for: .global, timeScope: .allTime, range: NSRange(location: 1, length: 1), completionHandler: {(myLocalPlayerEntry, myEntries, _, _) in
                            scoreDidLoad(myLocalPlayerEntry, myEntries)
                        })
                    }
                }
            }
        })
    }
}

ログイン済みであれば Leaderboard からスコアを取得するように 下記3行目の if GKLocalPlayer.local.isAuthenticatedで認証済みかをチェックしている。

補足として、このコードでは、デフォルトの Leaderboard を使用している。また、筆者は便宜上 自作メソッドを2つにしているが、メソッドの分割は必須ではない。

これらによる画面の動きを下記の画面キャプチャで説明する。ここでは、すでに Game Center へはログイン済みである。
また赤枠部分が、今回紹介するコードで実現する箇所である。それ以外のところは、過去の記事(Apple の Game Center スコア値を取得する, Game CenterへログインしたタイミングでLeaderboardからスコアを取得する)にて紹介している

アプリ起動直後の画面

まずは起動直後の画面である。
画面の赤枠の worldScore は Leaderboard から取得した全世界のトップスコア、myScoreは Leaderboard から取得したローカルプレイヤーのスコアである。

起動後、画面が表示される際にアプリはアクティブになるので、上記の自作のメソッド func didBecomeActive() は一度実行されている。
しかし、上の画面キャプチャの場合もそうであるが、Game Center へのログイン処理完了前にこの自作メソッドが実行されるため、上記コード if GKLocalPlayer.local.isAuthenticatedの結果が false となり、Game Center からのスコア取得は行われない。従って、赤枠内のスコアはいずれもデフォルトの 0 を表示している。

補足:赤枠の上部にある青枠にはスコアが表示されているが、これは認証状態の変更を受信してスコアを取得・表示している。

アプリをインアクティブにする

ここで、アプリを終了させずに、アクティブ以外の状態にする。アクティブ以外の状態とは、画面を他のアプリに切り替えたり、起動中のアプリを選択する App スイッチャーの状態などが挙げられる。

アプリをアクティブにする

アプリをアクティブにする(つまり再びこのアプリ画面を表示する)と、上記で説明したように、通知didBecomeActiveNotificationを受信し、自動的に Leaderboard から最新のスコアを読み込み、画面キャプチャのように表示することができる。

尚、赤枠の上部の青枠部分は、Game CenterへログインしたタイミングでLeaderboardからスコアを取得するで説明したコードで紹介している。
また赤枠の下部の青枠部分は、ボタンをタップしたら Leaderboard からスコアを取得し表示するで紹介している。

以上

シェアする

  • このエントリーをはてなブックマークに追加

フォローする