gamekit

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

GameKit

GameKit

Integrate Game Center features into iOS 26+ games using GameKit and Swift 6.3. Provides player authentication, leaderboards, achievements, multiplayer matchmaking, access point, dashboard, challenges, and saved games.
使用GameKit和Swift 6.3将Game Center功能集成到iOS 26及以上版本的游戏中。提供玩家身份验证、排行榜、成就、多人游戏匹配、接入点、控制台、挑战和游戏存档功能。

Contents

目录

Authentication

身份验证

All GameKit features require the local player to authenticate first. Set the
authenticateHandler
on
GKLocalPlayer.local
early in the app lifecycle. GameKit calls the handler multiple times during initialization.
swift
import GameKit

func authenticatePlayer() {
    GKLocalPlayer.local.authenticateHandler = { viewController, error in
        if let viewController {
            // Present so the player can sign in or create an account.
            present(viewController, animated: true)
            return
        }
        if let error {
            // Player could not sign in. Disable Game Center features.
            disableGameCenter()
            return
        }

        // Player authenticated. Check restrictions before starting.
        let player = GKLocalPlayer.local

        if player.isUnderage {
            hideExplicitContent()
        }
        if player.isMultiplayerGamingRestricted {
            disableMultiplayer()
        }
        if player.isPersonalizedCommunicationRestricted {
            disableInGameChat()
        }

        configureAccessPoint()
    }
}
Guard on
GKLocalPlayer.local.isAuthenticated
before calling any GameKit API. For server-side identity verification, see references/gamekit-patterns.md.
所有GameKit功能都需要先对本地玩家进行身份验证。在应用生命周期的早期为
GKLocalPlayer.local
设置
authenticateHandler
。GameKit会在初始化过程中多次调用该处理器。
swift
import GameKit

func authenticatePlayer() {
    GKLocalPlayer.local.authenticateHandler = { viewController, error in
        if let viewController {
            // Present so the player can sign in or create an account.
            present(viewController, animated: true)
            return
        }
        if let error {
            // Player could not sign in. Disable Game Center features.
            disableGameCenter()
            return
        }

        // Player authenticated. Check restrictions before starting.
        let player = GKLocalPlayer.local

        if player.isUnderage {
            hideExplicitContent()
        }
        if player.isMultiplayerGamingRestricted {
            disableMultiplayer()
        }
        if player.isPersonalizedCommunicationRestricted {
            disableInGameChat()
        }

        configureAccessPoint()
    }
}
在调用任何GameKit API之前,请先检查
GKLocalPlayer.local.isAuthenticated
。如需进行服务器端身份验证,请参考references/gamekit-patterns.md

Access Point

接入点

GKAccessPoint
displays a Game Center control in a corner of the screen. When tapped, it opens the Game Center dashboard. Configure it after authentication.
swift
func configureAccessPoint() {
    GKAccessPoint.shared.location = .topLeading
    GKAccessPoint.shared.showHighlights = true
    GKAccessPoint.shared.isActive = true
}
Hide the access point during gameplay and show it on menu screens:
swift
GKAccessPoint.shared.isActive = false  // Hide during active gameplay
GKAccessPoint.shared.isActive = true   // Show on pause or menu
Open the dashboard to a specific state programmatically:
swift
// Open directly to a leaderboard
GKAccessPoint.shared.trigger(
    leaderboardID: "com.mygame.highscores",
    playerScope: .global,
    timeScope: .allTime
) { }

