您可以使用 Google Play Games 服務,讓玩家登入以 Firebase 建構並以 C++ 編寫的 Android 遊戲。如要透過 Firebase 使用 Google Play Games 服務登入功能,請先透過 Google Play Games 登入玩家,並在登入時要求 OAuth 2.0 授權碼。接著,將授權碼傳遞至 PlayGamesAuthProvider,產生 Firebase 憑證,即可用來向 Firebase 驗證。
事前準備
如要使用 Firebase Authentication,請先完成下列步驟:
註冊 C++ 專案,並設定使用 Firebase。
如果 C++ 專案已使用 Firebase,則專案已註冊並設定 Firebase。
將 Firebase C++ SDK 新增至 C++ 專案。
請注意,將 Firebase 新增至 C++ 專案時,您需要在Firebase控制台和開啟的 C++ 專案中執行工作 (例如從控制台下載 Firebase 設定檔,然後移至 C++ 專案)。
設定 Firebase 專案
指定應用程式的 SHA-1 指紋 (如果尚未指定)。
您可以使用 gradle
signingReport指令取得簽署憑證的 SHA 雜湊:./gradlew signingReport
如要瞭解如何取得應用程式的 SHA 指紋,請參閱「驗證用戶端」一文。
啟用 Google Play Games 做為登入資訊提供者:
使用 Firebase 應用程式資訊設定 Play Games services
在 Google Play 管理中心中,開啟 Google Play 應用程式或建立應用程式。
在「拓展」部分,依序點選 Play Games services >「設定與管理」>「設定」。
按一下「是,我的遊戲使用了 Google API」,從清單中選取 Firebase 專案,然後按一下「使用」。
在 Play Games services 設定頁面中,按一下「新增憑證」。
- 選取「遊戲伺服器」類型。
- 在「OAuth client」(OAuth 用戶端) 欄位中,選取專案的網路用戶端 ID。請務必使用啟用 Play Games 登入時指定的用戶端 ID。
- 儲存變更。
在 Play Games services 設定頁面中,再次按一下「新增憑證」。
- 選取「Android」類型。
- 在「OAuth client」(OAuth 用戶端) 欄位中,選取專案的 Android 用戶端 ID。 (如果沒有看到 Android 用戶端 ID,請務必在 Firebase 控制台中設定遊戲的 SHA-1 指紋。)
- 儲存變更。
在「測試人員」頁面中,新增所有需要在遊戲發布至 Play Store 前登入遊戲的使用者電子郵件地址。
將 Play 遊戲登入功能整合至遊戲
您必須先整合 Google Play 遊戲登入功能,才能讓玩家登入遊戲。
如要為 C++ Android 專案新增 Play 遊戲登入支援功能,最簡單且建議的方法是使用 Google 登入 C++ SDK。
如要使用 Google 登入 C++ SDK 在遊戲中新增 Play 遊戲登入功能,請完成下列步驟:
複製或下載 Google 登入 Unity 外掛程式存放區,其中也包含 C++ SDK。
使用 Android Studio 或
gradlew build,建構staging/native/目錄中的專案。建構作業會將輸出內容複製到名為
google-signin-cpp的目錄。在遊戲的原生程式碼 make 檔案中加入 Google 登入 C++ SDK:
CMake
在頂層
CMakeLists.txt檔案中:set(GSI_PACKAGE_DIR "/path/to/google-signin-cpp")
add_library(lib-google-signin-cpp STATIC IMPORTED) set_target_properties(lib-google-signin-cpp PROPERTIES IMPORTED_LOCATION ${GSI_PACKAGE_DIR}/lib/${ANDROID_ABI}/libgoogle-signin-cpp.a )
...
target_link_libraries( ... lib-google-signin-cpp)ndk-build
在
Android.mk檔案中:include $(CLEAR_VARS) LOCAL_MODULE := google-signin-cpp GSI_SDK_DIR := /path/to/google-signin-cpp LOCAL_SRC_FILES := $(GSI_SDK_DIR)/lib/$(TARGET_ARCH_ABI)/libgoogle-signin-cpp.a LOCAL_EXPORT_C_INCLUDES := $(GSI_SDK_DIR)/include include $(PREBUILT_STATIC_LIBRARY)
接著,加入 C++ SDK 必要的 Java 輔助元件。
如要這麼做,請在專案層級的
build.gradle檔案中,將 SDK 建構輸出目錄新增為本機存放區:allprojects { repositories { // ... flatDir { dirs 'path/to/google-signin-cpp' } } }在模組層級的
build.gradle檔案中,將輔助元件宣告為依附元件:dependencies { implementation 'com.google.android.gms:play-services-auth:21.5.1' // Depend on the AAR built with the Google Sign-in SDK in order to add // the Java helper classes, which are used by the C++ library. compile(name:'google-signin-cpp-release', ext:'aar') }接著,在遊戲中設定
GoogleSignIn物件,以便使用 Play Games 登入功能並擷取伺服器授權碼:#include "google_signin.h" #include "future.h" using namespace google::signin; // ... GoogleSignIn::Configuration config = {}; config.web_client_id = "YOUR_WEB_CLIENT_ID_HERE"; config.request_id_token = false; config.use_game_signin = true; config.request_auth_code = true; GoogleSignIn gsi = GoogleSignIn(GetActivity(), GetJavaVM()); gsi.Configure(config);最後,呼叫
SignIn(),讓玩家登入 Play 遊戲:Future<GoogleSignIn::SignInResult> &future = gsi.SignIn();當
SignIn()傳回的 Future 解決時,您可以從結果取得伺服器授權碼:if (!future.Pending()) { const GoogleSignIn::StatusCode status = static_cast<GoogleSignIn::StatusCode>(future.Status()); if (status == GoogleSignIn::kStatusCodeSuccess) { // Player successfully signed in to Google Play! Get auth code to // pass to Firebase const GoogleSignIn::SignInResult result = static_cast<GoogleSignIn::SignInResult>(future.Result()); const char* server_auth_code = result.User.GetServerAuthCode(); } }
使用 Firebase 進行驗證
玩家透過 Play 遊戲登入後,您可以使用授權碼向 Firebase 進行驗證。
玩家使用 Play Games 成功登入後,請取得玩家帳戶的授權碼。
接著,將 Play 遊戲服務的授權碼換成 Firebase 憑證,並使用 Firebase 憑證驗證玩家:
firebase::auth::Credential credential = firebase::auth::PlayGamesAuthProvider::GetCredential(server_auth_code); firebase::Future<firebase::auth::AuthResult> result = auth->SignInAndRetrieveDataWithCredential(credential);如果您的程式有定期執行的更新迴圈 (例如每秒 30 或 60 次),您可以透過
Auth::SignInAndRetrieveDataWithCredentialLastResult檢查每次更新的結果:firebase::Future<firebase::auth::AuthResult> result = auth->SignInAndRetrieveDataWithCredentialLastResult(); if (result.status() == firebase::kFutureStatusComplete) { if (result.error() == firebase::auth::kAuthErrorNone) { firebase::auth::AuthResult auth_result = *result.result(); printf("Sign in succeeded for `%s`\n", auth_result.user.display_name().c_str()); } else { printf("Sign in failed with error '%s'\n", result.error_message()); } }
或者,如果您的程式是以事件為導向,您可能會偏好在 Future 上註冊回呼。
在 Future 上註冊回呼
部分程式的Update 函式每秒會呼叫 30 或 60 次。舉例來說,許多遊戲都採用這種模式。這些程式可以呼叫 LastResult 函式,輪詢非同步呼叫。
不過,如果您的程式是以事件驅動,可能比較適合註冊回呼函式。Future 完成後,系統會呼叫回呼函式。
void OnCreateCallback(const firebase::Future<firebase::auth::User*>& result, void* user_data) { // The callback is called when the Future enters the `complete` state. assert(result.status() == firebase::kFutureStatusComplete); // Use `user_data` to pass-in program context, if you like. MyProgramContext* program_context = static_cast<MyProgramContext*>(user_data); // Important to handle both success and failure situations. if (result.error() == firebase::auth::kAuthErrorNone) { firebase::auth::User* user = *result.result(); printf("Create user succeeded for email %s\n", user->email().c_str()); // Perform other actions on User, if you like. firebase::auth::User::UserProfile profile; profile.display_name = program_context->display_name; user->UpdateUserProfile(profile); } else { printf("Created user failed with error '%s'\n", result.error_message()); } } void CreateUser(firebase::auth::Auth* auth) { // Callbacks work the same for any firebase::Future. firebase::Future<firebase::auth::AuthResult> result = auth->CreateUserWithEmailAndPasswordLastResult(); // `&my_program_context` is passed verbatim to OnCreateCallback(). result.OnCompletion(OnCreateCallback, &my_program_context); }
void CreateUserUsingLambda(firebase::auth::Auth* auth) { // Callbacks work the same for any firebase::Future. firebase::Future<firebase::auth::AuthResult> result = auth->CreateUserWithEmailAndPasswordLastResult(); // The lambda has the same signature as the callback function. result.OnCompletion( [](const firebase::Future<firebase::auth::User*>& result, void* user_data) { // `user_data` is the same as &my_program_context, below. // Note that we can't capture this value in the [] because std::function // is not supported by our minimum compiler spec (which is pre C++11). MyProgramContext* program_context = static_cast<MyProgramContext*>(user_data); // Process create user result... (void)program_context; }, &my_program_context); }
後續步驟
使用者首次登入後,系統會建立新的使用者帳戶,並連結至 Play 遊戲 ID。這個新帳戶會儲存在 Firebase 專案中,可用於識別專案中每個應用程式的使用者。
在遊戲中,您可以從 firebase::auth::User 物件取得使用者的 Firebase UID:
firebase::auth::User user = auth->current_user();
if (user.is_valid()) {
std::string playerName = user.displayName();
// The user's ID, unique to the Firebase project.
// Do NOT use this value to authenticate with your backend server,
// if you have one. Use firebase::auth::User::Token() instead.
std::string uid = user.uid();
}
在 Firebase 即時資料庫和 Cloud Storage 安全性規則中,您可以從 auth 變數取得已登入使用者的專屬使用者 ID,並用來控管使用者可存取的資料。
如要取得使用者的 Play 遊戲玩家資訊或存取 Play 遊戲服務,請使用 Google Play 遊戲服務 C++ SDK 提供的 API。
如要登出使用者,請呼叫 SignOut():
auth->SignOut();