GameKit でのランキング表示(SwiftUI)
はじめに
GameKit と SwiftUI を使ってランキングを表示する方法についてです。
こういうやつ。
App Store Connect の設定
まずは App Store Connect でいろいろ設定する必要があります。
左のメニューの GameCenter > Leaderboard を追加で Leaderboard を追加します。
Leaderboard タイプで適したやつを選択(今回は標準 Leaderboard にしました)。
スコアフォーマットをそれぞれ設定し作成をクリック。
ローカリゼーションは 1 つ以上必要なのでローカリゼーションを追加から追加します。
これで App Store Connect での設定は完了です。
GameCenter 対応
続いてアプリ側の設定です。
TARGETS > Signing & Capabilities > + Capability から GameCenter を追加。
下記のように実装して認証をおこないます。
import SwiftUI
import GameKit
struct ContentView: View {
@State var isAuthenticated = false
var body: some View {
VStack {
Text("Hoge")
}
.onAppear {
let localPlayer = GKLocalPlayer.local
localPlayer.authenticateHandler = { viewController, error in
if let error = error {
print(error)
}
if let viewController = viewController {
let windowScene = UIApplication.shared
.connectedScenes
.compactMap { $0 as? UIWindowScene }
.filter { $0.activationState == .foregroundActive }
.first
windowScene?.windows.first?.rootViewController?.present(viewController, animated: true)
}
self.isAuthenticated = localPlayer.isAuthenticated
}
}
}
}
rootViewController 取得の処理はマルチウィンドウのときなどによくない気もしますが大抵の場合は問題ないのでよしとしましょう。サインインしていない場合は下記のような画面が表示されます。
スコアの送信
下記の処理でスコアの送信ができます。
var score: Double
GKLeaderboard.submitScore(Int(score * 100),
context: 0,
player: GKLocalPlayer.local,
leaderboardIDs: ["Piyo"]) { error in
if let error = error {
print(error)
}
}
スコアは Int なので変換して送信します。今回は Leaderboard の設定を小数点以下 2 桁までにしているので ✕ 100 をして送信しています(これで 100 を送信すると Leaderboard では 1.00 と表示されます)。
スコアの取得
下記のようにすれば自分の順位とスコアが取得できます。
Task {
do {
let boards = try await GKLeaderboard.loadLeaderboards(IDs: ["Piyo"])
let entries = try? await boards.first?.loadEntries(for: [GKLocalPlayer.local], timeScope: .allTime)
let rank = entries?.0?.rank
let score = entries?.0?.score
print(rank)
print(score)
} catch let error {
print(error)
}
}
ランキングの表示
ランキングの表示は GKGameCenterViewController を使うので下記のように UIViewControllerRepresentable で View を作ります。
import SwiftUI
import GameKit
struct GameCenterView: UIViewControllerRepresentable {
func makeUIViewController(context: Context) -> GKGameCenterViewController {
let viewController = GKGameCenterViewController(state: .leaderboards)
viewController.gameCenterDelegate = context.coordinator
return viewController
}
func updateUIViewController(_ uiViewController: GKGameCenterViewController, context: Context) {
}
class Coordinator: NSObject, GKGameCenterControllerDelegate {
func gameCenterViewControllerDidFinish(_ gameCenterViewController: GKGameCenterViewController) {
gameCenterViewController.dismiss(animated: true, completion: nil)
}
}
func makeCoordinator() -> Coordinator {
return Coordinator()
}
}
表示したい View で下記のように実装すればランキングを表示できます。
@State var isPresented = false
Button("ranking") {
if isAuthenticated {
isPresented = true
} else {
print("サインインしてない")
}
}.fullScreenCover(isPresented: $isPresented) {
GameCenterView()
}
全部実装したらこんな感じです。
import SwiftUI
import GameKit
struct ContentView: View {
@State private var isAuthenticated = false
@State private var rank = 0
@State private var score = 0
@State private var isPresented = false
var body: some View {
VStack {
if rank > 0 {
Text("順位:\(rank)位")
}
if score > 0 {
Text("スコア:\(String(format: "%.2f", Double(score)/100))")
}
Button("send") {
send(score: 60.00)
}
Button("fetch") {
fetch()
}
Button("ranking") {
if isAuthenticated {
isPresented = true
} else {
print("サインインしてない")
}
}.fullScreenCover(isPresented: $isPresented) {
GameCenterView()
}
}
.onAppear {
let localPlayer = GKLocalPlayer.local
localPlayer.authenticateHandler = { viewController, error in
if let error = error {
print(error)
}
if let viewController = viewController {
let windowScene = UIApplication.shared
.connectedScenes
.compactMap { $0 as? UIWindowScene }
.filter { $0.activationState == .foregroundActive }
.first
windowScene?.windows.first?.rootViewController?.present(viewController, animated: true)
}
self.isAuthenticated = localPlayer.isAuthenticated
}
}
}
private func fetch() {
guard isAuthenticated else {
return
}
Task {
do {
let boards = try await GKLeaderboard.loadLeaderboards(IDs: ["Piyo"])
let entries = try? await boards.first?.loadEntries(for: [GKLocalPlayer.local], timeScope: .allTime)
if let rank = entries?.0?.rank {
self.rank = rank
}
if let score = entries?.0?.score {
self.score = score
}
} catch let error {
print(error)
}
}
}
private func send(score: Double) {
guard isAuthenticated else {
return
}
GKLeaderboard.submitScore(Int(score * 100),
context: 0,
player: GKLocalPlayer.local,
leaderboardIDs: ["Piyo"]) { error in
if let error = error {
print(error)
}
}
}
}
テスト後の処理
テストの際にいろいろデータを登録してありえない高得点のスコアを送信してしまうこともあるかと思いますが App Store Connect でデータの削除ができます。
ただこれはすべてのデータの削除になるのでリリース後に変なデータを入れるとそれだけ削除とかはできないと思います。。。
おわりに
GameKit に関しては記事も少なくちょこちょこ API も変わったりするのでちょっとだけめんどくさいですがこれでランキング表示ができるようになりました!
17 言語にローカライズして世界中の人と競える 100 マス計算アプリを作ったのでよかったらアプリダンロードしてください!!
Discussion