Google Play Game Services with libGDX

Introduction

Android changes, libGDX changes and Google Play Services change really fast. In order to implement Google Play Services in a libGDX Android app I proceeded from the excellent tutorial by TheInvader360. Our simple game Cave Fizzer is used as an example here. Its core project contains the CaveFizzer class, extending ApplicationAdapter. There is an android project containing the AndroidLauncher class, as well as a desktop project containing the DesktopLauncher class.

Set up Game Services in the Developer Console

Your app should be uploaded to the developer console. It’s okay if it isn’t published yet or if it is published to alfa or beta testing.

Go to ‘Game services’ in the left sidebar and add a new app. These steps should be pretty self-explanatory (if not, refer to the tutorial, some details may differ). Fill out the details and add the required graphic assets.

I was prompted to link an Android app. If not, go to ‘Linked apps’. Select the package for which you want to use the services (e.g. de.nitri.cavefizzer). In my case a new API project was created automatically with the SHA1 certificate from the signed APK. If this doesn’t happen, click ‘Authorize app’, Fill in the package name and the SHA1 certificate fingerprint of the signed APK. Current versions of the Eclipse Android SDK bundle display this fingerprint on signed export. You can also retrieve it from the APK file yourself:

keytool -list -printcert -jarfile CaveFizzer.apk

Or retrieve it from the keystore:

keytool -exportcert -alias <your-key-name> -keystore <path-to-production-keystore> -list -v

Make sure you copy the SHA1 fingerprint.

The app ID for this API project appears at the bottom of the linked app details page. You’ll need it in your app.

You are required to define at least five achievements. You can unlock them in your game code. Google wants you to upload a unique 512×512 graphic for each achievement. You may consider using the medal assets by Kenney.nl.

Also add a leaderboard. The leaderboard will be updated from your game code by its unique ID.

Finally you can publish your game services or add a valid test account if you don’t want to publish yet.

Add a debug certificate

Using a the debug key in your API project appears to be rather cranky at the time of this writing. First retrieve the SHA1 certificate fingerprint from your debug keystore:

keytool -exportcert -alias androiddebugkey -keystore ~/.android/debug.keystore -list -v

Go to the Google API console and select your project. Add a ‘Client ID’ under ‘APIs and auth’ -> ‘Credentials’. Select ‘Installed Application’ and ‘Android’. Fill in the package name and the debug SHA1 certificate. Save and you’re done. Or not.

In my case the certificate was not accepted when running the app. The only ‘solution’ I found was to link the app again in the ‘Game services’ console, so that it became linked twice. When I removed the second link (only possible before publishing) it stopped working again.

Add the Google Play Services libraries to your project

Start the Android SDK manager and make sure you have the latest version of Google Play services installed. Locate the google-play-services_lib project and copy it to wherever you want to keep your library projects. Import it into Eclipse (File->Import->Android->Existing Projects into Workspace).

Download the sample games, unpack somewhere and import BaseGameUtils into Workspace.

Check the ‘Is library’ box for the projects (Properties->Android->Library). Add these libraries to your game’s Android project (Properties->Android->Library)).

Update the manifest

You need to have these permissions in AndroidManifest.xml:

<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />

Set the minSdkVersion to 9.

Also add these meta elements to ‘application’:

<meta-data android:name="com.google.android.gms.games.APP_ID" android:value="@string/app_id" />
<meta-data android:name="com.google.android.gms.version" android:value="@integer/google_play_services_version"/>

Set the app ID of your API project in res/values/strings.xml:

<string name="app_id">1234567890</string>

Create an interface in your core project

In order to access the services from the core project we’ll create an interface. I used the ActionResolver from the referred tutorial.

ActionResolver.java:

package de.nitri.cavefizzer;
 
public interface ActionResolver {
	public boolean getSignedInGPGS();
 
	public void loginGPGS();
 
	public void submitScoreGPGS(int score);
 
	public void unlockAchievementGPGS(String achievementId);
 
	public void getLeaderboardGPGS();
 
	public void getAchievementsGPGS();
 
}