// Open directly to achievements
GKAccessPoint.shared.trigger(state: .achievements) { }
GKAccessPoint
会在屏幕角落显示一个Game Center控件,点击后将打开Game Center控制台。请在身份验证完成后对其进行配置。
swift
func configureAccessPoint() {
    GKAccessPoint.shared.location = .topLeading
    GKAccessPoint.shared.showHighlights = true
    GKAccessPoint.shared.isActive = true
}
在游戏进行时隐藏接入点,在菜单界面显示:
swift
GKAccessPoint.shared.isActive = false  // 游戏进行中隐藏
GKAccessPoint.shared.isActive = true   // 暂停或菜单界面显示
通过编程方式直接打开控制台到指定状态:
swift
// 直接打开到排行榜
GKAccessPoint.shared.trigger(
    leaderboardID: "com.mygame.highscores",
    playerScope: .global,
    timeScope: .allTime
) { }

// 直接打开到成就页面
GKAccessPoint.shared.trigger(state: .achievements) { }

Dashboard

控制台

Present the Game Center dashboard using
GKGameCenterViewController
. The presenting object must conform to
GKGameCenterControllerDelegate
.
swift
final class GameViewController: UIViewController, GKGameCenterControllerDelegate {

    func showDashboard() {
        let vc = GKGameCenterViewController(state: .dashboard)
        vc.gameCenterDelegate = self
        present(vc, animated: true)
    }

    func showLeaderboard(_ leaderboardID: String) {
        let vc = GKGameCenterViewController(
            leaderboardID: leaderboardID,
            playerScope: .global,
            timeScope: .allTime
        )
        vc.gameCenterDelegate = self
        present(vc, animated: true)
    }

    func gameCenterViewControllerDidFinish(
        _ gameCenterViewController: GKGameCenterViewController
    ) {
        gameCenterViewController.dismiss(animated: true)
    }
}
Dashboard states:
.dashboard
,
.leaderboards
,
.achievements
,
.localPlayerProfile
.
使用
GKGameCenterViewController
展示Game Center控制台。展示对象必须遵循
GKGameCenterControllerDelegate
协议。
swift
final class GameViewController: UIViewController, GKGameCenterControllerDelegate {

    func showDashboard() {
        let vc = GKGameCenterViewController(state: .dashboard)
        vc.gameCenterDelegate = self
        present(vc, animated: true)
    }

    func showLeaderboard(_ leaderboardID: String) {
        let vc = GKGameCenterViewController(
            leaderboardID: leaderboardID,
            playerScope: .global,
            timeScope: .allTime
        )
        vc.gameCenterDelegate = self
        present(vc, animated: true)
    }

    func gameCenterViewControllerDidFinish(
        _ gameCenterViewController: GKGameCenterViewController
    ) {
        gameCenterViewController.dismiss(animated: true)
    }
}
控制台状态包括:
.dashboard
.leaderboards
.achievements
.localPlayerProfile

Leaderboards

排行榜

Configure leaderboards in App Store Connect before submitting scores. Supports classic (persistent) and recurring (time-limited, auto-resetting) types.
在提交分数前,请先在App Store Connect中配置排行榜。支持经典(持久型)和周期性(限时自动重置)两种类型。

Submitting Scores

提交分数

Submit to one or more leaderboards using the class method:
swift
func submitScore(_ score: Int, leaderboardIDs: [String]) async throws {
    try await GKLeaderboard.submitScore(
        score,
        context: 0,
        player: GKLocalPlayer.local,
        leaderboardIDs: leaderboardIDs
    )
}
使用类方法向一个或多个排行榜提交分数:
swift
func submitScore(_ score: Int, leaderboardIDs: [String]) async throws {
    try await GKLeaderboard.submitScore(
        score,
        context: 0,
        player: GKLocalPlayer.local,
        leaderboardIDs: leaderboardIDs
    )
}

Loading Entries

加载条目

