Design for Game Center
Graphics & Games 入门 20m

Game Center 设计指南

Design for Game Center

2020年6月26日

在 Apple 官方观看视频

一句话判断

Game Center 在 iOS 14 中迎来了视觉和体验的全面重做,新的成就展示、排行榜 UI 和多人游戏界面让你的游戏社交功能看起来不再像 2013 年的产物。

这场 Session 讲了什么

Game Center 自 iOS 4 引入以来,外观几乎没有大改过。iOS 14 对 Game Center 做了一次全面的视觉刷新,让它与 iOS 14 的设计语言保持一致。

重点更新包括:成就页面采用全新的卡片式布局,每个成就有更大的图标和更清晰的描述;排行榜支持在 Widget 中展示玩家的排名变化;多人游戏邀请界面重新设计,支持通过 iMessage 和邀请链接来邀请好友;新增了 “Access Point” UI 组件——一个浮动的 Game Center 入口按钮,玩家点击后可以快速查看成就、排行榜和好友列表。Session 还讨论了如何在游戏中合理放置 Game Center 的 UI 元素,以不打断游戏体验为原则。

值得深挖的点

Access Point:Game Center 的浮动入口

这是 Game Center 在 iOS 14 中最重要的 UI 更新。GKAccessPoint 是一个可以放在屏幕角落的浮动按钮,它显示玩家的 Game Center 头像和昵称。玩家点击后,系统会弹出一个半屏的 Game Center 面板,展示成就进度、排行榜排名和好友状态。这个设计解决了之前 Game Center UI 需要开发者自己实现入口的问题——现在只需要一行代码就能添加一个标准的入口。

成就的重设计

iOS 14 的成就展示从之前的”列表”模式改为了”卡片”模式。每个成就有更大的展示空间,包括 100x100 的图标、标题、描述和进度条。完成后的成就会有一个金色的完成标记。你的游戏不需要做任何改动就能获得新的展示效果——系统会自动使用新 UI 渲染你的成就数据。

代码片段

添加 Game Center Access Point

场景:在游戏主界面添加 Game Center 的浮动入口。

import GameKit

class GameViewController: UIViewController {
    
    override func viewDidLoad() {
        super.viewDidLoad()
        setupGameCenter()
    }
    
    private func setupGameCenter() {
        // 认证玩家
        GKLocalPlayer.local.authenticateHandler = { [weak self] viewController, error in
            if let vc = viewController {
                // 需要显示 Game Center 登录界面
                self?.present(vc, animated: true)
            } else if GKLocalPlayer.local.isAuthenticated {
                // 已认证,配置 Access Point
                self?.configureAccessPoint()
            }
        }
    }
    
    private func configureAccessPoint() {
        // 获取 Access Point 实例
        let accessPoint = GKAccessPoint.shared
        
        // 设置位置(屏幕角落)
        accessPoint.location = .topLeading  // 或 .topTrailing, .bottomLeading, .bottomTrailing
        
        // 设置是否显示
        accessPoint.isActive = true
        
        // 设置父视图控制器(用于弹出 Game Center 面板)
        accessPoint.parentWindow = view.window
    }
    
    // 在游戏进行中隐藏 Access Point
    func startGameplay() {
        GKAccessPoint.shared.isActive = false  // 游戏中隐藏,避免遮挡
    }
    
    // 游戏结束后重新显示
    func endGameplay() {
        GKAccessPoint.shared.isActive = true
    }
}

报告成就进度

场景:玩家完成特定挑战后更新成就进度。

import GameKit

class AchievementManager {
    
    /// 报告成就进度
    func reportAchievement(identifier: String, percentComplete: Double) {
        let achievement = GKAchievement(identifier: identifier)
        achievement.percentComplete = percentComplete
        achievement.showsCompletionBanner = true  // 完成时显示通知横幅
        
        GKAchievement.report([achievement]) { error in
            if let error = error {
                print("报告成就失败: \(error)")
            } else {
                print("成就进度已更新: \(identifier) - \(percentComplete)%")
            }
        }
    }
    
    /// 加载所有成就定义
    func loadAchievements() {
        GKAchievementDescription.loadAchievementDescriptions { descriptions, error in
            guard let descriptions = descriptions else { return }
            
            for desc in descriptions {
                print("成就: \(desc.title)")
                print("  描述: \(desc.achievedDescription)")
                print("  未完成描述: \(desc.unachievedDescription)")
                // desc.image 包含成就图标
            }
        }
    }
    
