This codelab will teach you how to extend an existing Unity game to run on an Android device and integrate Play Game Services. In this section, we will set up the Unity environment with all of the materials you will need to complete this codelab.

What you'll learn

What you'll need

Game Summary

For this codelab, we'll be using a simple and entertaining game, called "Lollygagger". The main game play is to travel along a path, atop a robot, shooting Lollipop "cannonballs" at the Androids that are lollygagging around. Key features of note are:

How will you use use this tutorial?

Read it through only Read it and complete the exercises

How would you rate your experience with building Unity apps?

Novice Intermediate Proficient

How would you rate your experience with building Android apps?

Novice Intermediate Proficient

How would you rate your experience with Google Play Game Services?

Novice Intermediate Proficient

Open a terminal window and clone the repository:

$ git clone https://github.com/googlecodelabs/playservices_unity.git

Then get the Google Play Game Services plugin by cloning it from GitHub also:

$ git clone https://github.com/playgameservices/play-games-plugin-for-unity.git

Next, we'll build the starter game.

First, we'll create a new Unity project, add the starter game and make sure it runs. From there, we'll add Play Game services.

Start Unity

  1. Start Unity!
  2. Create a new 3D project called something like Lollygagger.
  3. If you have not developed an Android application with Unity before, make sure you configure the Android SDK within Unity. In Unity, click Preferences > External Tools > Android SDK Location, then select the folder where you downloaded and unzipped the Android SDK.

Import the game assets

Import the sample game scenes and assets into your project:

  1. Click Assets > Import Package > Custom Package.
  2. Select the lollygagger_step0.unitypackage file you downloaded
  3. Click Import.

Configure the Android player

  1. Configure Unity to build an Android application:
  1. Add the game scenes to the build:

  1. Configure the Unity player settings:

  1. Set the Android Banner by expanding the Icon section

Configure Bundle and Code Signing

Still in the player settings, expand the "Other Settings" section by clicking on it.

Enter the bundle identifier. This identifier needs to match the application configuration in the Play Console (we'll look at that in the next step).

  1. Enter a unique Bundle ID, this follows .Net namespace (or Java package) naming rules. Typically this is of the form: com.<yourcompany>.<appname>.
  2. Configure the Android TV properties
  1. At the bottom of the Player Settings, expand the Publish Settings section.

Make sure "Use Existing Keys" is checked.

Select the debug keystore that is created as part of Android Studio, or the Android SDK.

The keystore is used to cryptographically sign the application. The fingerprint of the key and the bundle id uniquely identify the application when communicating with Play Game Services.

For Mac/Linux this should be named $HOME/.android/debug.keystore

For Windows this should be named C:\Users\<USERNAME>\.android\debug.keystore

Enter the keystore password: android

Select the alias: androiddebugkey

Enter the password: android

Save and Run

  1. Save your project! Better safe than sorry :)
  2. Click Build & Run under the File menu. When prompted for a name for the APK file, enter Lollygagger.apk.
  3. You can now run the app! Look for Lollygagger in the apps list on the phone.

Congratulations! If you can play the starter project game on your Android device, you are now ready to achievements, leaderboards, and other Play Game Services that will help engage and retain your players.

Troubleshooting

Even the most simple things can have gotchas! Here are a couple and what to do about it. If you're still having problems ask for help!

Issue

Solution

Issue: Error building Player: UnityException: Bundle Identifier has not been set up correctly

Solution: Set the bundle identifier to something other than the default. This is in the player settings > Other Settings section.

Create Your Game Project

Before your users can sign in and use Play Game Services features, you need to register your game with the Developer Console. In this section, you will create a Play Game Services project in the Developer Console and link it to your Unity project.

  1. Navigate to the Google Play Developer Console and sign in. If you don't have an account yet, you will need to create one.
  2. Click Game Services and then click the Add new game button in the upper right.
  1. Using the toggle, turn Saved Games to ON. This is a very important step, you will need to have this feature enabled to complete the codelab.
  2. Finally, save your settings for this game by clicking on Save at the top of the form.