Have AndroidLauncher implement this interface, as well as the GameHelperListener from BaseGameUtils. Pass the instance of AndroidLauncher to the constructor of the core game (initialize(new CaveFizzer(this), config):

package de.nitri.cavefizzer.android;
 
//...
 
import com.google.android.gms.games.Games;
import com.google.example.games.basegameutils.GameHelper;
import com.google.example.games.basegameutils.GameHelper.GameHelperListener;
 
import de.nitri.cavefizzer.ActionResolver;
import de.nitri.cavefizzer.CaveFizzer;
 
public class AndroidLauncher extends AndroidApplication implements
		GameHelperListener, ActionResolver {
 
	@Override
	protected void onCreate (Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		AndroidApplicationConfiguration config = new AndroidApplicationConfiguration();
		//...
		initialize(new CaveFizzer(this), config);
	}
 
	//...
 
	@Override
	public void onStart() {
		super.onStart();
		gameHelper.onStart(this);
	}
 
	@Override
	public void onStop() {
		super.onStop();
		gameHelper.onStop();
	}
 
	@Override
	public void onActivityResult(int request, int response, Intent data) {
		super.onActivityResult(request, response, data);
		gameHelper.onActivityResult(request, response, data);
	}
 
	@Override
	public boolean getSignedInGPGS() {
		return gameHelper.isSignedIn();
	}
 
	@Override
	public void loginGPGS() {
		try {
			runOnUiThread(new Runnable() {
				public void run() {
					gameHelper.beginUserInitiatedSignIn();
				}
			});
		} catch (final Exception ex) {
		}
	}
 
	@Override
	public void submitScoreGPGS(int score) {
		Games.Leaderboards.submitScore(gameHelper.getApiClient(),
				"CgkI_.............", score);
 
	}
 
	@Override
	public void unlockAchievementGPGS(String achievementId) {
		Games.Achievements.unlock(gameHelper.getApiClient(), achievementId);
 
	}
 
	@Override
	public void getLeaderboardGPGS() {
		if (gameHelper.isSignedIn()) {
			startActivityForResult(
					Games.Leaderboards.getLeaderboardIntent(
							gameHelper.getApiClient(), "CgkI_............."),
					100);
		} else if (!gameHelper.isConnecting()) {
			loginGPGS();
		}
 
	}
 
	@Override
	public void getAchievementsGPGS() {
		if (gameHelper.isSignedIn()) {
			startActivityForResult(
					Games.Achievements.getAchievementsIntent(gameHelper
							.getApiClient()), 101);
		} else if (!gameHelper.isConnecting()) {
			loginGPGS();
		}
 
	}
 
	@Override
	public void onSignInFailed() {
		// TODO Auto-generated method stub
 
	}
 
	@Override
	public void onSignInSucceeded() {
		// TODO Auto-generated method stub
 
	}

Integrate your game logic

Now we can obtain the resolver from the main constructor in our core project:

public CaveFizzer(ActionResolver actionResolver) {
	this.actionResolver = actionResolver;
}

And finally we can use it to access the game services, e.g. on game over:

if (actionResolver.getSignedInGPGS()) {
	actionResolver.submitScoreGPGS(score);
	if (score >= 10)
		actionResolver.unlockAchievementGPGS("CgkI_.............");
	if (score >= 25)
		actionResolver.unlockAchievementGPGS("CgkI_.............");
	if (score >= 50)
		actionResolver.unlockAchievementGPGS("CgkI_.............");
	if (score >= 100)
		actionResolver.unlockAchievementGPGS("CgkI_.............");
	if (score >= 250)
		actionResolver.unlockAchievementGPGS("CgkI_.............");
}

The function of the high scores button depends on whether the user is logged in:

if (Gdx.app.getType() == ApplicationType.Android && circleScoresButton.contains(pos.x, pos.y)) {
	if (actionResolver.getSignedInGPGS())
		actionResolver.getLeaderboardGPGS();
	else
		actionResolver.loginGPGS();
}

That’s about it. The desktop project can be fixed with a dummy resolver interface.

ActionResolverDesktop.java:

package de.nitri.cavefizzer.desktop;
 
import de.nitri.cavefizzer.ActionResolver;
 
public class ActionResolverDesktop implements ActionResolver {	
	boolean signedInStateGPGS = false;
 
	@Override
	public boolean getSignedInGPGS() {
		return signedInStateGPGS;
	}
 
	@Override
	public void loginGPGS() {
		System.out.println("loginGPGS");
		signedInStateGPGS = true;
	}
 
	@Override
	public void submitScoreGPGS(int score) {
		System.out.println("submitScoreGPGS " + score);
	}
 
	@Override
	public void unlockAchievementGPGS(String achievementId) {
		System.out.println("unlockAchievement " + achievementId);
	}
 
	@Override
	public void getLeaderboardGPGS() {
		System.out.println("getLeaderboardGPGS");
	}
 
	@Override
	public void getAchievementsGPGS() {
		System.out.println("getAchievementsGPGS");
	}
 
	// ...
}

2 thoughts on “Google Play Game Services with libGDX

Leave a Reply