    /// 加载玩家的成就进度
    func loadPlayerProgress() {
        GKAchievement.loadAchievements { achievements, error in
            guard let achievements = achievements else { return }
            
            for achievement in achievements {
                print("\(achievement.identifier): \(achievement.percentComplete)%")
                print("  最后更新: \(achievement.lastReportedDate)")
            }
        }
    }
}

提交排行榜分数

场景:游戏结束时将分数提交到排行榜。

import GameKit

class LeaderboardManager {
    
    /// 提交分数
    func submitScore(_ score: Int, leaderboardID: String) {
        GKLeaderboard.submitScore(
            score,
            context: 0,
            player: GKLocalPlayer.local,
            leaderboardIDs: [leaderboardID]
        ) { error in
            if let error = error {
                print("提交分数失败: \(error)")
            } else {
                print("分数已提交: \(score) 到排行榜 \(leaderboardID)")
            }
        }
    }
    
    /// 加载排行榜数据
    func loadLeaderboard(leaderboardID: String) {
        let request = GKLeaderboard()
        request.identifier = leaderboardID
        request.playerScope = .friendsOnly  // 只看好友排名
        request.timeScope = .allTime
        request.range = NSRange(location: 1, length: 25)  // 前 25 名
        
        request.loadScores { scores, error in
            guard let scores = scores else { return }
            
            for score in scores {
                print("排名 \(score.rank): \(score.player.alias) - \(score.value) 分")
            }
        }
    }
}

多人游戏邀请

场景:使用新的 Game Center 邀请界面邀请好友。

import GameKit

class MultiplayerManager: NSObject, GKMatchmakerViewControllerDelegate {
    
    private var match: GKMatch?
    
    /// 显示邀请界面
    func showInviteScreen(minPlayers: Int = 2, maxPlayers: Int = 4) {
        let request = GKMatchRequest()
        request.minPlayers = minPlayers
        request.maxPlayers = maxPlayers
        request.defaultNumberOfPlayers = minPlayers
        
        // 使用近场邀请(同处一室的玩家)
        request.inviteeResponseHandler = { player, response in
            print("\(player.alias) 的回应: \(response)")
        }
        
        let matchmakerVC = GKMatchmakerViewController(matchRequest: request)
        matchmakerVC?.delegate = self
        
        if let vc = matchmakerVC {
            // present(vc, animated: true)
        }
    }
    
    // GKMatchmakerViewControllerDelegate
    func matchmakerViewController(_ viewController: GKMatchmakerViewController,
                                   didFind match: GKMatch) {
        // 找到了匹配的玩家
        self.match = match
        print("匹配成功,共 \(match.players.count) 名玩家")
        viewController.dismiss(animated: true)
        startGame()
    }
    
    func matchmakerViewController(_ viewController: GKMatchmakerViewController,
                                   didFailWithError error: Error) {
        print("匹配失败: \(error)")
        viewController.dismiss(animated: true)
    }
    
    func matchmakerViewControllerWasCancelled(_ viewController: GKMatchmakerViewController) {
        print("用户取消了匹配")
        viewController.dismiss(animated: true)
    }
    
    private func startGame() {
        // 开始多人游戏
    }
}

最佳实践

已有项目:如果你的游戏已经集成了 Game Center,在 iOS 14 上添加 GKAccessPoint 作为统一的 Game Center 入口。移除你自定义的 Game Center 入口 UI,使用系统标准的 Access Point 更省力也更一致。确保在游戏进行中隐藏 Access Point 以避免遮挡。

新项目:从第一天就集成 Game Center。在游戏主界面添加 Access Point,定义合理的成就和排行榜。成就的数量建议在 20-50 个之间——太少没有收集动力,太多让玩家望而却步。排行榜建议按时间范围(每日、每周、全时间)分类。

还有什么值得关注

  • Game Center 的 Widget 可以在 iOS 14 的主屏上显示玩家的排行榜排名。
  • 通过 iMessage 邀请好友时,好友可以直接在消息中看到游戏名称和邀请描述。
  • GKPlayer.profileImage 可以获取玩家的 Game Center 头像,用于游戏内的社交展示。
WWDC 2020