Link Your Android App

  1. Click on the Linked Apps tab in the left sidebar, then click Android. Note: If you don't see a ‘Linked Apps' button, resize your window to fullscreen or click the ‘hamburger' button on the left-hand side.
  2. Under Package Name, put the Bundle Identifier you chose when setting up your Unity project (which you can find by opening File>Build Settings and clicking Player Settings). Make sure you use the same string in both places. Then click Save and continue.
  3. On the next screen, click Authorize your app now. You may be presented with a ‘Branding Information' dialog. This is where you would control the information that appears on the sign-in screen, but we'll skip this for now. Just click ‘Continue'.
  4. On the Client ID Settings dialog, you will be asked for a ‘Signing certificate fingerprint (SHA1)'. You will need to generate this from your Android debug keystore. Use the following keytool command (keytool is part of the Oracle JDK and should already be installed on your machine in the bin directory of your JDK):
# Replace $DEBUG_KEYSTORE with the location of your android debug keystore
# which is normally $HOME/.android/debug.keystore on Mac/Linux and 
# C:\Users\<USERNAME>\.android\debug.keystore on Windows
keytool -list -v -keystore $DEBUG_KEYSTORE -alias androiddebugkey -storepass android -keypass android
  1. Copy the SHA1 fingerprint from the Certificate fingerprints section of the output. Then click Create Client. If you did everything correctly, you should see the message ‘Successfully linked your Client ID', followed by a Client ID string. You won't need this string, so don't bother writing it down.
  2. Click the Testing tab in the left sidebar. Then click Add Testers and add any Google accounts that you will be using to test this game. These should be the same as the accounts on your Android development device.

We are going to create one leaderboard for this game that tracks the number of targets hit in a single level.

  1. Navigate back to the Lollygagger project in the Google Play Developer Console and click on the Leaderboards tab.
  2. Click on Add Leaderboard and enter the following information:
  1. Click Save to finish entering leaderboards.

You should now be at the Leaderboards screen, which will display the ID of the leaderboard you just created:

Now that you have created a game project in the Developer Console, you are ready to configure your Unity project to interact with Play Game Services.

Import the Play Game Services plugin

  1. Open Unity and import the Play Game Services plugin for Unity, click Assets > Import Package > Custom Package and select current-build/GooglePlayGamesPlugin-*.unitypackage from the play-games-plugin-for-unity folder where you downloaded the Play Games Unity plugin.
  2. Make sure all the files are selected and click Import.

Setup the plugin for the Application

  1. Open the project in Unity and click Window > Google Play Games > Setup > Android Setup....
  2. Enter the full name of the C# class to store the IDs for the game resources. This class we'll reference in the code in order to perform actions like submitting scores and unlocking achievements. Use GPGSIds for the "constants class name". In a larger project we would normally use a namespace to organize the classes.
  3. Switch back to the Play Game console, and go to the list of leaderboard resources (you should still be there...). Click on Get Resources in the middle of the page, and copy the contents for Android to the clipboard.
  4. Paste the contents of the clipboard into the Resources Definition text area.
  5. You can leave the other settings and the web client Id empty.
  6. Click Setup.
  7. There will be a confirmation that it is setup successfully, click Setup.

Now that you have registered your game in the Developer Console, you are ready to start integrating Play Game Services into your app. In this section, we will add a sign-in button to your game's main menu and authenticate the user for Google Play Games.

Initialize Play Game Services

We are going to start by adding sign-in functionality to the game's main menu. First open the MainMenu.unity scene in the Unity editor. If you press Play, you should see the following:

In the Hierarchy, click on Main Camera. In the Inspector view on the right-hand side, scroll down to find the ‘Main Menu Events' script component that is attached to Main Camera. Double click the script to open it in the editor. To initialize Play Game Services, we will need to add a Start() method, like this:

