继 Google 登录 API 弃用后,我们将在 2026 年移除游戏 v1 SDK。2025 年 2 月之后,您将无法在 Google Play 上发布新集成游戏 v1 SDK 的游戏。我们建议您转而使用游戏 v2 SDK。
虽然现有集成旧版游戏 v1 的游戏在未来几年仍将继续运行,但建议您从 2025 年 6 月开始迁移到 v2。
本指南适用于使用 Play Games Services v1 SDK。有关最新 SDK 版本的信息,请参阅v2 文档。
如果您的游戏使用后端服务器,建议您使用 Google 登录来验证玩家身份,并将玩家身份安全地传递给后端服务器。这还能让您的游戏安全地检索玩家身份和其他数据,而不会在通过设备时面临潜在的篡改风险。
在这种情况下,您的游戏会像往常一样提示玩家登录 Google Play Games Services。玩家成功登录后,GoogleSignInAccount
对象将包含一个特殊的单次使用代码(称为服务器 auth code),客户端会将其传递给服务器。然后在服务器上,将服务器 auth code 换取一个 OAuth 2.0 令牌,服务器可以使用该令牌调用 Google Play Games Services API。
有关在游戏中添加登录的其他指南,请参阅Android 游戏中的登录。
如需查看使用 Google 登录验证玩家身份的详细代码示例,请参阅 GitHub 上的 clientserverskeleton
示例。
离线访问需要执行以下步骤
- 在 Google Play 管理中心:为您的游戏服务器创建凭据。凭据的 OAuth 客户端类型将为“网页”。
- 在 Android 应用中:作为登录的一部分,为服务器凭据请求服务器 auth code,并将其传递给您的服务器。
- 在您的游戏服务器上:使用 Google 身份验证服务将服务器 auth code 换取 OAuth 访问令牌,然后使用此令牌调用 Play Games Services REST API。
开始之前
在将 Google 登录集成到您的游戏之前,您首先需要在 Google Play 管理中心中添加您的游戏,如设置 Google Play Games Services 中所述。
为您的游戏创建一个关联的服务器端 Web 应用
Google Play Game Services 不为 Web 游戏提供后端支持。但是,它确实为您的 Android 游戏服务器提供后端服务器支持。
如果您想在服务器端应用中使用 Google Play Games Services 的 REST API,请按照以下步骤操作
- 在 Google Play 管理中心的 关联应用 部分为您的游戏创建关联的 Web 应用。请注意,此流程不使用
launch_url
,可以留空。 - 要获取应用的凭据信息,请按照以下步骤操作
- 在 Google Play 管理中心,从您的游戏点击 游戏详情。
- 向下滚动到 API 控制台项目 部分,然后点击链接到 API 控制台项目。
- 在 Google API 控制台的 API 和服务 > 凭据 屏幕中,下载您的 Web 应用的
client_secret.json
文件,并将其保存到服务器可以访问的位置。记录凭据的客户端 ID 以供以后参考。
- 重启您的服务器端应用,使其准备好接受来自游戏客户端应用的请求。
在客户端执行登录
GoogleSignInClient
类是检索当前登录玩家帐号以及在设备上尚未登录您的应用的玩家进行登录的主要入口点。
要创建登录客户端,请按照以下步骤操作
- 通过
GoogleSignInOptions
对象创建登录客户端。在用于配置登录的GoogleSignInOptions.Builder
中,您必须指定GoogleSignInOptions.DEFAULT_GAMES_SIGN_IN
。 - 您还必须通过调用
GoogleSignInOptions.Builder.requestServerAuthCode()
方法(以服务器的客户端 ID 作为参数)来指定您的游戏需要后端服务器的 auth code。稍后您将在后端服务器上检索 auth code 以获取访问令牌,如获取服务器 auth code 中所述。 - 调用
GoogleSignIn.getClient()
方法并传入您之前配置的选项。如果调用成功,Google 登录 API 将返回一个GoogleSignInClient
实例。 - 获取
GoogleSignInClient
实例后,您应该按照 执行静默登录 中的说明,从 activity 的onResume()
方法中进行静默登录。
以下是一个示例
private static final int RC_SIGN_IN = 9001; private GoogleSignInClient mGoogleSignInClient; private void startSignInForAuthCode() { // Client ID for your backend server. String webClientId = getString(R.string.webclient_id); GoogleSignInOptions signInOption = new GoogleSignInOptions.Builder(GoogleSignInOptions.DEFAULT_GAMES_SIGN_IN) .requestServerAuthCode(webClientId) .build(); GoogleSignInClient signInClient = GoogleSignIn.getClient(this, signInOption); Intent intent = signInClient.getSignInIntent(); startActivityForResult(intent, RC_SIGN_IN); }
获取服务器 auth code
要检索您的游戏可在后端服务器上用于访问令牌的服务器 auth code,请在 Google 登录成功后返回的 GoogleSignInAccount
对象上调用 getServerAuthCode()
方法。
以下是一个示例
// Auth code to send to backend server. private String mServerAuthCode; @Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { super.onActivityResult(requestCode, resultCode, data); if (requestCode == RC_SIGN_IN) { GoogleSignInResult result = Auth.GoogleSignInApi.getSignInResultFromIntent(data); if (result.isSuccess()) { mServerAuthCode = result.getSignInAccount().getServerAuthCode(); } else { String message = result.getStatus().getStatusMessage(); if (message == null || message.isEmpty()) { message = getString(R.string.signin_other_error); } new AlertDialog.Builder(this).setMessage(message) .setNeutralButton(android.R.string.ok, null).show(); } } }
在服务器上将服务器 auth code 换取访问令牌
将服务器 auth code 发送至您的后端服务器,以换取访问令牌和刷新令牌。使用访问令牌代表玩家调用 Google Play Games Services API,并可选择存储刷新令牌,以便在访问令牌过期时获取新的访问令牌。
以下代码片段展示了如何在 Java 编程语言中实现服务器端代码,以将服务器 auth code 换取访问令牌。它使用的是 clientserverskeleton 示例应用
/**
* Exchanges the authcode for an access token credential. The credential
* is the associated with the given player.
*
* @param authCode - the non-null authcode passed from the client.
* @param player - the player object which the given authcode is
* associated with.
* @return the HTTP response code indicating the outcome of the exchange.
*/
private int exchangeAuthCode(String authCode, Player player) {
try {
// The client_secret.json file is downloaded from the Google API
// console. This is used to identify your web application. The
// contents of this file should not be shared.
//
File secretFile = new File("client_secret.json");
// If we don't have the file, we can't access any APIs, so return
// an error.
if (!secretFile.exists()) {
log("Secret file : " + secretFile
.getAbsolutePath() + " does not exist!");
return HttpServletResponse.SC_FORBIDDEN;
}
GoogleClientSecrets clientSecrets = GoogleClientSecrets.load(
JacksonFactory.getDefaultInstance(), new
FileReader(secretFile));
// Extract the application id of the game from the client id.
String applicationId = extractApplicationId(clientSecrets
.getDetails().getClientId());
GoogleTokenResponse tokenResponse =
new GoogleAuthorizationCodeTokenRequest(
HTTPTransport,
JacksonFactory.getDefaultInstance(),
"https://oauth2.googleapis.com/token",
clientSecrets.getDetails().getClientId(),
clientSecrets.getDetails().getClientSecret(),
authCode,
"")
.execute();
log("hasRefresh == " + (tokenResponse.getRefreshToken() != null));
log("Exchanging authCode: " + authCode + " for token");
Credential credential = new Credential
.Builder(BearerToken.authorizationHeaderAccessMethod())
.setJsonFactory(JacksonFactory.getDefaultInstance())
.setTransport(HTTPTransport)
.setTokenServerEncodedUrl("https://www.googleapis.com/oauth2/v4/token")
.setClientAuthentication(new HttpExecuteInterceptor() {
@Override
public void intercept(HttpRequest request)
throws IOException {
}
})
.build()
.setFromTokenResponse(tokenResponse);
player.setCredential(credential);
// Now that we have a credential, we can access the Games API.
PlayGamesAPI api = new PlayGamesAPI(player, applicationId,
HTTPTransport, JacksonFactory.getDefaultInstance());
// Call the verify method, which checks that the access token has
// access to the Games API, and that the player id used by the
// client matches the playerId associated with the accessToken.
boolean ok = api.verifyPlayer();
// Call a Games API on the server.
if (ok) {
ok = api.updatePlayerInfo();
if (ok) {
// persist the player.
savePlayer(api.getPlayer());
}
}
return ok ? HttpServletResponse.SC_OK :
HttpServletResponse.SC_INTERNAL_SERVER_ERROR;
} catch (IOException e) {
e.printStackTrace();
}
return HttpServletResponse.SC_INTERNAL_SERVER_ERROR;
}
要了解更多关于代表已登录玩家从后端服务器访问 Google API 的信息,请参阅启用服务器端访问。
处理玩家退出
要让玩家退出游戏,请在 GoogleSignInClient
上调用 signOut()
方法。有关示例代码片段,请参阅让玩家退出。
从服务器调用 REST API
有关可用 API 调用的完整说明,请参阅适用于 Google Play Games Services 的 REST API。
您可能会觉得有用的 REST API 调用示例如下
玩家
- 想获取已登录玩家的 ID 和个人资料数据吗?调用 Players.get,并将 ID 设为
'me'
。
好友
请务必查看好友指南,其中更详细地解释了好友功能。
- 想检索玩家的好友列表吗?调用 Players.list,并将
collection
设为'friends_all'
。 - 想检查您是否可以访问好友列表?调用 Players.get 获取
me
,并查看响应中的profileSettings.friendsListVisibility
字段。
成就
请务必查看成就指南,其中更详细地解释了成就。
- 想获取当前成就列表?您可以调用 AchievementDefinitions.list。
- 结合调用 Achievements.list,找出玩家解锁了哪些成就。
- 玩家获得成就了吗?使用 Achievements.unlock 解锁它!
- 玩家在部分成就上取得了进展吗?使用 Achievements.increment 报告进度(并查看玩家是否已解锁)。
- 您正在调试尚未上线的游戏吗?尝试调用 Management API 中的 Achievements.reset 或 Achievements.resetAll,将成就重置为原始状态。
排行榜
请务必查看排行榜指南,其中更详细地解释了排行榜。
- 想获取游戏中的所有排行榜列表?调用 Leaderboards.list。
- 玩家游戏结束了吗?您可以将他们的分数提交到 Scores.submit,并查看这是否是新的最高分。
- 想显示排行榜吗?从 Scores.list 获取数据并展示给用户。
- 使用 Scores.listWindow 查找靠近用户最高分的一系列分数。
- 要获取有关玩家在特定排行榜中得分的更多信息(例如,玩家是否属于所有玩家的前 12%),请调用 Scores.get。
- 您正在调试游戏吗?尝试调用 Management API 中的 Scores.reset,以重置该玩家在特定排行榜中的所有分数。