本文档介绍了如何将现有游戏从游戏 v1 SDK 迁移到游戏 v2 SDK。 适用于 Unity 的 Play 游戏插件(版本 10 及更低版本)使用游戏 v1 SDK。
准备工作
- 确保您已设置 Play 管理中心并安装 Unity 编辑器。
下载适用于 Unity 的 Google Play 游戏插件
如需使用 Play 游戏服务的最新功能,请下载并安装最新版本的插件。您可以从 GitHub 代码库下载该插件。
移除旧插件
在 Unity 编辑器中,移除以下文件夹或文件。
Assets/GooglePlayGames Assets/GeneratedLocalRepo/GooglePlayGames Assets/Plugins/Android/GooglePlayGamesManifest.androidlib Assets/Plugins/Android
将新插件导入 Unity 项目
如需将插件导入 Unity 项目,请按以下步骤操作:
- 打开游戏项目。
- 在 Unity 编辑器中,依次点击 Assets > Import Package > Custom Package
,将下载的
unitypackage文件导入到项目资源中。 确保当前的 build 平台已设置为 Android 。
在主菜单中,依次点击 File > Build Settings 。
选择 Android ,然后点击 Switch Platform 。
Window > Google Play Games 下应该会显示新的菜单项。如果未显示,请点击 Assets > Refresh 来刷新资源,然后再次尝试设置 build 平台。
在 Unity 编辑器中,依次点击 File > Build Settings > Player Settings > Other Settings 。
在 目标 API 级别 框中,选择一个版本。
在 Scripting backend 框中,输入
IL2CPP。在 Target architectures 框中,选择一个值。
记下软件包名称 package_name。您稍后可以使用此信息 。
Unity 项目中的玩家设置。
迁移途径
游戏的正确迁移途径取决于游戏实现 Play 游戏服务 v1 和处理玩家身份的方式。为确保顺利过渡并防止玩家数据丢失,请确定最符合您现有设置的场景,然后按照相应的步骤操作。
选项 1:适用于 IGA 绑定到 Play 游戏服务玩家 ID 的游戏
此场景适用于以下游戏:已将 Play 游戏服务 Player ID 用作玩家的游戏内账号 (IGA) 的唯一标识符,并且之前未请求或存储
OpenID。主要挑战在于将现有 IGA 链接到主要标识符 (OpenID),同时不会丢失与玩家进度的连接。
迁移流程包括以下步骤:
- 游戏启动时,Play 游戏服务 v2 SDK 会自动以静默方式对平台进行身份验证。
游戏会显示登录界面。此界面必须包含使用 Google 账号登录 (SiWG)按钮,以替换 Google Play 按钮。如需集成,请执行以下操作:
将 CredManBridge.java 下载到您的文件夹。此 Java 类充当 Unity 和
androidx.credentials库之间的桥梁。CredManBridge.java
package com.wickedcube.trivialkart; import android.accounts.Account; import android.content.Context; import android.util.Log; import android.os.CancellationSignal; import androidx.credentials.CredentialManager; import androidx.credentials.GetCredentialRequest; import androidx.credentials.GetCredentialResponse; import androidx.credentials.exceptions.GetCredentialException; import androidx.credentials.exceptions.NoCredentialException; import com.google.android.libraries.identity.googleid.GetGoogleIdOption; import com.google.android.libraries.identity.googleid.GoogleIdTokenCredential; import com.google.android.gms.auth.api.identity.AuthorizationClient; import com.google.android.gms.auth.api.identity.AuthorizationRequest; import com.google.android.gms.auth.api.identity.AuthorizationResult; import com.google.android.gms.common.api.ApiException; import com.google.android.gms.auth.api.identity.Identity; import com.google.android.gms.common.api.Scope; import com.unity3d.player.UnityPlayer; import java.util.Collections; import java.util.List; import java.util.concurrent.Executor; import java.util.concurrent.Executors;public class CredManBridge {
// --- MODE 1: SILENT SIGN-IN (Called on Awake) --- // Tries to auto-select an authorized account. If it fails, it does NOT show UI. public static void signInSilent(Context context, String webClientId) { CredentialManager credentialManager = CredentialManager.create(context); CancellationSignal cancellationSignal = new CancellationSignal(); Executor executor = Executors.newSingleThreadExecutor();
Log.d("CredMan", "Attempting Silent Sign-In...");
GetGoogleIdOption silentOption = new GetGoogleIdOption.Builder() .setFilterByAuthorizedAccounts(true) // Strict: Only authorized accounts .setServerClientId(webClientId) .setAutoSelectEnabled(true) // Auto-select if possible .build();
GetCredentialRequest silentRequest = new GetCredentialRequest.Builder() .addCredentialOption(silentOption) .build();
credentialManager.getCredentialAsync( context, silentRequest, cancellationSignal, executor, new androidx.credentials.CredentialManagerCallback<GetCredentialResponse, GetCredentialException>() { @Override public void onResult(GetCredentialResponse result) { Log.d("CredMan", "Silent Sign-In Successful!"); handleSignInResult(context, result, webClientId); }
@Override public void onError(GetCredentialException e) { // Send a specific error code so Unity knows to just stay on the Start Screen Log.d("CredMan", "Silent sign-in failed. Keeping UI hidden."); UnityPlayer.UnitySendMessage("AuthManager", "OnSignInError", "SilentFailed"); } }); }
// --- MODE 2: INTERACTIVE SIGN-IN (Called on Button Click) --- // Forces the Account Selection / "Add Account" sheet to appear. public static void signInInteractive(Context context, String webClientId) { CredentialManager credentialManager = CredentialManager.create(context); CancellationSignal cancellationSignal = new CancellationSignal(); Executor executor = Executors.newSingleThreadExecutor();
Log.d("CredMan", "Starting Interactive Sign-In...");
GetGoogleIdOption interactiveOption = new GetGoogleIdOption.Builder() .setFilterByAuthorizedAccounts(false) // Show ALL accounts (and "Add Account") .setServerClientId(webClientId) .setAutoSelectEnabled(false) // Force the UI to show .build();
GetCredentialRequest interactiveRequest = new GetCredentialRequest.Builder() .addCredentialOption(interactiveOption) .build();
credentialManager.getCredentialAsync( context, interactiveRequest, cancellationSignal, executor, new androidx.credentials.CredentialManagerCallback<getcredentialresponse, getcredentialexception="">() { @Override public void onResult(GetCredentialResponse result) { Log.d("CredMan", "Interactive Sign-In Successful!"); handleSignInResult(context, result, webClientId); }</getcredentialresponse,>
@Override public void onError(GetCredentialException e) { Log.e("CredMan", "Interactive Sign-In Canceled or Failed", e); UnityPlayer.UnitySendMessage("AuthManager", "OnSignInError", "Canceled"); } }); }
private static void handleSignInResult(Context context, GetCredentialResponse result, String webClientId) { try { GoogleIdTokenCredential credential = GoogleIdTokenCredential.createFrom(result.getCredential().getData()); String email = credential.getId();
Account account = new Account(email, "com.google"); // Requesting GAMES_LITE scope to check for pre-existing V1 grants List<Scope> requestedScopes = Collections.singletonList(new Scope("https://www.googleapis.com/auth/games_lite")); AuthorizationRequest authRequest = new AuthorizationRequest.Builder() .setRequestedScopes(requestedScopes) .setAccount(account) .requestOfflineAccess(webClientId) .build(); AuthorizationClient authClient = Identity.getAuthorizationClient(context); authClient.authorize(authRequest) .addOnSuccessListener(authorizationResult -> { if (authorizationResult.getServerAuthCode() != null) { // CASE 1: RETURNING USER (Success) // The user has already granted GAMES_LITE in the past. // We got the code directly without showing UI. Log.i("CredMan", "PGS v1: Existing grant found. Returning user detected. Auth Code retrieved."); UnityPlayer.UnitySendMessage("AuthManager", "OnSignInSuccess", authorizationResult.getServerAuthCode()); } else if (authorizationResult.hasResolution()) { // CASE 2: NEW USER (PendingIntent) // The user has NOT granted GAMES_LITE before. The API returned a PendingIntent // (authorizationResult.getPendingIntent()) to show the consent screen. // As per your flow, we DISCARD this intent and do not show UI. Log.i("CredMan", "PGS v1: No existing grant (PendingIntent returned). This is a NEW user or they revoked access."); Log.i("CredMan", "PGS v1: Discarding PendingIntent. Proceeding as New User."); // Notify Unity that this is a "New User" so it can trigger V2 logic instead of failing UnityPlayer.UnitySendMessage("AuthManager", "OnSignInError", "NewUser_NoGrant"); } else { // Edge Case: No code and no resolution? Log.e("CredMan", "PGS v1: Authorization success but no Auth Code or Resolution returned."); UnityPlayer.UnitySendMessage("AuthManager", "OnSignInError", "No Auth Code returned"); } }) .addOnFailureListener(e -> { // CASE 3: GENERIC FAILURE Log.e("CredMan", "PGS v1: Authorization failed completely.", e); UnityPlayer.UnitySendMessage("AuthManager", "OnSignInError", "Authorization Failed: " + e.getMessage()); });} catch (Exception e) { UnityPlayer.UnitySendMessage("AuthManager", "OnSignInError", "Parsing Error: " + e.getMessage()); } } }
Credential Manager 集成:
- 将
GetGoogleIdOption与setFilterByAuthorizedAccounts(true)结合使用,以进行静默登录,仅允许之前已授权应用的用户登录。 - 将
setFilterByAuthorizedAccounts(false)用于互动式登录,以允许用户选择账号或添加新账号。
- 将
范围请求:
- 获取基本 Google 凭据后,它会创建一个
AuthorizationRequest,请求特定的旧版范围: https://www.googleapis.com/auth/games_lite。 - 此范围至关重要,因为它会授予服务器查找用户旧版 PlayerID 的权限。
- 获取基本 Google 凭据后,它会创建一个
结果处理:
- 如果用户授予权限(或之前已授予权限),桥接器会将
ServerAuthCode返回给 Unity。 - 如果用户未授予权限(新用户场景),API 会返回
PendingIntent。在此示例中,intent 会被舍弃,用户会被视为新用户,以简化流程。
- 如果用户授予权限(或之前已授予权限),桥接器会将
如需支持 Credential Manager 和 Google Identity 服务,请确保将以下依赖项添加到
mainTemplate.gradleGradle 配置中。dependencies { // Standard Unity dependencies implementation fileTree(dir: 'libs', include: ['*.jar']) // Credential Manager and Identity Libraries implementation 'androidx.credentials:credentials:1.3.0' implementation 'androidx.credentials:credentials-play-services-auth:1.3.0' implementation 'com.google.android.libraries.identity.googleid:googleid:1.1.1' // Play Services Auth for legacy scope handling implementation 'com.google.android.gms:play-services-auth:21.2.0' }
- Credential Manager: 处理核心身份编排和账号选择界面。
- GoogleID 库: 专门提供
GetGoogleIdOption以检索OpenIDConnect 令牌。 - Play 服务身份验证: 需要此项才能保持兼容性,并请求
GAMES_LITE范围以检索旧版Player ID。
当玩家点按 SiWG 按钮并选择 Google 账号时,游戏必须检索两个不同的标识符:
OpenID,用于绑定 IGA 的主要标识符。- Play 游戏服务
Player ID,通过使用GAMES_LITE范围检索,以在后端系统中查找玩家的 IGA 并执行绑定。
在后续游戏启动中,玩家可以通过 SiWG 流程访问其 IGA,而无需游戏使用
Player ID作为主要标识符。
您可以使用游戏客户端实现执行第 4 步。
- 开发者调用 Android Credential Manager API,以使用 Google 账号让用户登录。
- 用户完成 SiwG 并选择 Google 账号后,开发者会收到一个结果对象,其中包含 ID 令牌、电子邮件地址。
- 开发者会根据电子邮件地址构建一个账号对象。
- 开发者使用
GAMES_LITE范围和账号调用 Authorization API。 - 如果账号对
GAMES_LITE范围具有预先存在的授权,Authorization API 会直接在响应对象中返回令牌。- 使用响应令牌调用 Play 游戏服务服务器并检索 Play 游戏服务
Player ID。 - 开发者验证 Play 游戏服务
Player ID是否已与游戏内账号关联。- 开发者知道这是 Play 游戏服务 v1 的回访用户。
- 开发者可以将新的 gaia ID 与之前的 Play 游戏服务 v1 账号相关联。
- 使用响应令牌调用 Play 游戏服务服务器并检索 Play 游戏服务
- 或者,如果账号对
GAMES_LITE范围没有预先存在的授权,Authorization API 会返回 PendingIntent。- 开发者知道用户没有 Play 游戏服务 v1 中的现有账号。
- 开发者可以安全地舍弃 PendingIntent,而无需显示任何界面。
选项 2:适用于已将 IGA 绑定到 OpenID 的游戏
此组中的开发者拥有最直接的迁移途径。如果游戏的游戏内账号已主要绑定到 OpenID,您只需按照步骤中所述,执行从 v1 到 v2 的标准技术 SDK 迁移。
更新自动登录代码
将 PlayGamesClientConfiguration 初始化类替换为 PlayGamesPlatform.Instance.Authenticate()
类。
无需初始化和激活
PlayGamesPlatform
。调用 PlayGamesPlatform.Instance.Authenticate()
会获取自动登录的结果。
如需详细了解集成 Play 游戏服务 v2
时推荐的身份验证流程,请参阅理想身份验证流程的用户体验指南。
C#
在 Unity 编辑器中,找到包含 PlayGamesClientConfiguration 类的文件。
using GooglePlayGames;
using GooglePlayGames.BasicApi;
using UnityEngine.SocialPlatforms;
public void Start() {
PlayGamesClientConfiguration config =
new PlayGamesClientConfiguration.Builder()
// Enables saving game progress
.EnableSavedGames()
// Requests the email address of the player be available
// will bring up a prompt for consent
.RequestEmail()
// Requests a server auth code be generated so it can be passed to an
// associated backend server application and exchanged for an OAuth token
.RequestServerAuthCode(false)
// Requests an ID token be generated. This OAuth token can be used to
// identify the player to other services such as Firebase.
.RequestIdToken()
.Build();
PlayGamesPlatform.InitializeInstance(config);
// recommended for debugging:
PlayGamesPlatform.DebugLogEnabled = true;
// Activate the Google Play Games platform
PlayGamesPlatform.Activate();
}
然后更新为以下代码:
using GooglePlayGames;
public void Start() {
PlayGamesPlatform.Instance.Authenticate(ProcessAuthentication);
}
internal void ProcessAuthentication(SignInStatus status) {
if (status == SignInStatus.Success) {
// Continue with Play Games Services
} else {
// Disable your integration with Play Games Services or show a login
// button to ask users to sign-in. Clicking it should call
// PlayGamesPlatform.Instance.ManuallyAuthenticate(ProcessAuthentication).
}
}
选择社交平台
如需选择社交平台,请参阅 选择社交平台。
检索服务器身份验证代码
如需获取服务器端访问代码, 请参阅检索服务器身份验证代码。
移除退出代码
移除退出代码。Play 游戏服务不再需要游戏内退出按钮。
移除以下示例中显示的代码:
C#
// sign out
PlayGamesPlatform.Instance.SignOut();
测试游戏
通过测试确保游戏按设计运行。您执行的测试取决于游戏的功能。
以下是要运行的常见测试列表。
成功登录 。
自动登录功能正常运行。用户应在启动游戏后登录 Play 游戏服务。
系统会显示欢迎弹出式窗口。
欢迎弹出式窗口示例(点击可放大)。 系统会显示成功的日志消息。在终端运行以下命令:
adb logcat | grep com.google.android.
以下示例展示了一条成功的日志消息:
[
$PlaylogGamesSignInAction$SignInPerformerSource@e1cdecc number=1 name=GAMES_SERVICE_BROKER>], returning true for shouldShowWelcomePopup. [CONTEXT service_id=1 ]
确保界面组件的一致性 。
在 Play 游戏服务用户界面 (UI) 中,弹出式窗口、排行榜和成就可在各种屏幕尺寸和屏幕方向上正确且一致地显示。
退出选项在 Play 游戏服务界面中不可见。
确保您可以成功检索 Player ID,并且如果适用,服务器端功能可以按预期运行。
如果游戏使用服务器端身份验证,请彻底测试
requestServerSideAccess流程。确保服务器收到身份验证代码并可以将其换成访问令牌。 测试网络错误、无效client ID场景的成功和失败情况。
如果您的游戏使用了以下任何功能,请对其进行测试,以确保它们与迁移前的工作方式相同:
- 排行榜:提交得分并查看排行榜。检查玩家姓名和得分的排名和显示是否正确。
- 成就:解锁成就并验证它们是否已正确记录 并显示在 Play 游戏界面中。
- 游戏存档:如果游戏使用游戏存档,请确保保存和加载游戏进度可以顺利进行。这一点对于跨多个设备以及在应用更新后进行测试尤为重要。
迁移后任务
迁移到游戏 v2 SDK 后,请完成以下步骤。