مهاجرت به Play Games Services v2 (Unity)

این سند نحوه انتقال بازی‌های موجود از games v1 SDK به games v2 SDK را شرح می‌دهد. افزونه Play Games برای Unity، نسخه‌های 10 و قبل از آن، از games v1 SDK استفاده می‌کند.

قبل از اینکه شروع کنی

  • مطمئن شوید که قبلاً Play Console را راه‌اندازی کرده و Unity Editor را نصب کرده‌اید.

افزونه Google Play Games را برای Unity دانلود کنید

برای بهره‌مندی از جدیدترین ویژگی‌های سرویس‌های بازی‌های Play، آخرین نسخه افزونه را دانلود و نصب کنید. آن را از مخزن gitHub دانلود کنید.

افزونه قدیمی را حذف کنید

در ویرایشگر Unity، پوشه‌ها یا فایل‌های زیر را حذف کنید.

Assets/GooglePlayGames

Assets/GeneratedLocalRepo/GooglePlayGames

Assets/Plugins/Android/GooglePlayGamesManifest.androidlib

Assets/Plugins/Android
پوشه‌های هایلایت شده را در پروژه Unity خود حذف کنید.
پوشه‌های هایلایت شده را در پروژه Unity خود حذف کنید (برای بزرگنمایی کلیک کنید).

افزونه جدید را به پروژه Unity خود وارد کنید

برای وارد کردن افزونه به پروژه Unity خود، این مراحل را دنبال کنید:

  1. پروژه بازی خود را باز کنید.
  2. در ویرایشگر یونیتی، روی Assets > Import Package > Custom Package کلیک کنید تا فایل unitypackage دانلود شده را به asset های پروژه خود وارد کنید.
  3. مطمئن شوید که پلتفرم ساخت فعلی شما روی اندروید تنظیم شده است.

    1. در منوی اصلی، روی File > Build Settings کلیک کنید.

    2. اندروید را انتخاب کنید و روی «تغییر پلتفرم» کلیک کنید.

    3. باید یک آیتم منوی جدید در زیر Window > Google Play Games وجود داشته باشد. اگر وجود ندارد، با کلیک روی Assets > Refresh ، فایل‌ها را به‌روزرسانی کنید و سپس دوباره سعی کنید پلتفرم ساخت را تنظیم کنید.

  4. در ویرایشگر یونیتی، روی فایل > تنظیمات ساخت > تنظیمات پخش‌کننده > سایر تنظیمات کلیک کنید.

  5. در کادر Target API level ، یک نسخه را انتخاب کنید.

  6. در کادر Scripting backend ، IL2CPP را وارد کنید.

  7. در کادر Target architectures ، یک مقدار انتخاب کنید.

  8. به نام بسته package_name توجه کنید. می‌توانید بعداً از این اطلاعات استفاده کنید.

    تنظیمات پخش‌کننده در پروژه یونیتی شما
    تنظیمات پخش‌کننده در پروژه یونیتی شما.
  9. کپی کردن منابع اندروید از کنسول پلی

  10. منابع اندروید را به پروژه یونیتی خود اضافه کنید

مسیرهای مهاجرت

مسیر صحیح مهاجرت برای بازی شما به نحوه پیاده‌سازی Play Games Services v1 و مدیریت هویت بازیکن بستگی دارد. برای اطمینان از انتقال روان و جلوگیری از از دست رفتن اطلاعات بازیکن، سناریویی را که به بهترین وجه با تنظیمات موجود شما مطابقت دارد، شناسایی کرده و مراحل مربوطه را دنبال کنید.

گزینه ۱: برای بازی‌هایی که IGA ملزم به استفاده از شناسه بازیکن سرویس‌های Play Games است

این سناریو در مورد بازی‌هایی اعمال می‌شود که Player ID Play Games Services به عنوان تنها شناسه برای حساب درون بازی (IGA) بازیکن استفاده کرده‌اند و قبلاً OpenID درخواست یا ذخیره نکرده‌اند. چالش اصلی، پیوند دادن IGA موجود به یک شناسه اصلی ( OpenID ) بدون از دست دادن ارتباط با پیشرفت بازیکن است.

