Using Google Play Games on the Web

As many of you know, I now work for Google on the Play Games Team. We provide APIs for game developers, implementing useful features like leaderboards and achievements, so the developer doesn't have to. While many Android developers are using our services, adoption on the web could be better, so lets take a look at how to integrate the Google Play Games Services achievements into a web game.

Getting ready

Become a Google developer and have added the game to the Developer Console. I've done this with my Snake game:

Add Game Screen

Link the game to at least one web URL:

Add Achievement Screen

Create a couple of basic achievements for your application:

Add Achievement Screen

Set your Google+ email up as a trusted tester (or publish your game so anyone can log in):

Testing Screen

Here is the achievement setup screen:

Testing Screen

If you have an incremental achievement, check the optional box (I recommend testing one incremental and one non-incremental):

Testing Screen

How do it…

To use play services, players must first authenticate with G+[2]. For the simplest method, include the following in the header of the page:

<meta name="google-signin-clientid" content="YOUR_CLIENT_ID_HERE" />
<meta name="google-signin-cookiepolicy" content="single_host_origin" />
<meta name="google-signin-approvalprompt" content="auto" />
<meta name="google-signin-callback" content="YOUR_JS_CALLBACK_FUNCTION" />
<meta name="google-signin-scope" content="https://www.googleapis.com/auth/games" />

And the G+ button:

<span id="signinButton"><span class="g-signin"></span></span>

And the necessary G+ JavaScript:

<script type="text/javascript">
    (function() {
        var po = document.createElement('script'); po.type = 'text/javascript'; po.async = true;
        po.src = 'https://apis.google.com/js/client:plusone.js';
        var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(po, s);
    })();
</script>

This will load the G+ script, show the login button, and if the user clicks on the login button, authenticate players to use the Games services. When they authenticate G+ will call the JavaScript function specified by google-signin-callback:

function signinCallback(authResult) {
    if (authResult['status']['signed_in']) {
      document.getElementById('signinButton').style.display = 'none';
      // Safe to use API here (gapi.client.games).
    } else {
      // Update the app to reflect a signed out user
      // Possible error values:
      //   "user_signed_out" - User is signed-out
      //   "access_denied" - User denied access to your app
      //   "immediate_failed" - Could not automatically log in the user
      console.log('Sign-in state: ' + authResult['error']);
    }
  };

Once authenticated you can begin to use the Games API[3]. To fetch and store a list of the achievement definitions:

var oAchievementDefinitions = {};
gapi.client.games.achievementDefinitions.list().execute(function(oResponse) {
    var aItems = oResponse.items || [];
    for (var i = 0, j = aItems.length; i < j; i++) {
        oAchievementDefinitions[aItems[i].id] = aItems[i];
    }
});

Before calling APIs on behalf of the player, fetch the player object for the authenticated user:

gapi.client.games.players.get({playerId: 'me'}).execute(function(oPlayer) {
  // Use the player to make API calls here, oPlayer.playerId
});

To fetch a player's achievement progress:

gapi.client.games.achievements.list(
        {forceRefresh: true, playerId: oPlayer.playerId})
    .execute(function(oResponse) {
        // oResponse.items will have the state and player progress for each definition
    });

To complete an achievement:

gapi.client.games.achievements.unlock({achievementId: 'ID_OF_ACHIEVEMENT'})
    .execute(function(oResponse) {
        if (oResponse.newlyUnlocked) {
            // First time the achievement unlocked, show something in the UI
        }
    });

To increment an achievement:

gapi.client.games.achievements.increment(
        {achievementId: 'ID_OF_ACHIEVEMENT', stepsToIncrement: 1}))
    .execute(function(oResponse) {
        if (oResponse.newlyUnlocked) {
            // First time the achievement unlocked, show something in the UI
        }
    });

How it works…

This is a dense article and mostly everything is shown through examples above, so I will keep the writeup brief. The first step is to become a developer on Google[1] and use the console to create the artifacts for your game. It’s pretty straight forward, but will cost a one-time fee of $20.

The two tricky parts to setup are linking an app to a game and setting up trusted testers, if you do not publish the game making it public. For the purposes of a web game, linking an app means entering the URL for the game. Each URL will have its own entry and therefore its own client ID (which goes in the header meta information). You can have any number of linked apps, so have at least one for production and one for development. If you have trouble authenticating G+, make sure the URLs are correct and that you are using the right client ID. Trusted testers are the email address of people who can authenticate against your game, even if the game isn’t published yet. These emails should be the G+ account of testers, and allows developers to authenticate before the app is publicly available.

Unfortunately, since Google Play Games Services is a Google product, it currently only uses G+ for authentication. I think this is limiting, but hopefully a better authentication story can be found in the future. Once, the user authenticates you need to create/fetch a player ID for them from the Games service (shown above). You will have a limited amount of information about the player on the player object, but more importantly can now start calling Games services APIs for your game on the player’s behalf.