swift
func loadTopScores(
    leaderboardID: String,
    count: Int = 10
) async throws -> (GKLeaderboard.Entry?, [GKLeaderboard.Entry]) {
    let leaderboards = try await GKLeaderboard.loadLeaderboards(
        IDs: [leaderboardID]
    )
    guard let leaderboard = leaderboards.first else { return (nil, []) }

    let (localEntry, entries, _) = try await leaderboard.loadEntries(
        for: .global,
        timeScope: .allTime,
        range: 1...count
    )
    return (localEntry, entries)
}
GKLeaderboard.Entry
provides
player
,
rank
,
score
,
formattedScore
,
context
, and
date
. For recurring leaderboard timing, leaderboard images, and leaderboard sets, see references/gamekit-patterns.md.
swift
func loadTopScores(
    leaderboardID: String,
    count: Int = 10
) async throws -> (GKLeaderboard.Entry?, [GKLeaderboard.Entry]) {
    let leaderboards = try await GKLeaderboard.loadLeaderboards(
        IDs: [leaderboardID]
    )
    guard let leaderboard = leaderboards.first else { return (nil, []) }

    let (localEntry, entries, _) = try await leaderboard.loadEntries(
        for: .global,
        timeScope: .allTime,
        range: 1...count
    )
    return (localEntry, entries)
}
GKLeaderboard.Entry
包含
player
rank
score
formattedScore
context
date
属性。关于周期性排行榜计时、排行榜图片和排行榜组的内容,请参考references/gamekit-patterns.md

Achievements

成就

Configure achievements in App Store Connect. Each achievement has a unique identifier, point value, and localized title/description.
在App Store Connect中配置成就。每个成就都有唯一的标识符、积分值以及本地化的标题和描述。

Reporting Progress

上报进度

Set
percentComplete
from 0.0 to 100.0. GameKit only accepts increases; setting a lower value than previously reported has no effect.
swift
func reportAchievement(identifier: String, percentComplete: Double) async throws {
    let achievement = GKAchievement(identifier: identifier)
    achievement.percentComplete = percentComplete
    achievement.showsCompletionBanner = true
    try await GKAchievement.report([achievement])
}

// Unlock an achievement completely
func unlockAchievement(_ identifier: String) async throws {
    try await reportAchievement(identifier: identifier, percentComplete: 100.0)
}
设置
percentComplete
的值从0.0到100.0。GameKit仅接受进度提升,设置低于之前上报的值将不会产生任何效果。
swift
func reportAchievement(identifier: String, percentComplete: Double) async throws {
    let achievement = GKAchievement(identifier: identifier)
    achievement.percentComplete = percentComplete
    achievement.showsCompletionBanner = true
    try await GKAchievement.report([achievement])
}

// 完全解锁成就
func unlockAchievement(_ identifier: String) async throws {
    try await reportAchievement(identifier: identifier, percentComplete: 100.0)
}

Loading Player Achievements

加载玩家成就

swift
func loadPlayerAchievements() async throws -> [GKAchievement] {
    try await GKAchievement.loadAchievements() ?? []
}
If an achievement is not returned, the player has no progress on it yet. Create a new
GKAchievement(identifier:)
to begin reporting. Use
GKAchievement.resetAchievements()
to reset all progress during testing.
swift
func loadPlayerAchievements() async throws -> [GKAchievement] {
    try await GKAchievement.loadAchievements() ?? []
}
如果某个成就未被返回,说明玩家尚未在该成就上取得任何进度。可以创建新的
GKAchievement(identifier:)
对象开始上报进度。测试时可使用
GKAchievement.resetAchievements()
重置所有进度。

Real-Time Multiplayer

实时多人游戏

Real-time multiplayer connects players in a peer-to-peer network for simultaneous gameplay. Players exchange data directly through
GKMatch
.
实时多人游戏通过对等网络连接玩家,支持同步游戏玩法。玩家通过
GKMatch
直接交换数据。

Matchmaking with GameKit UI

使用GameKit UI进行匹配