جریان مهاجرت شامل مراحل زیر است:

  1. وقتی بازی اجرا می‌شود، Play Games Services v2 SDK به طور خودکار و بی‌صدا پلتفرم را احراز هویت می‌کند.
  2. بازی صفحه ورود به سیستم خود را نمایش می‌دهد. این صفحه باید دارای دکمه ورود با گوگل (SiWG) باشد که جایگزین دکمه گوگل پلی می‌شود. برای ادغام:

    1. فایل CredManBridge.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");
          }
      }
      

      ); }

      // --- حالت ۲: ورود تعاملی (با کلیک روی دکمه فراخوانی می‌شود) --- // صفحه انتخاب حساب / "افزودن حساب" را مجبور به نمایش می‌کند. public static void signInInteractive(Context context, String webClientId) { CredentialManager credentialManager = CredentialManager.create(context); CancellationSignal cancellationSignal = new CancellationSignal(); Executor executor = Executors.newSingleThreadExecutor();

      Log.d("CredMan", "شروع ورود تعاملی...");

      GetGoogleIdOption interactiveOption = new GetGoogleIdOption.Builder() .setFilterByAuthorizedAccounts(false) // نمایش همه حساب‌ها (و "افزودن حساب") .setServerClientId(webClientId) .setAutoSelectEnabled(false) // نمایش اجباری رابط کاربری .build();

      GetCredentialRequest interactiveRequest = new GetCredentialRequest.Builder().addCredentialOption(interactiveOption).build();

      credentialManager.getCredentialAsync(context, interactiveRequest, cancellationSignal, executor, new androidx.credentials.CredentialManagerCallback () { @Override public void onResult(GetCredentialResponse result) { Log.d("CredMan", "ورود تعاملی موفقیت‌آمیز بود!"); handleSignInResult(context, result, webClientId); }

          @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", "خطای تجزیه: " + e.getMessage()); } } }

    2. یکپارچه‌سازی مدیریت اعتبارنامه‌ها:

      • برای ورود بی‌صدا، GetGoogleIdOption به همراه setFilterByAuthorizedAccounts(true) استفاده کنید تا فقط کاربرانی که قبلاً برنامه را تأیید کرده‌اند، وارد سیستم شوند.
      • برای ورودهای تعاملی setFilterByAuthorizedAccounts(false) استفاده کنید تا به کاربران اجازه دهید یک حساب کاربری انتخاب کنند یا یک حساب کاربری جدید اضافه کنند.
    3. درخواست محدوده:

      • پس از دریافت اعتبارنامه پایه گوگل، یک AuthorizationRequest ایجاد می‌کند که محدوده خاص legacy scope را درخواست می‌کند: https://www.googleapis.com/auth/games_lite .
      • این محدوده بسیار مهم است زیرا به سرور اجازه می‌دهد تا PlayerID قدیمی کاربر را جستجو کند.
    4. پردازش نتایج:

      • اگر کاربر اجازه دهد (یا قبلاً آن را داده باشد)، پل، ServerAuthCode را به Unity برمی‌گرداند.
      • اگر کاربر مجوزی اعطا نکرده باشد (سناریوی کاربر جدید)، API یک PendingIntent برمی‌گرداند. در این نمونه، اینتنت نادیده گرفته می‌شود و کاربر به عنوان یک کاربر جدید در نظر گرفته می‌شود تا روند کار ساده‌تر شود.
  3. برای پشتیبانی از سرویس‌های Credential Manager و Google Identity، مطمئن شوید که وابستگی‌های زیر به پیکربندی gradle mainTemplate.gradle اضافه شده‌اند.

    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'
    }
    • مدیر اعتبارنامه: هماهنگ‌سازی هویت اصلی و رابط کاربری برای انتخاب حساب را مدیریت می‌کند.
    • کتابخانه GoogleID: به طور خاص GetGoogleIdOption برای بازیابی توکن‌های OpenID Connect ارائه می‌دهد.
    • تأیید سرویس‌های پخش: برای حفظ سازگاری و درخواست دامنه GAMES_LITE برای بازیابی Player ID قدیمی، مورد نیاز است.
  4. وقتی بازیکن دکمه SiWG را لمس می‌کند و یک حساب گوگل انتخاب می‌کند، بازی باید دو شناسه مجزا را بازیابی کند:

    • OpenID ، شناسه اصلی برای اتصال IGA.
    • Player ID سرویس‌های بازی‌های Play، که با استفاده از دامنه GAMES_LITE بازیابی می‌شود، برای جستجوی IGA بازیکن در سیستم backend شما و انجام اتصال.
  5. در انتشارهای بعدی بازی، بازیکنان می‌توانند از طریق جریان SiWG به IGA خود دسترسی پیدا کنند، بدون اینکه بازی‌ها از Player ID به عنوان شناسه اصلی استفاده کنند.

شما می‌توانید مرحله ۴ را با استفاده از پیاده‌سازی سمت کلاینت بازی انجام دهید.

  1. توسعه‌دهنده، API مدیریت اعتبارنامه اندروید را فراخوانی می‌کند تا کاربر را با یک حساب گوگل وارد سیستم کند.
  2. پس از اینکه کاربر SiwG را تکمیل و یک حساب گوگل انتخاب کرد، توسعه‌دهنده یک شیء نتیجه دریافت می‌کند که حاوی توکن شناسه و آدرس ایمیل است.
  3. توسعه‌دهنده یک شیء حساب کاربری (Account) از آدرس ایمیل می‌سازد.
  4. توسعه‌دهنده، API احراز هویت را با دامنه GAMES_LITE و حساب کاربری فراخوانی می‌کند.
  5. اگر حساب کاربری از قبل در محدوده GAMES_LITE مجوز داشته باشد، API احراز هویت یک توکن را مستقیماً در شیء پاسخ برمی‌گرداند.
    1. از توکن پاسخ برای فراخوانی سرورهای Play Games Services و بازیابی Player ID Play Games Services استفاده کنید.
    2. توسعه‌دهنده تأیید می‌کند که آیا Player ID Play Games Services به یک حساب درون‌بازی مرتبط شده است یا خیر.
      1. توسعه‌دهنده می‌داند که این یک کاربر قدیمی از Play Games Services نسخه ۱ است.
    3. توسعه‌دهنده می‌تواند شناسه جدید گایا را به حساب قبلی Play Games Services v1 پیوند دهد.
  6. یا اگر حساب کاربری از قبل مجوزی در محدوده GAMES_LITE نداشته باشد، API احراز هویت یک PendingIntent برمی‌گرداند.
    1. توسعه‌دهنده می‌داند که کاربر از Play Games Services نسخه ۱ حساب کاربری ندارد.
    2. توسعه‌دهنده می‌تواند با خیال راحت PendingIntent را بدون نمایش هیچ رابط کاربری (UI) حذف کند.

گزینه ۲: برای بازی‌هایی که از قبل IGA را به OpenID متصل کرده‌اند

توسعه‌دهندگان این گروه، ساده‌ترین مسیر مهاجرت را دارند. اگر حساب درون بازی شما از قبل به OpenID متصل است، فقط باید مهاجرت استاندارد فنی SDK را از نسخه ۱ به نسخه ۲، همانطور که در مراحل ذکر شده است، انجام دهید.

کد ورود خودکار را به‌روزرسانی کنید

کلاس مقداردهی اولیه PlayGamesClientConfiguration را با کلاس PlayGamesPlatform.Instance.Authenticate() جایگزین کنید. مقداردهی اولیه و فعال‌سازی PlayGamesPlatform الزامی نیست. فراخوانی PlayGamesPlatform.Instance.Authenticate() نتیجه ورود خودکار را دریافت می‌کند. برای اطلاعات بیشتر در مورد جریان احراز هویت توصیه‌شده با ادغام Play Games Services v2، به راهنمای تجربه کاربری برای جریان احراز هویت ایده‌آل مراجعه کنید.

سی شارپ

در ویرایشگر یونیتی، فایل‌های حاوی کلاس 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 دیگر نیازی به دکمه خروج از بازی ندارند.

کد نشان داده شده در مثال زیر را حذف کنید:

سی شارپ

// sign out
PlayGamesPlatform.Instance.SignOut();

بازی خود را آزمایش کنید

با آزمایش کردن بازی خود، از عملکرد آن مطابق با طراحی اطمینان حاصل کنید. آزمایش‌هایی که انجام می‌دهید به ویژگی‌های بازی شما بستگی دارد.

در ادامه لیستی از آزمایش‌های رایج برای اجرا آمده است.

  1. ورود موفقیت‌آمیز .

    1. ورود خودکار کار می‌کند. کاربر باید هنگام اجرای بازی، وارد سرویس‌های بازی‌های پلی شود.

    2. پنجره خوشامدگویی نمایش داده می‌شود.

      نمونه پنجره خوشامدگویی.
      نمونه پنجره خوشامدگویی (برای بزرگنمایی کلیک کنید).

    3. پیام‌های ثبت موفقیت‌آمیز نمایش داده می‌شوند. دستور زیر را در ترمینال اجرا کنید:

      adb logcat | grep com.google.android.

      یک پیام ثبت موفقیت‌آمیز در مثال زیر نشان داده شده است:

      [$PlaylogGamesSignInAction$SignInPerformerSource@e1cdecc
      number=1 name=GAMES_SERVICE_BROKER>], returning true for shouldShowWelcomePopup.
      [CONTEXT service_id=1 ]
  2. از سازگاری اجزای رابط کاربری اطمینان حاصل کنید .

    1. پنجره‌های بازشو، جدول امتیازات و دستاوردها به درستی و به طور مداوم در اندازه‌ها و جهت‌های مختلف صفحه نمایش در رابط کاربری (UI) سرویس‌های بازی‌های Play نمایش داده می‌شوند.

    2. گزینه خروج از سیستم در رابط کاربری سرویس‌های بازی‌های پلی قابل مشاهده نیست.

    3. مطمئن شوید که می‌توانید با موفقیت شناسه بازیکن را بازیابی کنید، و در صورت لزوم، قابلیت‌های سمت سرور مطابق انتظار کار می‌کنند.

    4. اگر بازی از احراز هویت سمت سرور استفاده می‌کند، جریان requestServerSideAccess را به طور کامل آزمایش کنید. مطمئن شوید که سرور کد احراز هویت را دریافت می‌کند و می‌تواند آن را با یک توکن دسترسی مبادله کند. سناریوهای موفقیت و شکست را برای خطاهای شبکه و سناریوهای client ID نامعتبر آزمایش کنید.

اگر بازی شما از هر یک از ویژگی‌های زیر استفاده می‌کرد، آنها را آزمایش کنید تا مطمئن شوید که مانند قبل از انتقال کار می‌کنند:

  • جدول امتیازات : امتیازات را ارسال کنید و جدول امتیازات را مشاهده کنید. رتبه‌بندی صحیح و نمایش نام و امتیازات بازیکنان را بررسی کنید.
  • دستاوردها : دستاوردها را باز کنید و تأیید کنید که آنها به درستی ثبت شده و در رابط کاربری بازی‌های Play نمایش داده می‌شوند.
  • بازی‌های ذخیره‌شده : اگر بازی از بازی‌های ذخیره‌شده استفاده می‌کند، مطمئن شوید که ذخیره و بارگذاری پیشرفت بازی بی‌نقص انجام می‌شود. این امر به‌ویژه برای آزمایش در چندین دستگاه و پس از به‌روزرسانی‌های برنامه بسیار مهم است.

وظایف پس از مهاجرت

پس از مهاجرت به SDK بازی‌ها نسخه ۲، مراحل زیر را انجام دهید.

  1. استفاده از امضای برنامه Play

  2. ایجاد فایل AAB

  3. یک نسخه آزمایشی داخلی ایجاد کنید

  4. اعتبارنامه‌های امضای برنامه خود را تأیید کنید