WWDC Quick Look 💓 By SwiftGGTeam
Tap into Game Center: Leaderboards, Achievements, and Multiplayer

Tap into Game Center: Leaderboards, Achievements, and Multiplayer

观看原视频

Highlight

Game Center 在 2020 年加入 recurring leaderboards、新的排行榜和成就页面、成就进度上报流程、多人匹配 UI 选项,以及 personalizedCommunicationRestricted 限制标记,让游戏能把周期竞赛、成就收集和实时或回合制匹配接入 GameKit。

核心内容

很多游戏一开始只需要一个最高分榜。等玩家多了,问题就出现了:永久排行榜被早期高手占住,新玩家很难追上;每周活动要单独清榜,开发者还要处理时间窗口;成就进度散落在游戏 UI 里,玩家不知道下一步该做什么;多人邀请又要同时照顾好友、最近一起玩过的人、联系人和自动匹配。

WWDC 2020 的 Game Center 把这些常见游戏服务收进 GameKit。排行榜新增 recurring leaderboards(周期排行榜),可以按小时、天或周重复创建 occurrence(单次周期)。Game Center 的游戏内 UI 也重新设计,排行榜、成就和多人匹配都能用系统页面承载。

这个变化的价值很直接。开发者不用自己维护每周竞赛表、成就页和邀请大厅的全部 UI。游戏只需要在合适的时机提交分数、上报成就、创建匹配请求,再让 Game Center 负责展示、匹配和部分网络流程。

Session 分成三段。第一段讲排行榜,重点是 classic leaderboard 和 recurring leaderboard 的差异。第二段讲成就,重点是成就的四种显示状态、数量限制和 GKAchievement 上报。第三段讲多人游戏,重点是实时、回合制、server-hosted 三种模式,以及 iOS 14 新增的匹配 UI 配置和隐私限制标记。

详细内容

周期排行榜:把限时竞赛交给 Game Center

01:23)过去的 Game Center 排行榜都是 classic leaderboards。它们永久保留玩家分数和排名,适合记录总经验、历史最高分、关卡最短用时。2020 年新增 recurring leaderboards,用于短时间、重复发生的竞赛,例如每小时 15 分钟挑战、每周日中午一小时赛事、每天一轮新榜。

02:32)周期排行榜在 App Store Connect 中配置。开发者设置 initial start date、frequency 和 duration。每个 occurrence 有开始和结束时间,新的 occurrence 只会在当前 occurrence 结束后开始。duration 不能大于 frequency,因为 occurrence 不能重叠。

04:05)提交 classic leaderboard 分数时,使用 GKLeaderboard.submitScore。同一个调用可以把分数提交到一个或多个 leaderboard ID。

// Use the class method to submit score to one or more leaderboards at once
GKLeaderboard.submitScore(self.points, context: 0, player: GKLocalPlayer.local,
    leaderboardIDs: ["my.leaderboard.id"]) { error in
}

关键点:

  • self.points 是本次要提交的分数。
  • context: 0 是可选上下文值,Game Center 会把它和分数一起保存。
  • player: GKLocalPlayer.local 表示当前已认证的本地玩家。
  • leaderboardIDs 可以放多个排行榜 ID,适合一次结算写入多个榜。
  • completion handler 中处理提交错误。

04:30)对 recurring leaderboard,也可以直接向周期排行榜 ID 提交分数。Game Center 会把分数投递到当前 active occurrence。如果设备离线,Game Center 会延后发送;服务器按收到分数的时间验证,因此离线提交可能在 occurrence 过期后失败。

// Use the class method to submit score to one or more leaderboards at once
GKLeaderboard.submitScore(self.points, context: 0, player: GKLocalPlayer.local,
    leaderboardIDs: ["my.recurring.leaderboard.id"]) { error in
}

关键点:

  • 代码形态和 classic leaderboard 相同。
  • "my.recurring.leaderboard.id" 指向 App Store Connect 中配置的周期排行榜。
  • 提交只能落到当前 active occurrence。
  • 离线场景要在游戏结算体验中预留错误处理。

04:48)如果游戏已经加载了某个 occurrence,也可以通过 GKLeaderboard 实例提交。Session 建议用 startDateduration 判断 occurrence 是否仍然 active。

// Submitting to a specific occurrence of a recurring leaderboard
GKLeaderboard.loadLeaderboards(IDs:["my.recurring.leaderboard.id"]) { (fetchedLBs, error) in
    if let lb = fetchedLBs?.first {
       lb.submitScore(self.points, context: 0, player: GKLocalPlayer.local) { error in
       }
    }
}