using System.Collections;
using GooglePlayGames;
using GooglePlayGames.BasicApi;
using UnityEngine;

public class MainMenuEvents : MonoBehaviour {

    // ... other code here... 
    public void Start() {
       GameObject startButton = GameObject.Find("startButton");
       EventSystem.current.firstSelectedGameObject = startButton;


        //  ADD THIS CODE BETWEEN THESE COMMENTS

        // Create client configuration
        PlayGamesClientConfiguration config = new 
            PlayGamesClientConfiguration.Builder()
            .Build();

        // Enable debugging output (recommended)
        PlayGamesPlatform.DebugLogEnabled = true;
        
        // Initialize and activate the platform
        PlayGamesPlatform.InitializeInstance(config);
        PlayGamesPlatform.Activate();
       // END THE CODE TO PASTE INTO START
    }

    // ...
}

Now we need to declare these new classes we are using, so add these using statements at the top of the file with the others:

using GooglePlayGames.BasicApi;
using GooglePlayGames;

The code above will initialize the Play Games platform in your script with the saved games feature enabled.

Add the sign-in UI

Add a button to the GUI by right-clicking on Project at the bottom of your screen and selecting the Prefabs folder.

In the Prefabs folder find the signInButton prefab and drag it onto the Panel element of the hierarchy, like below. It should be a child element of Panel, so if you collapse Panel, you should not be able to see signInButton:

Now add the authStatus prefab to the hierarchy with the same click-and-drag method. Don't worry if there is no visual result, the content of authStatus is currently the empty string, we will update the content in code. When you have added authStatus you should have the following hierarchy:

Add sign-in functionality

