このドキュメントでは、既存のゲームを games v1 SDK から games v2 SDK に移行する方法について説明します。
始める前に
Android Studio など、お好みの IDE を使用してゲームを移行できます。games v2 に移行する前に、次の手順を完了します。
- Android Studio をダウンロードしてインストールする
- ゲームで Google Play Games サービス v1 の SDK を使用している
依存関係を更新する
モジュールの
build.gradle
ファイルで、モジュール レベルの依存関係にある次の行を見つけます。implementation "com.google.android.gms:play-services-games-v1:+"
次のコードに置き換えます。
implementation "com.google.android.gms:play-services-games-v2:version"
version は、ゲーム SDK の最新バージョンに置き換えます。
依存関係を更新したら、このドキュメントのすべての手順を完了してください。
プロジェクト ID を定義する
Play ゲームサービス SDK のプロジェクト ID をアプリに追加する手順は次のとおりです。
AndroidManifest.xml
ファイルで、次の<meta-data>
要素と属性を<application>
要素に追加します。<manifest> <application> <meta-data android:name="com.google.android.gms.games.APP_ID" android:value="@string/game_services_project_id"/> </application> </manifest>
ゲームのゲームサービスのプロジェクト ID を値として使用し、文字列リソース参照
@string/game_services_project_id
を定義します。ゲームサービスのプロジェクト ID は、Google Play Console の [設定] ページのゲーム名で確認できます。res/values/strings.xml
ファイルで、文字列リソース参照を追加し、値としてプロジェクト ID を設定します。次に例を示します。<!-- res/values/strings.xml --> <resources> <!-- Replace 0000000000 with your game’s project id. Example value shown above. --> <string translatable="false" name="game_services_project_id"> 0000000000 </string> </resources>
非推奨の Google ログインから移行する
GoogleSignInClient
クラスを GamesSignInClient
クラスに置き換えます。
Java
GoogleSignInClient
クラスのファイルを見つけます。
import com.google.android.gms.auth.api.signin.GoogleSignIn;
import com.google.android.gms.auth.api.signin.GoogleSignInClient;
import com.google.android.gms.auth.api.signin.GoogleSignInOptions;
// ... existing code
@Override
public void onCreate(@Nullable Bundle bundle) {
super.onCreate(bundle);
// ... existing code
GoogleSignInOptions signInOption =
new GoogleSignInOptions.Builder(GoogleSignInOptions.DEFAULT_GAMES_SIGN_IN).build();
// Client used to sign in to Google services
GoogleSignInClient googleSignInClient =
GoogleSignIn.getClient(this, signInOptions);
}
それを次のように更新します。
import com.google.android.gms.games.PlayGamesSdk;
import com.google.android.gms.games.PlayGames;
import com.google.android.gms.games.GamesSignInClient;
// ... existing code
@Override
public void onCreate(){
super.onCreate();
PlayGamesSdk.initialize(this);
// Client used to sign in to Google services
GamesSignInClient gamesSignInClient =
PlayGames.getGamesSignInClient(getActivity());
}
Kotlin
GoogleSignInClient
クラスのファイルを見つけます。
import com.google.android.gms.auth.api.signin.GoogleSignIn
import com.google.android.gms.auth.api.signin.GoogleSignInClient
import com.google.android.gms.auth.api.signin.GoogleSignInOptions
// ... existing code
val signInOptions = GoogleSignInOptions.DEFAULT_GAMES_SIGN_IN
// ... existing code
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val googleSignInClient: GoogleSignInClient =
GoogleSignIn.getClient(this, signInOptions)
}
それを次のように更新します。
import com.google.android.gms.games.PlayGames
import com.google.android.gms.games.PlayGamesSdk
import com.google.android.gms.games.GamesSignInClient
// ... existing code
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
PlayGamesSdk.initialize(this)
// client used to sign in to Google services
val gamesSignInClient: GamesSignInClient =
PlayGames.getGamesSignInClient(this)
}
GoogleSignIn
コードを更新する
ゲーム v2 SDK では GoogleSignIn
API はサポートされていません。次の例に示すように、GoogleSignIn
API コードを GamesSignInClient
API に置き換えます。
サーバーサイドのアクセス トークンをリクエストするには、GamesSignInClient.requestServerSideAccess()
メソッドを使用します。詳しくは、サーバーサイド アクセス クラスを更新するをご覧ください。
Java
GoogleSignIn
クラスのファイルを見つけます。
// Request code used when invoking an external activity.
private static final int RC_SIGN_IN = 9001;
private boolean isSignedIn() {
GoogleSignInAccount account = GoogleSignIn.getLastSignedInAccount(this);
GoogleSignInOptions signInOptions =
GoogleSignInOptions.DEFAULT_GAMES_SIGN_IN;
return GoogleSignIn.hasPermissions(account, signInOptions.getScopeArray());
}
private void signInSilently() {
GoogleSignInOptions signInOptions =
GoogleSignInOptions.DEFAULT_GAMES_SIGN_IN;
GoogleSignInClient signInClient = GoogleSignIn.getClient(this, signInOptions);
signInClient
.silentSignIn()
.addOnCompleteListener(
this,
task -> {
if (task.isSuccessful()) {
// The signed-in account is stored in the task's result.
GoogleSignInAccount signedInAccount = task.getResult();
showSignInPopup();
} else {
// Perform interactive sign in.
startSignInIntent();
}
});
}
private void startSignInIntent() {
GoogleSignInClient signInClient = GoogleSignIn.getClient(this,
GoogleSignInOptions.DEFAULT_GAMES_SIGN_IN);
Intent intent = signInClient.getSignInIntent();
startActivityForResult(intent, RC_SIGN_IN);
}
@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()) {
// The signed-in account is stored in the result.
GoogleSignInAccount signedInAccount = result.getSignInAccount();
showSignInPopup();
} 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();
}
}
}
private void showSignInPopup() {
Games.getGamesClient(requireContext(), signedInAccount)
.setViewForPopups(contentView)
.addOnCompleteListener(
task -> {
if (task.isSuccessful()) {
logger.atInfo().log("SignIn successful");
} else {
logger.atInfo().log("SignIn failed");
}
});
}
それを次のように更新します。
private void signInSilently() {
gamesSignInClient.isAuthenticated().addOnCompleteListener(isAuthenticatedTask -> {
boolean isAuthenticated =
(isAuthenticatedTask.isSuccessful() &&
isAuthenticatedTask.getResult().isAuthenticated());
if (isAuthenticated) {
// Continue with Play Games Services
} else {
// If authentication fails, either disable Play Games Services
// integration or
// display a login button to prompt players to sign in.
// Use`gamesSignInClient.signIn()` when the login button is clicked.
}
});
}
@Override
protected void onResume() {
super.onResume();
// When the activity is inactive, the signed-in user's state can change;
// therefore, silently sign in when the app resumes.
signInSilently();
}
Kotlin
GoogleSignIn
クラスのファイルを見つけます。
// Request codes we use when invoking an external activity.
private val RC_SIGN_IN = 9001
// ... existing code
private fun isSignedIn(): Boolean {
val account = GoogleSignIn.getLastSignedInAccount(this)
val signInOptions = GoogleSignInOptions.DEFAULT_GAMES_SIGN_IN
return GoogleSignIn.hasPermissions(account, *signInOptions.scopeArray)
}
private fun signInSilently() {
val signInOptions = GoogleSignInOptions.DEFAULT_GAMES_SIGN_IN
val signInClient = GoogleSignIn.getClient(this, signInOptions)
signInClient.silentSignIn().addOnCompleteListener(this) { task ->
if (task.isSuccessful) {
// The signed-in account is stored in the task's result.
val signedInAccount = task.result
// Pass the account to showSignInPopup.
showSignInPopup(signedInAccount)
} else {
// Perform interactive sign in.
startSignInIntent()
}
}
}
private fun startSignInIntent() {
val signInClient = GoogleSignIn.getClient(this, GoogleSignInOptions.DEFAULT_GAMES_SIGN_IN)
val intent = signInClient.signInIntent
startActivityForResult(intent, RC_SIGN_IN)
}
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
if (requestCode == RC_SIGN_IN) {
val result = Auth.GoogleSignInApi.getSignInResultFromIntent(data)
if (result.isSuccess) {
// The signed-in account is stored in the result.
val signedInAccount = result.signInAccount
showSignInPopup(signedInAccount) // Pass the account to showSignInPopup.
} else {
var message = result.status.statusMessage
if (message == null || message.isEmpty()) {
message = getString(R.string.signin_other_error)
}
AlertDialog.Builder(this)
.setMessage(message)
.setNeutralButton(android.R.string.ok, null)
.show()
}
}
}
private fun showSignInPopup(signedInAccount: GoogleSignInAccount) {
// Add signedInAccount parameter.
Games.getGamesClient(this, signedInAccount)
.setViewForPopups(contentView) // Assuming contentView is defined.
.addOnCompleteListener { task ->
if (task.isSuccessful) {
logger.atInfo().log("SignIn successful")
} else {
logger.atInfo().log("SignIn failed")
}
}
}
それを次のように更新します。
private fun signInSilently() {
gamesSignInClient.isAuthenticated.addOnCompleteListener { isAuthenticatedTask ->
val isAuthenticated = isAuthenticatedTask.isSuccessful &&
isAuthenticatedTask.result.isAuthenticated
if (isAuthenticated) {
// Continue with Play Games Services
} else {
// To handle a user who is not signed in, either disable Play Games Services integration
// or display a login button. Selecting this button calls `gamesSignInClient.signIn()`.
}
}
}
override fun onResume() {
super.onResume()
// Since the state of the signed in user can change when the activity is
// not active it is recommended to try and sign in silently from when the
// app resumes.
signInSilently()
}
GamesSignInClient
コードを追加する
プレーヤーがログインに成功したら、Play ゲームサービスのログインボタンをゲームから削除します。ゲーム起動時にログインしないことをユーザーが選択した場合は、Play ゲームサービスのアイコンのボタンを表示し続け、GamesSignInClient.signIn()
でログイン プロセスを開始します。
Java
private void startSignInIntent() {
gamesSignInClient
.signIn()
.addOnCompleteListener( task -> {
if (task.isSuccessful() && task.getResult().isAuthenticated()) {
// sign in successful
} else {
// sign in failed
}
});
}
Kotlin
private fun startSignInIntent() {
gamesSignInClient
.signIn()
.addOnCompleteListener { task ->
if (task.isSuccessful && task.result.isAuthenticated) {
// sign in successful
} else {
// sign in failed
}
}
}
ログアウト コードを削除する
GoogleSignInClient.signOut
のコードを削除します。
次の例に示すコードを削除します。
Java
// ... existing code
private void signOut() {
GoogleSignInClient signInClient = GoogleSignIn.getClient(this,
GoogleSignInOptions.DEFAULT_GAMES_SIGN_IN);
signInClient.signOut().addOnCompleteListener(this,
new OnCompleteListener() {
@Override
public void onComplete(@NonNull Task task) {
// At this point, the user is signed out.
}
});
}
Kotlin
// ... existing code
private fun signOut() {
val signInClient = GoogleSignIn.getClient(this, GoogleSignInOptions.DEFAULT_GAMES_SIGN_IN)
signInClient.signOut().addOnCompleteListener(this) {
// At this point, the user is signed out.
}
}
自動ログインが成功したことを確認する
自動ログインしているかどうかを確認し、カスタム ロジックが利用可能な場合は追加するコードを以下に示します。
Java
private void checkIfAutomaticallySignedIn() {
gamesSignInClient.isAuthenticated().addOnCompleteListener(isAuthenticatedTask -> {
boolean isAuthenticated =
(isAuthenticatedTask.isSuccessful() &&
isAuthenticatedTask.getResult().isAuthenticated());
if (isAuthenticated) {
// Continue with Play Games Services
// If your game requires specific actions upon successful sign-in,
// you can add your custom logic here.
// For example, fetching player data or updating UI elements.
} else {
// Disable your integration with Play Games Services or show a
// login button to ask players to sign-in. Clicking it should
// call GamesSignInClient.signIn().
}
});
}
Kotlin
private void checkIfAutomaticallySignedIn() {
gamesSignInClient.isAuthenticated()
.addOnCompleteListener { task ->
val isAuthenticated = task.isSuccessful && task.result?.isAuthenticated ?: false
if (isAuthenticated) {
// Continue with Play Games Services
} else {
// Disable your integration or show a login button
}
}
}
クライアント クラスの名前とメソッドを更新する
ゲーム v2 に移行すると、クライアント クラス名を取得するために使用されるメソッドが異なります。Games.getxxxClient()
メソッドの代わりに、対応する PlayGames.getxxxClient()
メソッドを使用します。
たとえば、LeaderboardsClient
の場合は、Games.getLeaderboardsClient()
メソッドではなく PlayGames.getLeaderboardsClient()
を使用します。
ゲーム v2 には代替クラスがないため、GamesClient
クラスと GamesMetadataClient
クラスに関連するコードを削除します。
Java
LeaderboardsClient
のコードを見つけます。
import com.google.android.gms.games.LeaderboardsClient;
import com.google.android.gms.games.Games;
@Override
public void onCreate(@Nullable Bundle bundle) {
super.onCreate(bundle);
// Get the leaderboards client using Play Games services.
LeaderboardsClient leaderboardsClient = Games.getLeaderboardsClient(this,
GoogleSignIn.getLastSignedInAccount(this));
}
それを次のように更新します。
import com.google.android.gms.games.LeaderboardsClient;
import com.google.android.gms.games.PlayGames;
@Override
public void onCreate(@Nullable Bundle bundle) {
super.onCreate(bundle);
// Get the leaderboards client using Play Games services.
LeaderboardsClient leaderboardsClient = PlayGames.getLeaderboardsClient(getActivity());
}
Kotlin
LeaderboardsClient
のコードを見つけます。
import com.google.android.gms.games.LeaderboardsClient
import com.google.android.gms.games.Games
// Initialize the variables.
private lateinit var leaderboardsClient: LeaderboardsClient
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
leaderboardsClient = Games.getLeaderboardsClient(this,
GoogleSignIn.getLastSignedInAccount(this))
}
それを次のように更新します。
import com.google.android.gms.games.LeaderboardsClient
import com.google.android.gms.games.PlayGames
// Initialize the variables.
private lateinit var leaderboardsClient: LeaderboardsClient
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
leaderboardsClient = PlayGames.getLeaderboardsClient(this)
}
同様に、AchievementsClient
、EventsClient
、GamesSignInClient
、PlayerStatsClient
、RecallClient
、SnapshotsClient
、PlayersClient
の各クライアントに対応するメソッドを使用します。
サーバーサイドのアクセス クラスを更新する
サーバーサイドのアクセス トークンをリクエストするには、GoogleSignInAccount.getServerAuthCode()
メソッドではなく GamesSignInClient.requestServerSideAccess()
メソッドを使用します。
詳しくは、サーバー認証コードを送信するをご覧ください。
次の例は、サーバーサイドのアクセス トークンをリクエストする方法を示しています。
Java
GoogleSignInOptions
クラスのコードを確認します。
private static final int RC_SIGN_IN = 9001;
private GoogleSignInClient googleSignInClient;
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);
}
/** 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();
}
}
}
それを次のように更新します。
private void startRequestServerSideAccess() {
GamesSignInClient gamesSignInClient = PlayGames.getGamesSignInClient(this);
gamesSignInClient
.requestServerSideAccess(OAUTH_2_WEB_CLIENT_ID,
/* forceRefreshToken= */ false, /* additional AuthScope */ scopes)
.addOnCompleteListener(task -> {
if (task.isSuccessful()) {
AuthResponse authresp = task.getResult();
// Send the authorization code as a string and a
// list of the granted AuthScopes that were granted by the
// user. Exchange for an access token.
// Verify the player with Play Games Services REST APIs.
} else {
// Authentication code retrieval failed.
}
});
}
Kotlin
GoogleSignInOptions
クラスのコードを確認します。
// ... existing code
private val RC_SIGN_IN = 9001
private lateinit var googleSignInClient: GoogleSignInClient
// Auth code to send to backend server.
private var mServerAuthCode: String? = null
private fun startSignInForAuthCode() {
// Client ID for your backend server.
val webClientId = getString(R.string.webclient_id)
val signInOption = GoogleSignInOptions.Builder(GoogleSignInOptions.DEFAULT_GAMES_SIGN_IN)
.requestServerAuthCode(webClientId)
.build()
googleSignInClient = GoogleSignIn.getClient(this, signInOption)
val intent = googleSignInClient.signInIntent
startActivityForResult(intent, RC_SIGN_IN)
}
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
if (requestCode == RC_SIGN_IN) {
val result = Auth.GoogleSignInApi.getSignInResultFromIntent(data)
if (result.isSuccess) {
mServerAuthCode = result.signInAccount.serverAuthCode
} else {
var message = result.status.statusMessage
if (message == null || message.isEmpty()) {
message = getString(R.string.signin_other_error)
}
AlertDialog.Builder(this).setMessage(message)
.setNeutralButton(android.R.string.ok, null).show()
}
}
}
それを次のように更新します。
private void startRequestServerSideAccess() {
GamesSignInClient gamesSignInClient = PlayGames.getGamesSignInClient(this);
gamesSignInClient
.requestServerSideAccess(OAUTH_2_WEB_CLIENT_ID, /* forceRefreshToken= */ false,
/* additional AuthScope */ scopes)
.addOnCompleteListener(task -> {
if (task.isSuccessful()) {
AuthResponse authresp = task.getResult();
// Send the authorization code as a string and a
// list of the granted AuthScopes that were granted by the
// user. Exchange for an access token.
// Verify the player with Play Games Services REST APIs.
} else {
// Authentication code retrieval failed.
}
});
}
GoogleApiClient からの移行
古い統合の場合、ゲームは Play ゲームサービス SDK の GoogleApiClient
API のバリエーションに依存している可能性があります。これは 2017 年後半に非推奨となり、「接続のない」クライアントに置き換えられました。移行するには、GoogleApiClient
クラスを「接続なし」の同等のものに置き換えます。次の表に、ゲーム v1 からゲーム v2 への一般的なクラス マッピングを示します。
games v2(現在) | games v1(以前のバージョン) |
---|---|
com.google.android.gms.games.AchievementsClient | com.google.android.gms.games.achievement.Achievements |
com.google.android.gms.games.LeaderboardsClient | com.google.android.gms.games.leaderboard.Leaderboard |
com.google.android.gms.games.SnapshotsClient | com.google.android.gms.games.snapshot.Snapshots |
com.google.android.gms.games.PlayerStatsClient | com.google.android.gms.games.stats.PlayerStats |
com.google.android.gms.games.PlayersClient | com.google.android.gms.games.Players |
com.google.android.gms.games.GamesClientStatusCodes | com.google.android.gms.games.GamesStatusCodes |
ゲームをビルドして実行する
Android Studio でビルドして実行するには、アプリをビルドして実行するをご覧ください。
ゲームをテストする
ゲームをテストして、設計どおりに機能することを確認します。実施するテストは、ゲームの機能によって異なります。
実行する一般的なテストのリストを次に示します。
ログインに成功しました。
自動ログインが機能している。ゲームの起動時に、ユーザーが Play ゲームサービスにログインしている必要があります。
ウェルカム ポップアップが表示されます。
ウェルカム ポップアップの例(クリックして拡大)。 成功したログメッセージが表示されます。ターミナルで次のコマンドを実行します。
adb logcat | grep com.google.android.
成功したログメッセージの例を次に示します。
[
$PlaylogGamesSignInAction$SignInPerformerSource@e1cdecc number=1 name=GAMES_SERVICE_BROKER>], returning true for shouldShowWelcomePopup. [CONTEXT service_id=1 ]
UI コンポーネントの一貫性を確保する。
ポップアップ、リーダーボード、実績が、Play ゲームサービス ユーザー インターフェース(UI)のさまざまな画面サイズと向きで正しく一貫して表示される。
Play Games Services の UI にログアウト オプションが表示されない。
プレーヤー ID を正常に取得できること、およびサーバーサイドの機能が想定どおりに動作することを確認します。
ゲームでサーバーサイド認証を使用している場合は、
requestServerSideAccess
フローを徹底的にテストします。サーバーが認証コードを受け取り、アクセス トークンと交換できることを確認します。ネットワーク エラー、無効なclient ID
シナリオの成功と失敗の両方のシナリオをテストします。
ゲームで次の機能を使用していた場合は、移行前と同じように動作することを確認するためにテストします。
- リーダーボード: スコアを送信してリーダーボードを表示します。プレーヤーの名前とスコアのランキングと表示が正しいことを確認します。
- 実績: 実績をロック解除し、実績が正しく記録され、Google Play ゲーム UI に表示されることを確認します。
- 保存済みゲーム: ゲームで保存済みゲームを使用している場合は、ゲームの進行状況の保存と読み込みが問題なく動作することを確認します。これは、複数のデバイスでテストする場合や、アプリの更新後にテストする場合に特に重要です。
移行後のタスク
ゲーム v2 に移行したら、次の手順を行います。
ゲームを公開する
APK をビルドし、Google Play Console でゲームを公開します。
- Android Studio のメニューで、[Build] > [Build Bundles(s) / APK(s)] > [Build APK(s)] を選択します。
- ゲームを公開します。 詳しくは、 Play Console から限定公開アプリを公開するをご覧ください。