关键点:

  • loadLeaderboards 先按 ID 加载排行榜。
  • fetchedLBs?.first 取到当前 occurrence 对应的 GKLeaderboard
  • lb.submitScore 把分数提交到这个 occurrence。
  • 这种写法适合自定义 UI 已经持有 occurrence 对象的场景。

排行榜 UI:系统页面和自定义页面各有入口

05:25)展示排行榜有两种方式。Apple 推荐使用 GameKit 的游戏内 UI,因为 GKGameCenterViewController 会处理列表、排行榜详情、好友和全局范围、classic leaderboard 的时间范围、recurring leaderboard 的 occurrence 切换。

// Launching in-game UI

// Display a list of leaderboards
let vc = GKGameCenterViewController(
             state: .leaderboards)
vc.gameCenterDelegate = self
present(vc, animated: true, completion: nil)


// Or directly display scores for a specific leaderboard
let vc = GKGameCenterViewController(
             leaderboardID: "YOUR_ASC_LEADERBOARD_ID",
             playerScope: .global,
             timeScope: .allTime)
vc.gameCenterDelegate = self
present(vc, animated: true, completion: nil)

关键点:

  • state: .leaderboards 打开排行榜列表。
  • gameCenterDelegate 用来接收关闭等 Game Center 页面事件。
  • 第二个初始化器可以 deep link 到某个排行榜的分数页。
  • playerScope: .global 显示全局范围,也可以按 GameKit 支持的范围切换。
  • timeScope: .allTime 用于 classic leaderboard 的时间维度。

07:14)如果游戏要自绘排行榜 UI,就要自己加载 leaderboard 和 score。recurring leaderboard 还要处理 occurrence。Game Center 只保留过期 occurrence 最多 30 天;已登录玩家通常能看当前 occurrence 和一个 previous occurrence。

// Accessing previous occurrence

// Load current occurrence of a recurring leaderboard
GKLeaderboard.loadLeaderboards(IDs:["my.recurring.leaderboard.id"]) { (fetchedLBs, error) in
    if let current = fetchedLBs?.first {
       // Load previous occurrence using the current occurrence
       current.loadPreviousOccurrence { (prevOccurrence, error) in
           // Do something with the previous occurrence
       }
    }
}

关键点:

  • loadLeaderboards 对 recurring leaderboard 默认加载 current occurrence。
  • current.loadPreviousOccurrence 通过当前 occurrence 取上一个可访问 occurrence。
  • prevOccurrence 可用于展示上一轮成绩或赛后回顾。
  • 游戏自绘 UI 时,需要考虑 occurrence 被清理或玩家未参与上一轮的情况。

成就:用进度驱动玩家回到游戏

10:20)成就是玩家达成目标后的 collectible item。Game Center 的成就有四种呈现状态:locked、in-progress、completed、hidden。hidden 适合不想提前剧透剧情或目标的成就。iOS、macOS 和 tvOS 的 Game Center Dashboard 都会展示成就,因此成就文案、图片和进度要能独立说明玩家目标。

13:01)数量限制也很明确:一个游戏最多 100 个 achievements,每个最多 100 points,合计最多 1000 points。初版上线时不要把预算用完,后续更新还可能需要新成就。

14:34)集成成就分三步:先认证本地玩家;再用 GKAchievement 上报进度;最后用 Game Center Dashboard 或成就页面展示。只要 GameKit 里某个成就进度仍是 0,它就会以 locked 状态出现。

if let achievement = GKAchievement(identifier: identifier) {
    achievement.percentComplete = percentComplete
    GKAchievement.report([achievement]) { error in
        if let error = error {
            print("Error in reporting achievements: \(error)")
        }
    }
}

关键点:

  • identifier 必须对应 App Store Connect 中配置的 achievement ID。
  • percentComplete 表示完成进度,达到 100 后成就完成。
  • GKAchievement.report 可以一次上报一个数组。
  • completion handler 中要处理上报失败,尤其是网络或认证问题。

16:05)展示成就时,优先使用 GKGameCenterViewController(state: .achievements)。如果自绘成就页,再使用 GKAchievementDescription 读取本地化标题、未完成描述、完成描述,以及完成和未完成图片。

// Showing the Game Center achievements page

