前記事の続きです。
下準備
必要なライブラリは1つだけです。
dependencies {
compile 'com.google.android.gms:play-services-games:8.4.0'
}
GoogleApiClientの接続設定
Google Play Game Services(以下、ゲームサービス)へのサインインや実績、リーダーボードの送信などはGoogleApiClient
を使って行われます。
下記のソースコードは、実際にズンドコキヨシゲーム(以下、ズンゲー)での初期化からサインイン周りの実装内容になります。
本来は必要なタイミングで接続等をすべきでしょうが、とりあえず実装だけしてみたかったのでアプリ起動時(正しくはフォアグラウンド移行時)に強制サインインする仕組みにしています。
public class MainActivity extends AppCompatActivity
implements GoogleApiClient.ConnectionCallbacks, GoogleApiClient.OnConnectionFailedListener {
private GoogleApiClient mGoogleApiClient;
private boolean mIntentInProgress;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// 初期設定
// 各種リスナー登録とGoogleAPIで利用するAPIやスコープの設定
mGoogleApiClient = new GoogleApiClient.Builder(this)
.addConnectionCallbacks(this)
.addOnConnectionFailedListener(this)
.addApi(Games.API).addScope(Games.SCOPE_GAMES)
.build();
}
@Override
protected void onStart() {
super.onStart();
// 今回は画面が表示されるたびに必ず接続させる
mGoogleApiClient.connect();
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (requestCode == 100) {
mIntentInProgress = false;
if (resultCode != RESULT_OK) {
// エラーの場合、resultCodeにGamesActivityResultCodes内の値が入っている
return;
}
if (!mGoogleApiClient.isConnected()) {
mGoogleApiClient.reconnect();
}
}
}
@Override
public void onConnected(Bundle bundle) {
// プレイヤー情報取得
Player p = Games.Players.getCurrentPlayer(mGoogleApiClient);
// 試しにプレイヤー名を表示
String displayName = "???";
if (p != null) {
displayName = p.getDisplayName();
}
Toast.makeText(this, String.format("%s でログインしています。", displayName), Toast.LENGTH_SHORT).show();
}
@Override
public void onConnectionSuspended(int i) {
mGoogleApiClient.connect();
}
@Override
public void onConnectionFailed(ConnectionResult connectionResult) {
int errorCode = connectionResult.getErrorCode();
// サインインしていない場合、サインイン処理を実行する
if (errorCode == ConnectionResult.SIGN_IN_REQUIRED
&& !mIntentInProgress && connectionResult.hasResolution()) {
try {
mIntentInProgress = true;
connectionResult.startResolutionForResult(this, 100);
} catch (IntentSender.SendIntentException e) {
mIntentInProgress = false;
mGoogleApiClient.connect();
}
}
}
}
サインイン部分は(おそらく)Playゲームアプリを通しているため、onActivityResult()
にて結果を見る必要があります。
さらにちょっとややこしい部分だと思いますが、非サインイン時は.connect()
を実行した際にonConnectionFailed()
が呼ばれるので、その中のErrorCode
を見て適宜サインインの処理を呼び出す必要が出てきます。
この辺りは課金関連のやりとりに近いところもあるので、その辺を実装した経験のある人には見慣れた内容かもしれません。
なお、.isConnected()
を呼び出すことで、Google APIに接続できているか確認することができます。というか、できない状態でGoogleApiClientを利用すると容赦なくクラッシュするので(※)、以降の実績やリーダーボードの送信時にも必ず接続状況を見るようにしましょう。
※ この事は公開後に知ったので、ズンドコキヨシゲームv1.0.1は非ログイン状態で動かすと容赦なくアプリが落ちます!
各種実装
実績やリーダーボードへの情報送信には、各アイテムのIDが必要になります。
これらはGoogle Play Developer Consoleで確認することができますし、一括してリソース化したテキストデータも表示してくれます。
モザイク化した部分が必要なID、下の「リソースを取得」で登録したIDのstrings.xmlのリソースデータを生成してくれます。
実績解除の送信
実績の解除送信は、メソッド一つで実装できます。
Games.Achievements.unlock(googleApiClient, /* 取得する実績のID */);
ここは「実績」というシステムに慣れている方には説明不要ですが、解除した実績の取り消しは行えません。
送信自体も呼び出せば取り消しは不可能なので、実績解除の判定ロジックには気をつけましょう。
リーダーボードの送信
リーダーボードにデータを送る場合も、メソッド一つです。
Games.Leaderboards.submitScore(googleApiClient,
/* リーダーボードのID */, /* 送信する値 */);
複数回送信した場合、毎回上書きされるわけではなくゲームサービス側で一番良い値を採用してくれるようです。
こちらも送信した値は取り消せませんし、「送信する値」がダイレクトにリーダーボードに反映されるところに注意してください。
例えばあるタイミングで送信する値がおかしくなってしまった場合、リーダーボードの結果がむちゃくちゃになってしまい、新規のリーダーボードが必要になってしまいます(この辺は例の改ざん対策である程度対応できるかもしれませんが、不明です)。
実績画面、リーダーボード画面呼び出し
ユーザーの実績解除の確認画面や、リーダーボード画面は(おそらく)Play ゲームを呼び出して表示する形での実装になります。
// リーダーボード画面の呼び出し
startActivityForResult(Games.Leaderboards.getLeaderboardIntent(mGoogleApiClient,
/* 見たいリーダーボードのID */ , /*REQUEST_CODE*/);
// 実績画面の呼び出し
startActivityForResult(Games.Achievements.getAchievementsIntent(mGoogleApiClient),
/*REQUEST_CODE*/);
左が実績画面、右がリーダーボード画面
リーダーボード画面は2人しかいませんが、3人までは最上段で横並び表示、4人目以降は下に並ぶ感じで表示されるようです。
以上がズンゲーで実装した内容になります。サインイン+実績やリーダーボードの登録はこれだけでできますし、さらにゲームサービスに登録することでPlayゲームの録画機能を利用できるようになるので、積極的に導入してみるのも良いかなと思いました。
備考
実はゲームサービスから、ユーザーの分析や収益データをチェックすることができるようです。
ズンゲーではそもそも利用者がほぼいない+課金要素がないということで、ほとんどデータとしては役に立たないですが、ちゃんと利用者がいて課金要素のあるゲームであれば、なかなか興味深いデータを分析できるんじゃないでしょうか。