The Games services API endpoints are a lot like database tables with definitions and instances. For achievements there is the list of achievement definitions (achievementDefinitions.list()), which are unique per game, and a list of achievement instances (achievements.list()), which are unique per player. The achievement instances reference the achievement definition ID and must be mapped together in your code to properly indicate the state of an achievement for the current player. Incrementing, setting, revealing, or unlocking an achievement are all calls that the developer makes against the API to indicate the player’s current progress in the game.

To see a working web implementation, see version 3 of my Snake game.

I have shown how to work with Achievements, but leaderboards and turn-based multiplayer work much the same. Additionally, once you adopt our services, you have already done the hard work and will be ready for new features which we are frequently adding to the API.

Currently, all APIs are REST and the only optimizations built into the APIs are URL caching through headers. I have been playing with automatic client-side storage of GET requests, but there is a lot of room for improvement. Additionally, it would be cool to build a more developer-friendly wrapper around the REST API to increase adoption. If anybody takes this task up, feel free to reach out to me for reviews and help.

References

  1. Google Developer Console
  2. G+ Client Setup
  3. Play Games API Reference

Event Bubble & Capture Phases

One of the less understood, but powerful feature of browser events are their phases. According to the W3C level 2 spec there are three phases[1]: AT_TARGET=2, BUBBLING_PHASE=3, and CAPTURING_PHASE=1. Most browsers also implement a fourth phase[2]: NONE=0.

Getting ready

Just a quick note that everything discussed in this article is for modern browsers (all browsers except IE <9). Prior to IE 9, Internet Explorer used its own event system, instead of conforming ...

Passing Objects into addEventListener Instead of Functions

I was reviewing the browser event stack the other day and was reminded of a rarely used feature of addEventListener that allows developers to autobind the execution context object, instead of requiring a call to bind or using a library, that is worth sharing, if you weren’t already aware.

How do it…

Typically, when attaching an event, we write:
 var myObj = { handleEvent: function (evt) { // 'this' will be scoped ...

Using Promises to Cache Static AJAX JSON Data

This article showcases a useful caching strategy for static data that is fetch via AJAX. We will use jQuery to setup a promise and cache the data in the localStorage for subsequent page loads or data loads.

Getting ready

A modern web browser supporting localStorage and JSON. Also, a basic understanding of promises[2] is helpful.

How do it…

Here is the code:
 (function($) { var oKeyDeferredMap = {}; function fnReadData(sKey) ...

jQuery Function for Change Event and Delayed Keydown Event

In my experience, it is rare to assign only a change event to a text input, as any callback that would be executed for the change event should also be called on a key event as well, but with a slight delay (think how an autocomplete shows results as you type). This is a common pattern and I was surprised to not immediately find a jQuery plugin implementing it, so I decided to add one ...

Hoisting 102 - Examining a Global Context Hoisting Gotcha

In an earlier article we covered Variable Hoisting in JavaScript. At the time, I did not expect to face a hoisting related code bug so quickly. And given the circumstances of the bug, I expect it to be one of the most common hoisting problems, so I wanted to share it.

How do it…

Let’s jump right into the setup. You have a function that is defined in one JavaScript file (this file ...

jQuery Widget for Dynamic Input Helper Text

This is a proof of concept widget that I demoed for work. The desire is to update some text according to a regex and replacement, when an input field changes. This will allow developers to show a message and/or format the input value, so users understand they do not need to enter meta characters and see the result of their input (think phone or identification numbers). I built a simple jQuery plugin that can be ...

Using PhantomCSS for Regression Testing Your CSS

I finally had a chance to play with PhantomCSS, a CSS regression testing tool that compares screenshots to see if CSS changes have affected the design, and wanted to share my experience with you. We are using it at Ariba to validate that I do not break any designs as I migrate us from two giant, unmanageable CSS files into smaller, modularize and better written lessCSS files. PhantomCSS uses

Rotating Multi-State Button

One of the designers at Ariba, recently came up with a design that needed a tri-state button to govern toggling some content. We came up with an interesting solution where the button has three colors on the outside, with the bottom color governing the content being shown. I am not sure if the widget will survive a usability study, but I put together a proof of concept using HTML 5 technologies and wanted to share ...

Variable Hoisting in JavaScript

The JavaScript Variable Scope article covered the basics of variable scope in JavaScript. Today I’d like to continue that discussion, exploring a more complicated concept called hoisting, where the names of variables declared inside of a function are hoisted to the front of the function, so that they may be accessed, even before they are defined.

How do it…

Here is a simple example of hoisting:
 function foo() { alert(a); var ...