let viewController = GKGameCenterViewController(state: .achievements)
viewController.gameCenterDelegate = self
present(viewController, animated: true)

关键点:

  • state: .achievements 直接打开当前游戏的成就页。
  • gameCenterDelegate 负责页面生命周期回调。
  • present 让玩家在游戏内查看进度,不需要跳出游戏。
  • 自绘 UI 时,文字本地化仍应来自 GameKit 返回的数据。

多人游戏:匹配、邀请和隐私限制

19:22)Game Center 支持三类多人游戏。Real-time 模式负责寻找玩家、建立连接和发送数据。Turn-based 模式负责回合管理和数据存储。Server-hosted 模式把玩家列表交给开发者自己的服务器。

21:23)实时多人匹配的基本流程是:认证玩家,创建 GKMatchRequest,设置玩家数量和可邀请对象,再选择自动匹配或展示 GKMatchmakerViewController。应用启动时还应采用 GKInviteEventListener,并向本地玩家注册,用来接收其他玩家发来的邀请。

关键点:

  • GKMatchRequest 描述一局游戏需要多少玩家。
  • Session 明确提到开发者在 request 中设置玩家数量,并可定义要邀请的人。
  • GKMatchmaker 的自动匹配流程会寻找其他玩家,返回的 match object 用于玩家之间发送数据。
  • 如果需要玩家自己选人,可以展示 GKMatchmakerViewController
  • GKInviteEventListener 适合在 app 启动时注册,用来接收其他玩家发来的邀请。

22:52)回合制匹配流程接近实时匹配。差异在于采用 GKTurnBasedEventListener,并使用 GKTurnBasedMatchGKTurnBasedMatchmakerViewController。回合制对局不需要等待所有玩家同时在线,创建后当前玩家可以立即开始自己的回合。

关键点:

  • GKMatchRequest 仍然描述玩家数量和匹配条件。
  • GKTurnBasedEventListener 负责接收回合制事件。
  • GKTurnBasedMatch 用于程序化匹配,GKTurnBasedMatchmakerViewController 用于展示系统匹配界面。
  • 当前玩家可以先行动,等待其他玩家后续加入或响应。

23:29)iOS 14 为 matchmaking UI 增加了配置选项。automatch only 适合想让玩家快速随机开局的游戏。nearby only 适合同房间 ARKit 游戏。Session 还提到新的限制会同时影响多人邀请自定义消息和语音聊天。

// Check if personalized communication is restricted
if GKLocalPlayer.local.personalizedCommunicationRestricted {
    // Disable UI for Voice chat
}

关键点:

  • GKLocalPlayer.local 代表当前 Game Center 玩家。
  • personalizedCommunicationRestrictedtrue 时,应用应关闭相关个性化通信入口。
  • Session 给出的示例是禁用 voice chat UI。
  • 这个检查应和多人大厅、邀请入口、语音按钮的显示逻辑放在一起。

核心启发

  • 做每周挑战榜:把关卡分数接到 recurring leaderboard,每周自动开新 occurrence。值得做,因为新玩家每周都有追排名的机会。可以从 App Store Connect 配置 weekly frequency 和 duration 开始,再用 GKLeaderboard.submitScore 在关卡结算页提交。

  • 做限时活动入口:在主菜单显示下一轮挑战倒计时,并在活动期间展示当前 occurrence 的排行榜。值得做,因为 Session 明确说明 occurrence 有开始时间、duration 和下一次开始信息。可以先加载 recurring leaderboard,再根据 occurrence 时间决定按钮状态。

  • 做分阶段成就路线:为新手、熟练玩家和核心玩家分别设计成就。值得做,因为 Game Center Dashboard 会把 locked、in-progress 和 completed 状态直接展示给玩家。可以用 GKAchievement(identifier:)percentComplete 在每次任务进度变化时上报。

  • 做一键成就页:在暂停菜单或关卡结算页放一个“成就”按钮,直接打开 Game Center achievements 页面。值得做,因为系统页已经处理进度、图片和本地化展示。可以用 GKGameCenterViewController(state: .achievements) 起步。

  • 做快速多人开局:为休闲模式提供 automatch only,为本地同屏或 AR 玩法提供 nearby only。值得做,因为匹配 UI 可以减少玩家选人步骤。可以用 GKMatchRequest 定义玩家数量,再根据模式选择自动匹配或 GKMatchmakerViewController

关联 Session

评论

GitHub Issues · utterances