Re-open the MainMenuEvents script (attached to the ‘Main Camera' object) where we activated the Play Games platform. Add a new SignIn function like below:

public void SignIn() {
        Debug.Log("signInButton clicked!");
}

Now we have to link this function to the OnClick event of the button:

These steps are illustrated in the clip below:

The final result should look like this:

To ensure that you linked the event correctly, click Play in the Unity Editor and click the signinButton when the main menu loads. If you added the button correctly, you should see messages appear in the debug logs.

Press play again to exit this mode.

Now we will make the button actually useful. First we need to add some boilerplate to access the signInButton and authStatus elements from your code. Open the MainMenuEvents script and add the following lines:

First add member variables to reference the Text elements on the UI, paste them in the MainMenuEvents class at the top

using UnityEngine.UI;

public class MainMenuEvents : MonoBehaviour {
    
     // ADD THESE TWO LINES
    private Text signInButtonText;
    private Text authStatus;
}

Next, add the code that assigns values to these variables by pasting these lines at the top of the Start() method:

using UnityEngine.UI;

public class MainMenuEvents : MonoBehaviour {
    
    private Text signInButtonText;
    private Text authStatus;

    public void Start() {

        // ADD THESE LINES
        // Get object instances
        signInButtonText =
            GameObject.Find("signInButton").GetComponentInChildren<Text>();
        authStatus = GameObject.Find("authStatus").GetComponent<Text>();

        // ...
    }
    // ...
}

Next, add a function called SignInCallback. This will be called when the authentication is completed. The parameter indicates if the sign-in was successful or not.

public void SignInCallback(bool success) {
        if (success) {
            Debug.Log("(Lollygagger) Signed in!");
            
            // Change sign-in button text
            signInButtonText.text = "Sign out";
            
            // Show the user's name
            authStatus.text = "Signed in as: " + Social.localUser.userName;
        } else {
            Debug.Log("(Lollygagger) Sign-in failed...");
            
            // Show failure message
            signInButtonText.text = "Sign in";
            authStatus.text = "Sign-in failed";
        }
    }

Now call authenticate when the Sign in button is clicked. Modify the SignIn function so that it matches the code below:

    public void SignIn() {
        if (!PlayGamesPlatform.Instance.localUser.authenticated) {
            // Sign in with Play Game Services, showing the consent dialog
            // by setting the second parameter to isSilent=false.
            PlayGamesPlatform.Instance.Authenticate(SignInCallback, false);
        } else {
            // Sign out of play games
            PlayGamesPlatform.Instance.SignOut();
            
            // Reset UI
            signInButtonText.text = "Sign In";
            authStatus.text = "";
        }
    }

The code above implements both sign-in and sign-out, depending on the user's state. However the code above only triggers sign-in when the user presses the sign-in button. We'd like to have returning users silently signed in when they open the app. Add the following line to Start() to attempt silent sign-in when the app is opened:

public void Start() {
        // ...

        // PASTE THESE LINES AT THE END OF Start()
        // Try silent sign-in (second parameter is isSilent)
        PlayGamesPlatform.Instance.Authenticate(SignInCallback, true);
}

Test signing in

Now let's test the sign-in process. This step requires you to run the game on your Android device.

  1. Save the project File > Save Project.
  2. File >Build and Run the app.
  3. When the app starts, click the ‘Sign in' button. Once you have accepted all of the consent screens, you should see the button text change to "Sign Out" and your name appear below the button.

Now that your users are signed in with access to Play Game Services, it is time to start adding awesome features to your game. In this section, we will add achievements to the game for hitting enemies and a UI for users to view their progress.

Create Achievements in the Developer Console

We are going to create two achievements for this game. One that rewards the user for shooting her first target, and another that rewards the user for shooting 10 total targets. The latter will be an "incremental" achievement, which means the user needs to take multiple steps to unlock it.

  1. Navigate back to the Lollygagger project in the Google Play Developer Console and click on the Achievements tab.
  2. Click on Add Achievement and enter the following information:
  1. Next click Save and add another achievement, then enter the following information for the second achievement:
  1. Click Save to finish entering achievements.

You should now be at the Achievements screen, which will display the IDs of the two achievements you just created:

Click on Get resources and copy the android resource data to the clipboard. Just like we did when initially setting up the game.

Paste this in the resource data in the setup dialog (Window > Google Play Games > Setup > Android Setup...).

Click Setup. This will regenerate the GPGSIds file which now includes the Ids of the Achievements.

View Achievements

Now that your game has achievements in the Developer Console, your players may want to be able to check their progress on all of the achievements in your game and see which achievements they have not yet completed. Let's add a button to the main menu that brings up the achievements list UI.

First, let's add the code to show achievements. Open the MainMenuEvents script and add the following function:

public void ShowAchievements() {
        if (PlayGamesPlatform.Instance.localUser.authenticated) {
            PlayGamesPlatform.Instance.ShowAchievementsUI();
        }
        else {
          Debug.Log("Cannot show Achievements, not logged in");
        }
    }

Now create an Achievements button in the MainMenu scene by adding the achButton prefab to the hierarchy in the same way that you added the signInButton prefab earlier:

Finally, link the On Click event of the button to the ShowAchievements() method in the MainMenuEvents script, using the same method as you used to link the Sign In button to the SignIn() method:

Add a variable to the script that points to the achievement button, and initialize it in Start():

public class MainMenuEvents : MonoBehaviour {
    
    // ...
    private GameObject achButton;

    public void Start() {
        // ...
        // Get object instances
        // ...
        achButton = GameObject.Find("achButton");

        // Try silent sign-in (second parameter is isSilent)
        // ...
    }
    // ... 
 }

Finally, add a method called Update() which is called by Unity on every frame. In here we'll show or hide the achievement button depending on the player's authenticated state:

    public void Update() {
         achButton.SetActive(Social.localUser.authenticated);
    }

Build and run

Now save your progress and ‘Build and Run' the game on your Android device.

Test achievements

When you sign in at the main menu you your UI should look like this:

And after clicking ‘Achievements', you should see a screen like this:

Now your users can check the status of their achievements at any time! If you add more achievements via the Developer Console, they will show up on this screen.

Trigger Achievements

Now that you can check the progress of your achievements, we need to add code to trigger them when the user hits targets in the game.

Open the DroidController.cs script from the scripts folder and modify it to match the code below.

Remember to also add the using statement at the top since we're referencing a new class:

using GooglePlayGames;
public class DroidController : MonoBehaviour {

    void OnCollisionEnter (Collision col)
    {
        // if we are alive and hit by a projectile, then die.
        // and turn off the constraint to stand up so we bounce around.
        if (!isDead && col.gameObject.tag.Equals ("Projectile")) {
             Debug.Log("(Lollygagger) Droid hit!");
            Rigidbody rb = GetComponent<Rigidbody> ();
            rb.constraints = RigidbodyConstraints.None;
            rb.useGravity = true;
            Die ();
        
            //START CODE TO COPY FOR ACHIEVEMENT TRACKING
            
            // Only do achievements if the user is signed in
            if (Social.localUser.authenticated) {
               // Unlock the "welcome" achievement, it is OK to
               // unlock multiple times, only the first time matters.
                PlayGamesPlatform.Instance.ReportProgress(
                    GPGSIds.achievement_welcome_to_lollygagger,
                    100.0f, (bool success) => {
                           Debug.Log("(Lollygagger) Welcome Unlock: " +
                                 success);
                });

                // Increment the "sharpshooter" achievement
               PlayGamesPlatform.Instance.IncrementAchievement(
                      GPGSIds.achievement_sharpshooter,
                      1,
                      (bool success) => {
                        Debug.Log("(Lollygagger) Sharpshooter Increment: " +
                           success);
               });
            } // end of isAuthenticated
          
            // END CODE TO COPY



       } // end of is not dead and a projectile hit
   } // end of OnCollisionEnter
} // end of class

Build and run

Now save your progress and ‘Build and Run' the game on your Android device.

Test unlocking achievements

At the main menu, first sign in then begin a game. When you hit your first (and later, 10th) target you will see an ‘Achievement Unlocked' dialog appear at the top of the screen.

Congratulations!, you just got your first Play Game Services achievement!

Once you have finished adding achievements to your game, leaderboards are the natural next step. In this section, we will add a global high score leaderboard to the game and a UI for users to view their scores.

We added the leaderboard to the Play Console already, so we don't need to do any additional configuration, and we can jump right into the coding!

View Leaderboards

Let's add a button to the main menu that brings up the leaderboards list UI, just like the Achievements button we added earlier.

First, let's add the code to bring up the leaderboards UI. Open the MainMenuEvents script and add the following function:

    public void ShowLeaderboards() {
        if (PlayGamesPlatform.Instance.localUser.authenticated) {
            PlayGamesPlatform.Instance.ShowLeaderboardUI();
        }
        else {
          Debug.Log("Cannot show leaderboard: not authenticated");
        }
    }

Now create a button in the main menu scene by adding the ldrButton prefab to the hierarchy in the same way that you added the achButton and signInButton prefabs:

Finally, link the On Click event of the button to the ShowLeaderboards() method in the MainMenuEvents script, using the same method as you used to link the Achievements button to the ShowAchievements() method:

The last step is to configure the button not to display unless the user is signed in with Play Game Services. Modify MainMenuEvents to add the following code, mimicking the code used for the Achievements button:

  1. Declare the member variable ldrButton
  2. Initialize ldrButton by calling GameObject.Find()
  3. Initially set ldrButton to inactive
  4. Update the active state of ldrButton in Update()
public class MainMenuEvents : MonoBehaviour {
    
    // ...
    private GameObject ldrButton;

    public void Start() {
        // ...
        achButton = GameObject.Find("achButton");
        ldrButton = GameObject.Find ("ldrButton");

        // ...
        // Try silent sign-in (second parameter is isSilent)
        // This should always be the last line of Start()
        PlayGamesPlatform.Instance.Authenticate(SignInCallback, true);
    }

    public void Update() {

        // ... 

        // Hide or show the achievement and leaderboards buttons
        achButton.SetActive(Social.localUser.authenticated);
        ldrButton.SetActive(Social.localUser.authenticated);
    }

}

Build and run

Now save your progress and ‘Build and Run' the game on your Android device.

Testing Leaderboards

When you sign in at the main menu you your UI should look like this:

And after clicking ‘Leaderboards' and then clicking on the ‘Targets Hit in One Level', you should see a screen like this:

Now your users can check the status of the leaderboards at any time! If you add more leaderboards via the Developer Console, they will show up on this screen.

Update Leaderboard Scores

We will update the user's score at the end of each level, rather than with each enemy killed in order to cut down on the number of requests we have to make to the leaderboards API. First, let's add some accounting to keep track of targets hit. Open GameManager from within the scripts folder and add the following code, modifying RestartLevel and NextLevel and adding IncrementHits:

public class GameManager : MonoBehaviour {

    // ...  existing code

    // number of hits scored.
    private int mHits;
    
    public GameManager() {
        // ... existing code 
        // start with no hits.
        mHits = 0;
    }

    // ... existing code
    public void RestartLevel() {
        // ... existing code
        // restarting the level, reset the hits
        mHits = 0;
    }

    public void NextLevel () {
        // ... existing code
        // moving to the next level, reset the hits
        mHits = 0;
    }

    // NEW function. Add this.
    public void IncrementHits() {
        mHits = mHits + 1;
    }
}

Then open DroidController and add a call to IncrementHits in the collision logic:

void OnCollisionEnter (Collision col)
    {
        // ...
        if (!isDead && col.gameObject.tag.Equals ("Projectile")) {
            GameManager.Instance.IncrementHits();
            // ...
        }
    }

Now that we are keeping track of the user's hits, we will add the code to update the leaderboard. Open GameManager, adding a call to ReportScore() in ShowEndMenu(). The leaderboard ID is declated in GPGSIds - which is the class generated by running the Setup menu item. Make sure to check the authenticated state before reporting the score.

using GooglePlayGames;

public class GameManager : MonoBehaviour {

    // ...

    public void ShowEndMenu() {
        if (EventSystem.current.currentSelectedGameObject == null)
        {
            EventSystem.current.SetSelectedGameObject(
                restartMenuButton.gameObject);
        }

        if (mEndMenu.activeSelf)
        {
            return;
        }
// ADD THIS ELSE CONDITION TO REPORT THE SCORE
        else
        {
            // Submit leaderboard scores, if authenticated
            if (PlayGamesPlatform.Instance.localUser.authenticated)
            {
                // Note: make sure to add 'using GooglePlayGames'
                PlayGamesPlatform.Instance.ReportScore(mHits,
                    GPGSIds.leaderboard_targets_hit_in_one_level,
                    (bool success) =>
                    {
                        Debug.Log("(Lollygagger) Leaderboard update success: " + success);
                    });
            }

        }
// END LEADERBOARD CODE

        mEndMenu.SetActive(true);
    }
}

Build and run

Now save your progress and ‘Build and Run' the game on your Android device.

Test the leaderboard submission

Run the game on your Android device and play a level all the way through (make sure you hit a few targets!). This will submit a score to the leaderboard.

Go back to the main menu and hit the Leaderboards button to check your score and make sure the submission was successful!

Pat yourself on the back! You just added sign-in, achievements, and leaderboards to your Unity game. That's only the beginning of the cool features Play Game Services has to offer. This section will show you how to use the saved games API to persist game data to the cloud and share it across devices.

Enable Saved Games

Enabling saved games involves two steps: turning the feature on in the Developer Console, and enabling the feature when activating the platform in your code.

First, navigate back to the Lollygagger project in the Google Play Developer Console. In the Game Details tab, confirm that turn Saved Games is turned to ON and matches the image below (you should have done this earlier). Then click the blue Save button at the top of the screen.

Enable Saved Games API

Next, open the MainMenuEvents script and edit the Start method to enable saved games on your PlayGamesClientConfiguration:

public void Start() {
        // ...

        // Create client configuration with saved games enabled
        PlayGamesClientConfiguration config = new
            PlayGamesClientConfiguration.Builder()
            .EnableSavedGames()
            .Build();

        // ...
    }

Build and run

Now save your progress and ‘Build and Run' the game on your Android device.

Test the app

Try to sign in. If you get the ‘Sign In Failed' message, this is due to caching the game configuration in Google Play Services. Re-open your game and attempt to sign in again, if it succeeds then you have successfully enabled saved games.

Save Your Score

Writing data to the saved games API is a two-step process. The first is opening the saved games ‘snapshot' where you would like to save the data. The second is writing to the file, and committing the update. Open GameManager and add the following code which adds the ReadSavedGame and WriteSavedGame functions:

using System;
using GooglePlayGames.BasicApi;
using GooglePlayGames.BasicApi.SavedGame;

public class GameManager : MonoBehaviour {

    // ...

    public void ReadSavedGame(string filename, 
                             Action<SavedGameRequestStatus, ISavedGameMetadata> callback) {
        
        ISavedGameClient savedGameClient = PlayGamesPlatform.Instance.SavedGame;
        savedGameClient.OpenWithAutomaticConflictResolution(
            filename, 
            DataSource.ReadCacheOrNetwork, 
            ConflictResolutionStrategy.UseLongestPlaytime, 
            callback);
    }
    
    public void WriteSavedGame(ISavedGameMetadata game, byte[] savedData, 
                               Action<SavedGameRequestStatus, ISavedGameMetadata> callback) {
        
        SavedGameMetadataUpdate.Builder builder = new SavedGameMetadataUpdate.Builder()
            .WithUpdatedPlayedTime(TimeSpan.FromMinutes(game.TotalTimePlayed.Minutes + 1))
            .WithUpdatedDescription("Saved at: " + System.DateTime.Now);
        
        // You can add an image to saved game data (such as as screenshot)
        // byte[] pngData = <PNG AS BYTES>;
        // builder = builder.WithUpdatedPngCoverImage(pngData);
        
        SavedGameMetadataUpdate updatedMetadata = builder.Build();
        
        ISavedGameClient savedGameClient = PlayGamesPlatform.Instance.SavedGame;
        savedGameClient.CommitUpdate(game, updatedMetadata, savedData, callback);
    }
}

Now let's modify ShowEndMenu to update the total number of targets hit at the end of each level by calling WriteUpdatedScore():

public void ShowEndMenu() {
        // ...
        // Only do these actions once (ShowEndMenu called on a loop)
        if (mEndMenu.activeSelf) {
            return;
        }
        else
        {
            if (PlayGamesPlatform.Instance.localUser.authenticated) {
                // Note: make sure to add a 'using GooglePlayGames'
                //  statement to this script
                PlayGamesPlatform.Instance.ReportScore(mHits,
                    TARGETS_HIT_ID, (bool success) => {
                    Debug.Log ("(Lollygagger) Leaderboard update success: " + success);
                });

// NEW: add this function call.
                // Read saved game data and update
                WriteUpdatedScore();
// END new code to add.
            }
        }

        mEndMenu.SetActive(true);
    }

Finally, we need to implement WriteUpdatedScore:

public void WriteUpdatedScore() {
        // Local variable
        ISavedGameMetadata currentGame = null;

        // CALLBACK: Handle the result of a write
        Action<SavedGameRequestStatus, ISavedGameMetadata> writeCallback = 
        (SavedGameRequestStatus status, ISavedGameMetadata game) => {
            Debug.Log("(Lollygagger) Saved Game Write: " + status.ToString());
        };

        // CALLBACK: Handle the result of a binary read
        Action<SavedGameRequestStatus, byte[]> readBinaryCallback = 
        (SavedGameRequestStatus status, byte[] data) => {
            Debug.Log("(Lollygagger) Saved Game Binary Read: " + status.ToString());
            if (status == SavedGameRequestStatus.Success) {
                // Read score from the Saved Game
                int score = 0;
                try {
                    string scoreString = System.Text.Encoding.UTF8.GetString(data);
                    score = Convert.ToInt32(scoreString);
                } catch (Exception e) {
                    Debug.Log("(Lollygagger) Saved Game Write: convert exception");
                }
                
                // Increment score, convert to byte[]
                int newScore = score + mHits;
                string newScoreString = Convert.ToString(newScore);
                byte[] newData = System.Text.Encoding.UTF8.GetBytes(newScoreString);
                
                // Write new data
                Debug.Log("(Lollygagger) Old Score: " + score.ToString());
                Debug.Log("(Lollygagger) mHits: " + mHits.ToString());
                Debug.Log("(Lollygagger) New Score: " + newScore.ToString());
                WriteSavedGame(currentGame, newData, writeCallback);
            }
        };

        // CALLBACK: Handle the result of a read, which should return metadata
        Action<SavedGameRequestStatus, ISavedGameMetadata> readCallback = 
        (SavedGameRequestStatus status, ISavedGameMetadata game) => {
            Debug.Log("(Lollygagger) Saved Game Read: " + status.ToString());
            if (status == SavedGameRequestStatus.Success) {
                // Read the binary game data
                currentGame = game;
                PlayGamesPlatform.Instance.SavedGame.ReadBinaryData(game, 
                                                    readBinaryCallback);
            }
        };

        // Read the current data and kick off the callback chain
        Debug.Log("(Lollygagger) Saved Game: Reading");
        ReadSavedGame("file_total_hits", readCallback);
    }

Now let's see if it all worked. Run the game on your device, and on your machine run:

adb logcat | grep "Lollygagger"

Play a level and hit some targets. At the end of the level, you should see some Logcat output like this:

I/Unity   ( 5911): (Lollygagger) Saved Game: Reading
I/Unity   ( 5911): (Lollygagger) Leaderboard update success: True
I/Unity   ( 5911): (Lollygagger) Saved Game Read: Success
I/Unity   ( 5911): (Lollygagger) Saved Game Binary Read: Success
I/Unity   ( 5911): (Lollygagger) Old Score: 0
I/Unity   ( 5911): (Lollygagger) mHits: 7
I/Unity   ( 5911): (Lollygagger) New Score: 7
I/Unity   ( 5911): (Lollygagger) Saved Game Write: Success

If you play again, you will see that the score increases over time (rather than re-setting every level). You may be thinking "big deal, all I did was persist an integer and I had to write a ton of code!". So far, you would be right. But now let's demonstrate the real power of Saved Games. Uninstall the game from your Android device, then re-install the game from Unity. After you sign in and play through a level, your scores will be intact! This will work across devices as well, as long as you sign in with the same Google account.

Exercise: Post High Scores

If you made it all the way here, you are well on your way to becoming a master of Play Game Services. This section will be an exercise for you to see what you have learned:

Create a new leaderboard called "All Time Hits", and update it every time you write a new score to the saved games API.

If you complete this task, you will be able to display all time hit counts for all of your users, even combining hits from multiple devices! Pretty cool, right?

Now that you've finished the code lab, there are a few more things you can do before you are ready to publish your awesome game. This section will describe what is left and point you to some resources where you can continue learning.

More Features

There are many more great features of Play Game Services that were not covered by this codelab such as: