您可以使用 Google Play 遊戲服務,讓玩家登入在 Firebase 上建構且以 C++ 語言編寫的 Android 遊戲。如要使用 Firebase 登入 Google Play 遊戲服務,請先使用 Google Play 遊戲登入玩家,然後在驗證期間要求 OAuth 2.0 驗證碼。接著,將驗證碼傳遞至 PlayGamesAuthProvider
以產生 Firebase 憑證,並在 Firebase 進行驗證。
事前準備
使用 Firebase 驗證前,您必須先完成以下事項:
註冊 C++ 專案,並將其設為使用 Firebase。
如果您的 C++ 專案已使用 Firebase,則專案已針對 Firebase 完成註冊並完成相關設定。
在 C++ 專案中新增 Firebase C++ SDK。
請注意,將 Firebase 新增至 C++ 專案時,牽涉到 Firebase 控制台和開放式 C++ 專案中的工作 (例如從控制台下載 Firebase 設定檔,再移至 C++ 專案)。
設定 Firebase 專案
如果您尚未設定遊戲的 SHA-1 指紋,請前往 Firebase 控制台的「設定」頁面完成操作。
您可以使用 Gradle
signingReport
指令取得簽署憑證的 SHA 雜湊:./gradlew signingReport
啟用 Google Play 遊戲做為登入提供者:
在 Firebase 控制台中,開啟「Authentication」專區。
產生並取得專案的網路伺服器用戶端 ID 和用戶端密鑰:
在「Sign in method」分頁中,啟用「Google」登入提供者。
複製 Google 登入供應商的網路伺服器用戶端 ID 和密鑰。
在「Sign in method」分頁中,啟用「Play Games」登入供應商,並指定專案的網路伺服器用戶端 ID 和用戶端密鑰 (您在上一個步驟中取得)。
使用 Firebase 應用程式資訊設定 Play 遊戲服務
在 Google Play 管理中心開啟您的 Google Play 應用程式或建立一個應用程式。
在「Grow」部分中,依序按一下「Play Games services」>「Setup & Management」>「Configuration」。
按一下「是,我的遊戲已在使用 Google API」,從清單中選取您的 Firebase 專案,然後按一下「使用」。
在 Play 遊戲服務設定頁面上,按一下「新增憑證」。
- 選取「遊戲伺服器」類型。
- 在「OAuth 用戶端」欄位中,選取專案的網路用戶端 ID。請確認這組用戶端 ID 與您啟用 Play 遊戲登入功能時指定的用戶端 ID 相同。
- 儲存變更。
同樣在 Play 遊戲服務設定頁面,再按一下「Add Credential」。
- 選取「Android」類型。
- 在「OAuth client」(OAuth 用戶端) 欄位中,選取專案的 Android 用戶端 ID。(如果沒有看到您的 Android 用戶端 ID,請務必在 Firebase 控制台中設定遊戲的 SHA-1 指紋)。
- 儲存變更。
在「測試人員」頁面上,為需要能夠登入遊戲的使用者,在 Play 商店發布遊戲前,新增他們的電子郵件地址。
將 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)
接下來,請加入 Java 輔助元件,這是 C++ SDK 的必要元件。
方法是在專案層級的
build.gradle
檔案中,將 SDK 版本輸出目錄新增為本機存放區:allprojects { repositories { // ... flatDir { dirs 'path/to/google-signin-cpp' } } }
接著,在模組層級
build.gradle
檔案中,將輔助元件宣告為依附元件:dependencies { implementation 'com.google.android.gms:play-services-auth:21.2.0' // 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 遊戲登入功能及擷取伺服器驗證碼:#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 遊戲登入後,取得玩家帳戶的驗證碼。
接著,使用 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 上註冊回呼
某些程式包含可呼叫每秒 30 或 60 次的Update
函式。舉例來說,許多遊戲都採用這個模型。這些程式可以呼叫 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); }如有需要,回呼函式也可以是 lambda。
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,並使用該 ID 控制使用者可以存取哪些資料。
如要取得使用者的 Play 遊戲玩家資訊,或存取 Play 遊戲服務,請使用 Google Play 遊戲服務 C++ SDK 提供的 API。
如要將使用者登出,請呼叫 SignOut()
:
auth->SignOut();