隨著 Google Sign-In API 淘汰,我們將在 2026 年移除遊戲第 1 版 SDK。2025 年 2 月後,您將無法在 Google Play 上發布新整合 games v1 SDK 的遊戲。建議您改用 games v2 SDK。
雖然採用舊版遊戲第 1 版整合功能的現有遊戲仍可繼續運作幾年,但建議您從 2025 年 6 月開始遷移至第 2 版。
如果您的遊戲使用後端伺服器,建議您使用 Google 登入驗證玩家,並安全地將玩家身分傳遞至後端伺服器。這也能讓遊戲以安全的方式擷取玩家身分資料和其他資料,避免在透過裝置傳遞的過程中可能遭到竄改的風險。
在這種情況下,遊戲會提示玩家照常登入 Google Play 遊戲服務。玩家成功登入後,GoogleSignInAccount
物件就會包含一組一次性代碼 (稱為「伺服器驗證碼」),用戶端會將這組代碼傳遞至伺服器。接著,在伺服器上交換伺服器驗證碼,以取得伺服器可用以呼叫 Google Play 遊戲服務 API 的 OAuth 2.0 權杖。
如需在遊戲中加入登入功能的其他指引,請參閱「Android 遊戲登入」。
如需詳細的程式碼範例,說明如何使用 Google 登入功能驗證玩家,請參閱 GitHub 上的 clientserverskeleton
範例。
離線存取必須採取以下步驟:
- 在 Google Play 管理中心:建立遊戲伺服器的憑證。憑證的 OAuth 用戶端類型為「網站」。
- 在 Android 應用程式:在登入時要求取得伺服器憑證的伺服器驗證碼,然後將該驗證碼傳遞至伺服器。
- 在遊戲伺服器:使用 Google 驗證服務交換 OAuth 存取權杖的伺服器驗證碼,然後使用此驗證碼呼叫 Play 遊戲服務 REST API。
事前準備
您必須先在 Google Play 管理中心中新增遊戲,才能將 Google 登入功能整合至遊戲,如「設定 Google Play 遊戲服務」所述。
為遊戲建立相關的伺服器端網頁應用程式
Google Play 遊戲服務無法提供網路遊戲的後端支援,但是會提供 Android 遊戲伺服器的後端伺服器支援。
如果要在伺服器端應用程式中使用 Google Play 遊戲服務的 REST API,請按照下列步驟操作:
- 在 Google Play 管理中心的「已連結的應用程式」部分,為遊戲建立相關聯的網頁應用程式。請注意,
launch_url
不會用於這個流程,因此可以留空。 - 如要取得應用程式的憑證資訊,請按照下列步驟操作:
- 在 Google Play 管理中心的遊戲中,按一下「遊戲詳細資料」。
- 捲動至「API 控制台專案」部分,然後點選 API 控制台專案的連結。
- 在 Google API 控制台的「API 和服務」>「憑證」畫面中,下載網頁應用程式的
client_secret.json
檔案,並儲存至伺服器可存取的位置。記下憑證的用戶端 ID,以供日後參考。
- 重新啟動伺服器端應用程式,讓應用程式準備好接受遊戲用戶端應用程式的要求。
在用戶端執行登入作業
GoogleSignInClient
類別是擷取目前已登入的玩家帳戶的主要進入點,如果玩家尚未在裝置上登入應用程式,則可讓玩家登入。
如要建立登入用戶端,請按照下列步驟操作:
- 透過
GoogleSignInOptions
物件建立登入用戶端。如要設定登入功能,您必須在GoogleSignInOptions.Builder
中指定GoogleSignInOptions.DEFAULT_GAMES_SIGN_IN
。 - 您也必須指定遊戲需要後端伺服器的驗證碼,方法是呼叫
GoogleSignInOptions.Builder.requestServerAuthCode()
方法,並將伺服器的用戶端 ID 做為參數。您稍後會在後端伺服器上擷取驗證碼,以取得存取權杖,如「取得伺服器驗證碼」一節所述。 - 呼叫
GoogleSignIn.getClient()
方法,並傳入先前設定的選項。如果呼叫成功,Google 登入 API 會傳回GoogleSignInClient
的例項。 - 取得
GoogleSignInClient
例項後,請繼續從活動的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); }
取得伺服器驗證碼
如要擷取遊戲在後端伺服器取得存取權杖使用的伺服器驗證碼,請在 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(); } } }
在伺服器上交換伺服器驗證碼以取得存取權杖
將伺服器驗證碼傳送至後端伺服器,以交換取得存取權和更新權杖。請使用存取權杖代表玩家呼叫 Google Play 遊戲服務 API,然後可選擇是否儲存更新權杖,方便在存取權杖過期時取得新的存取權杖。
下列程式碼片段展示如何以 Java 程式設計語言實作伺服器端程式碼,交換伺服器驗證碼以取得存取權杖。這會使用 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 遊戲服務的 REST API」。
以下是幾個實用的 REST API 呼叫範例,歡迎參考:
球員
- 想要取得已登入玩家的 ID 和設定檔資料嗎?以
'me'
做為 ID 呼叫 Players.get。
六人行
請務必詳閱「好友」指南,進一步瞭解好友功能。
- 想擷取玩家的好友名單嗎?以
'friends_all'
做為collection
呼叫 Players.list。 - 請確認你是否有權存取好友名單。針對
me
呼叫 Players.get,然後查看回應中的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,重設玩家在特定排行榜的所有分數