Use
GKMatchmakerViewController
for the standard matchmaking interface:
swift
func presentMatchmaker() {
    let request = GKMatchRequest()
    request.minPlayers = 2
    request.maxPlayers = 4
    request.inviteMessage = "Join my game!"

    guard let matchmakerVC = GKMatchmakerViewController(matchRequest: request) else {
        return
    }
    matchmakerVC.matchmakerDelegate = self
    present(matchmakerVC, animated: true)
}
Implement
GKMatchmakerViewControllerDelegate
:
swift
extension GameViewController: GKMatchmakerViewControllerDelegate {
    func matchmakerViewController(
        _ viewController: GKMatchmakerViewController,
        didFind match: GKMatch
    ) {
        viewController.dismiss(animated: true)
        match.delegate = self
        startGame(with: match)
    }

    func matchmakerViewControllerWasCancelled(
        _ viewController: GKMatchmakerViewController
    ) {
        viewController.dismiss(animated: true)
    }

    func matchmakerViewController(
        _ viewController: GKMatchmakerViewController,
        didFailWithError error: Error
    ) {
        viewController.dismiss(animated: true)
    }
}
使用
GKMatchmakerViewController
实现标准匹配界面:
swift
func presentMatchmaker() {
    let request = GKMatchRequest()
    request.minPlayers = 2
    request.maxPlayers = 4
    request.inviteMessage = "Join my game!"

    guard let matchmakerVC = GKMatchmakerViewController(matchRequest: request) else {
        return
    }
    matchmakerVC.matchmakerDelegate = self
    present(matchmakerVC, animated: true)
}
实现
GKMatchmakerViewControllerDelegate
协议:
swift
extension GameViewController: GKMatchmakerViewControllerDelegate {
    func matchmakerViewController(
        _ viewController: GKMatchmakerViewController,
        didFind match: GKMatch
    ) {
        viewController.dismiss(animated: true)
        match.delegate = self
        startGame(with: match)
    }

    func matchmakerViewControllerWasCancelled(
        _ viewController: GKMatchmakerViewController
    ) {
        viewController.dismiss(animated: true)
    }

    func matchmakerViewController(
        _ viewController: GKMatchmakerViewController,
        didFailWithError error: Error
    ) {
        viewController.dismiss(animated: true)
    }
}

Exchanging Data

数据交换

Send and receive game state through
GKMatch
and
GKMatchDelegate
:
swift
extension GameViewController: GKMatchDelegate {
    func sendAction(_ action: GameAction, to match: GKMatch) throws {
        let data = try JSONEncoder().encode(action)
        try match.sendData(toAllPlayers: data, with: .reliable)
    }

    func match(_ match: GKMatch, didReceive data: Data, fromRemotePlayer player: GKPlayer) {
        guard let action = try? JSONDecoder().decode(GameAction.self, from: data) else {
            return
        }
        handleRemoteAction(action, from: player)
    }

    func match(_ match: GKMatch, player: GKPlayer, didChange state: GKPlayerConnectionState) {
        switch state {
        case .connected:
            checkIfReadyToStart(match)
        case .disconnected:
            handlePlayerDisconnected(player)
        default:
            break
        }
    }
}
Data modes:
.reliable
(TCP-like, ordered, guaranteed) and
.unreliable
(UDP-like, faster, no guarantee). Use
.reliable
for critical game state and
.unreliable
for frequent position updates. Register the local player as a listener (
GKLocalPlayer.local.register(self)
) to receive invitations through
GKInviteEventListener
. For programmatic matchmaking and custom match UI, see references/gamekit-patterns.md.
通过
GKMatch
GKMatchDelegate
发送和接收游戏状态:
swift
extension GameViewController: GKMatchDelegate {
    func sendAction(_ action: GameAction, to match: GKMatch) throws {
        let data = try JSONEncoder().encode(action)
        try match.sendData(toAllPlayers: data, with: .reliable)
    }

    func match(_ match: GKMatch, didReceive data: Data, fromRemotePlayer player: GKPlayer) {
        guard let action = try? JSONDecoder().decode(GameAction.self, from: data) else {
            return
        }
        handleRemoteAction(action, from: player)
    }

    func match(_ match: GKMatch, player: GKPlayer, didChange state: GKPlayerConnectionState) {
        switch state {
        case .connected:
            checkIfReadyToStart(match)
        case .disconnected:
            handlePlayerDisconnected(player)
        default:
            break
        }
    }
}
数据模式包括:
.reliable
(类TCP,有序且可靠)和
.unreliable
(类UDP,速度更快但不保证送达)。关键游戏状态使用
.reliable
,频繁的位置更新使用
.unreliable
。将本地玩家注册为监听器(
GKLocalPlayer.local.register(self)
),即可通过
GKInviteEventListener
接收邀请。关于编程式匹配和自定义匹配界面的内容,请参考references/gamekit-patterns.md

Turn-Based Multiplayer

回合制多人游戏

Turn-based games store match state on Game Center servers. Players take turns asynchronously and do not need to be online simultaneously.
回合制游戏将匹配状态存储在Game Center服务器上。玩家异步轮流进行游戏,无需同时在线。

Starting a Match

开始匹配

swift
let request = GKMatchRequest()
request.minPlayers = 2
request.maxPlayers = 4

let matchmakerVC = GKTurnBasedMatchmakerViewController(matchRequest: request)
matchmakerVC.turnBasedMatchmakerDelegate = self
present(matchmakerVC, animated: true)
swift
let request = GKMatchRequest()
request.minPlayers = 2
request.maxPlayers = 4

let matchmakerVC = GKTurnBasedMatchmakerViewController(matchRequest: request)
matchmakerVC.turnBasedMatchmakerDelegate = self
present(matchmakerVC, animated: true)

Taking Turns

进行回合

Encode game state into
Data
, end the turn, and specify the next participants:
swift
func endTurn(match: GKTurnBasedMatch, gameState: GameState) async throws {
    let data = try JSONEncoder().encode(gameState)

    // Build next participants list: remaining active players
    let nextParticipants = match.participants.filter {
        $0.matchOutcome == .none && $0 != match.currentParticipant
    }

    try await match.endTurn(
        withNextParticipants: nextParticipants,
        turnTimeout: GKTurnTimeoutDefault,
        match: data
    )
}
将游戏状态编码为
Data
,结束当前回合并指定下一位参与者:
swift
func endTurn(match: GKTurnBasedMatch, gameState: GameState) async throws {
    let data = try JSONEncoder().encode(gameState)

    // 构建下一位参与者列表:剩余活跃玩家
    let nextParticipants = match.participants.filter {
        $0.matchOutcome == .none && $0 != match.currentParticipant
    }

    try await match.endTurn(
        withNextParticipants: nextParticipants,
        turnTimeout: GKTurnTimeoutDefault,
        match: data
    )
}

Ending the Match

结束匹配

Set outcomes for all participants, then end the match:
swift
func endMatch(_ match: GKTurnBasedMatch, winnerIndex: Int, data: Data) async throws {
    for (index, participant) in match.participants.enumerated() {
        participant.matchOutcome = (index == winnerIndex) ? .won : .lost
    }
    try await match.endMatchInTurn(withMatch: data)
}
为所有参与者设置结果,然后结束匹配:
swift
func endMatch(_ match: GKTurnBasedMatch, winnerIndex: Int, data: Data) async throws {
    for (index, participant) in match.participants.enumerated() {
        participant.matchOutcome = (index == winnerIndex) ? .won : .lost
    }
    try await match.endMatchInTurn(withMatch: data)
}

Listening for Turn Events

监听回合事件

Register as a listener and implement
GKTurnBasedEventListener
:
swift
GKLocalPlayer.local.register(self)

extension GameViewController: GKTurnBasedEventListener {
    func player(_ player: GKPlayer, receivedTurnEventFor match: GKTurnBasedMatch,
                didBecomeActive: Bool) {
        // Load match data and update UI
        loadAndDisplayMatch(match)
    }

    func player(_ player: GKPlayer, matchEnded match: GKTurnBasedMatch) {
        showMatchResults(match)
    }
}
注册为监听器并实现
GKTurnBasedEventListener
协议:
swift
GKLocalPlayer.local.register(self)

extension GameViewController: GKTurnBasedEventListener {
    func player(_ player: GKPlayer, receivedTurnEventFor match: GKTurnBasedMatch,
                didBecomeActive: Bool) {
        // 加载匹配数据并更新UI
        loadAndDisplayMatch(match)
    }

    func player(_ player: GKPlayer, matchEnded match: GKTurnBasedMatch) {
        showMatchResults(match)
    }
}

Match Data Size

匹配数据大小

matchDataMaximumSize
is 64 KB. Store larger state externally and keep only references in match data.
matchDataMaximumSize
为64 KB。较大的状态请存储在外部,仅在匹配数据中保留引用。

Common Mistakes

常见错误

Not authenticating before using GameKit APIs

未验证身份就使用GameKit API

swift
// DON'T
func submitScore() {
    GKLeaderboard.submitScore(100, context: 0, player: GKLocalPlayer.local,
                              leaderboardIDs: ["scores"]) { _ in }
}

// DO
func submitScore() async throws {
    guard GKLocalPlayer.local.isAuthenticated else { return }
    try await GKLeaderboard.submitScore(
        100, context: 0, player: GKLocalPlayer.local, leaderboardIDs: ["scores"]
    )
}
swift
// 错误示例
func submitScore() {
    GKLeaderboard.submitScore(100, context: 0, player: GKLocalPlayer.local,
                              leaderboardIDs: ["scores"]) { _ in }
}

// 正确示例
func submitScore() async throws {
    guard GKLocalPlayer.local.isAuthenticated else { return }
    try await GKLeaderboard.submitScore(
        100, context: 0, player: GKLocalPlayer.local, leaderboardIDs: ["scores"]
    )
}

Setting authenticateHandler multiple times

多次设置authenticateHandler

swift
// DON'T: Set handler on every scene transition
override func viewDidAppear(_ animated: Bool) {
    super.viewDidAppear(animated)
    GKLocalPlayer.local.authenticateHandler = { vc, error in /* ... */ }
}

// DO: Set the handler once, early in the app lifecycle
swift
// 错误示例:每次场景切换都设置处理器
override func viewDidAppear(_ animated: Bool) {
    super.viewDidAppear(animated)
    GKLocalPlayer.local.authenticateHandler = { vc, error in /* ... */ }
}

// 正确示例:仅在应用启动时设置一次处理器

Ignoring multiplayer restrictions

忽略多人游戏限制

swift
// DON'T
func showMultiplayerMenu() { presentMatchmaker() }

// DO
func showMultiplayerMenu() {
    guard !GKLocalPlayer.local.isMultiplayerGamingRestricted else { return }
    presentMatchmaker()
}
swift
// 错误示例
func showMultiplayerMenu() { presentMatchmaker() }

// 正确示例
func showMultiplayerMenu() {
    guard !GKLocalPlayer.local.isMultiplayerGamingRestricted else { return }
    presentMatchmaker()
}

Not setting match delegate immediately

未立即设置匹配代理

swift
// DON'T: Set delegate in dismiss completion -- misses early messages
func matchmakerViewController(_ vc: GKMatchmakerViewController, didFind match: GKMatch) {
    vc.dismiss(animated: true) { match.delegate = self }
}

// DO: Set delegate before dismissing
func matchmakerViewController(_ vc: GKMatchmakerViewController, didFind match: GKMatch) {
    match.delegate = self
    vc.dismiss(animated: true)
}
swift
// 错误示例:在关闭完成后设置代理,会错过早期消息
func matchmakerViewController(_ vc: GKMatchmakerViewController, didFind match: GKMatch) {
    vc.dismiss(animated: true) { match.delegate = self }
}

// 正确示例:在关闭前设置代理
func matchmakerViewController(_ vc: GKMatchmakerViewController, didFind match: GKMatch) {
    match.delegate = self
    vc.dismiss(animated: true)
}

Not calling finishMatchmaking for programmatic matches

未为编程式匹配调用finishMatchmaking

swift
// DON'T
let match = try await GKMatchmaker.shared().findMatch(for: request)
startGame(with: match)

// DO
let match = try await GKMatchmaker.shared().findMatch(for: request)
GKMatchmaker.shared().finishMatchmaking(for: match)
startGame(with: match)
swift
// 错误示例
let match = try await GKMatchmaker.shared().findMatch(for: request)
startGame(with: match)

// 正确示例
let match = try await GKMatchmaker.shared().findMatch(for: request)
GKMatchmaker.shared().finishMatchmaking(for: match)
startGame(with: match)

Not disconnecting from match

未断开匹配连接

swift
// DON'T
func returnToMenu() { showMainMenu() }

// DO
func returnToMenu() {
    currentMatch?.disconnect()
    currentMatch?.delegate = nil
    currentMatch = nil
    showMainMenu()
}
swift
// 错误示例
func returnToMenu() { showMainMenu() }

// 正确示例
func returnToMenu() {
    currentMatch?.disconnect()
    currentMatch?.delegate = nil
    currentMatch = nil
    showMainMenu()
}

Review Checklist

审核检查清单

  • GKLocalPlayer.local.authenticateHandler
    set once at app launch
  • isAuthenticated
    checked before any GameKit API call
  • Player restrictions checked (
    isUnderage
    ,
    isMultiplayerGamingRestricted
    ,
    isPersonalizedCommunicationRestricted
    )
  • Game Center capability added in Xcode signing settings
  • Leaderboards and achievements configured in App Store Connect
  • Access point configured and toggled appropriately during gameplay
  • GKGameCenterControllerDelegate
    dismisses dashboard in
    gameCenterViewControllerDidFinish
  • Match delegate set immediately when match is found
  • finishMatchmaking(for:)
    called for programmatic matches;
    disconnect()
    and nil delegate on exit
  • Turn-based match data stays under 64 KB
  • Turn-based participants have outcomes set before
    endMatchInTurn
  • Invitation listener registered with
    GKLocalPlayer.local.register(_:)
  • Data mode chosen appropriately:
    .reliable
    for state,
    .unreliable
    for frequent updates
  • NSMicrophoneUsageDescription
    set if using voice chat
  • Error handling for all async GameKit calls
  • GKLocalPlayer.local.authenticateHandler
    仅在应用启动时设置一次
  • 在调用任何GameKit API前检查
    isAuthenticated
    状态
  • 检查玩家限制(
    isUnderage
    isMultiplayerGamingRestricted
    isPersonalizedCommunicationRestricted
  • 在Xcode签名设置中添加Game Center功能
  • 在App Store Connect中配置排行榜和成就
  • 正确配置接入点,并在游戏过程中适时切换显示状态
  • GKGameCenterControllerDelegate
    gameCenterViewControllerDidFinish
    方法中关闭控制台
  • 找到匹配后立即设置匹配代理
  • 为编程式匹配调用
    finishMatchmaking(for:)
    ;退出时调用
    disconnect()
    并将代理置为nil
  • 回合制匹配数据大小不超过64 KB
  • 回合制匹配在调用
    endMatchInTurn
    前为所有参与者设置结果
  • 通过
    GKLocalPlayer.local.register(_:)
    注册邀请监听器
  • 选择合适的数据模式:
    .reliable
    用于状态同步,
    .unreliable
    用于频繁更新
  • 如果使用语音聊天,设置
    NSMicrophoneUsageDescription
  • 为所有异步GameKit调用添加错误处理

References

参考资料