This codelab shows you how to send messages between two scenes in Unity with one device behaving as a game host and the other behaving as a controller. After you have completed the codelab, you will have a basic understanding of using nearby connections for virtual controller input.

What you’ll learn

What you’ll need

Game Summary

For this codelab, we'll be using a very simple "game" that consists of:

How will you use use this tutorial?

Read it through onlyRead it and complete the exercises

How would you rate your experience with building Unity apps?

NoviceIntermediateProficient

How would you rate your experience with building Android apps?

NoviceIntermediateProficient

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

NoviceIntermediateProficient

First you need the Play Game Services plugin for Unity.

You can either download all the sample code to your computer...

Download Zip

...or clone the GitHub repository from the command line.

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

Next you need the unity asset package containing the sample game.  You can download this here:

Download

First, let's build and run the game.  From there, we'll add Play Game services.

Start Unity

Start Unity!  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:

Import the Play Games Unity Plugin into your project:

Configure the Android player

Configure Unity to build an Android application:

Add the game scenes to the build:

Configure the Unity player settings:

Configure Bundle and Code Signing

Still in the Player Settings, expand the Other Settings section by clicking on it.

Enter a unique Bundle ID, this follows .Net namespace (or Java package) naming rules.  Typically this is of the form: com.<yourcompany>.<appname>:


At the bottom of the Player Settings, expand the Publish Settings section.

Note:
If you can't find the key debug.keystore, or if you want to create a new one, change directory to the .android directory on your system  (as explained above) and run the following command:
keytool -genkeypair -alias androiddebugkey -keypass android -keystore debug.keystore -storepass android -dname "CN=Android Debug,O=Android,C=US" -validity 9999

Configure Android TV properties

In the Other Settings panel set:

Enable Nearby Connections in the Play Games Services plugin

Click Window > Google Play Games > Setup > Nearby Connections setup:

In the dialog that is presented, enter your Android package name. This will be used as the service identifier to discover and advertise nearby connections.


After you click Setup, the Google Play Games plugin will configure nearby connections for your project.

Save and Run

Congratulations! If you can play the starter project game on your Android devices, you are now ready make the controller more sophisticated.

When the game starts, the Start() method within the MainMenu scene activates and initializes Nearby Connections and sets up the Nearby connections message listener:

    void Start () {
        PlayGamesPlatform.InitializeNearby((client) =>
        {
            Debug.Log("Nearby connections initialized");
        });

        if (listener == null) {
            listener = new MenuMessageListener();
            listener.container = this;
        }
        // ...
    }

When the Advertise button is pressed, the device presenting the main menu begins advertising that it can be connected to:

    // Advertises to run the game as the controlled screen.
    public void Advertise()
    {
        Debug.Log ("Advertising: " + PlayGamesPlatform.Nearby.GetServiceId ());

        List<string> appIdentifiers = new List<string>();
        appIdentifiers.Add(PlayGamesPlatform.Nearby.GetServiceId());
        PlayGamesPlatform.Nearby.StartAdvertising(
            "Awesome Game Host",  // User-friendly name
            appIdentifiers,  // App bundle Id for this game
            TimeSpan.FromSeconds(0),// 0 = advertise forever
            (AdvertisingResult result) => {
                Debug.Log("OnAdvertisingResult: " + result);
            },
            (ConnectionRequest request) => {
                Debug.Log("Received connection request: " +
                        request.RemoteEndpoint.DeviceId + " " +
                        request.RemoteEndpoint.EndpointId + " " +
                        request.RemoteEndpoint.Name);

                // Always accept requests...
                byte[] responseData = {0x00};
                PlayGamesPlatform.Nearby.AcceptConnectionRequest(
                    request.RemoteEndpoint.EndpointId,
                    responseData,
                   (IMessageListener)listener);
            }
        );
    }

Now, whenever a connect request is made, the host that is advertising will accept the connection request and will then begin receiving nearby connections messages from clients.

When the Controller scene starts, that device will stop advertising that it can be connected to and it will set itself up to connect to an advertising server:

    void Start () {
        if (discoListener == null) 
        {
            discoListener = new ControllerDiscoveryListener();
        }
        if (messListener == null) 
        {
            messListener = new ControllerMessageListener();
        }
        if (statusText == null) {
            // TODO: there is a better way
            var components = FindObjectsOfType<Text> ();
            
            for (int i=0; i<components.Length; i++) {
                if (components [i].text.StartsWith ("Status:")) {
                    statusText = components [i];
                }
            }
        }

        PlayGamesPlatform.Nearby.StopAdvertising();
        Debug.Log ("Stopping advertising... click discover to go");

        PlayGamesPlatform.InitializeNearby((client) => {
            Debug.Log("Nearby connections initialized");
        });
    }

Next, when the user clicks the Discover button, the app running the Controller scene will then discover available nearby connection services:

    public void StartDiscovery() 
    {
        Debug.Log ("Starting discovery...");
        Debug.Log (PlayGamesPlatform.Nearby.GetServiceId());

        PlayGamesPlatform.Nearby.StartDiscovery(
            PlayGamesPlatform.Nearby.GetServiceId(),
            TimeSpan.FromSeconds(0),
            discoListener);
    }

After services have been discovered, clicking the Join button connects the app running the Controller scene to the app running the Main menu scene:

    public void JoinAsController()
    { 
        byte[] data = {0x00};
        Debug.Log ("Joining server: " + selectedEndpoint);

        PlayGamesPlatform.Nearby.SendConnectionRequest(
            "Local Game player",  // the user-friendly name
            selectedEndpoint,  // the discovered endpoint  
            data, // byte[] of data
            (response) => {
            Debug.Log("response: " +
                      response.ResponseStatus);
            },
            (IMessageListener)messListener
            );
    }

Finally, messages are transmitted to the service using a helper function...

    private void sendMessage(byte[] payload) {
        List<string> endpoints = new List<string> ();
        endpoints.Add (selectedEndpoint);
        PlayGamesPlatform.Nearby.SendUnreliable(endpoints, payload);
    }

… and are presented as they are received on the MainMenu scene in its IMessageListener:

    internal class MenuMessageListener: IMessageListener
    {
        public MainMenu container;

        public void OnMessageReceived(string remoteEndpointId, byte[] data, bool isReliableMessage)
        {
            Debug.Log ("Received message :\n" +
                "Endpoint : " + remoteEndpointId + "\n" + 
                "Data size : " + data.Length + "\n" +
                "isReliable : " + isReliableMessage);

            switch (data [0]) {
                case 0x00:
                    container.UpdateCommandMessage("Up");
                    break;
                case 0x01:
                    container.UpdateCommandMessage("Down");
                    break;
                case 0x02:
                    container.UpdateCommandMessage("Fire");
                    break;
                case 0x03:
                    container.UpdateCommandMessage("Left");
                    break;
                case 0x04:
                    container.UpdateCommandMessage("Right");
                    break;
                case 0x05:
                    container.UpdateCommandMessage("Start");
                    break;
                default:
                    break;
            }
        }

        // ...
    }

Now that you’ve finished the code lab, there are a few more things you can do before you are ready to publish your game incorporating virtual controllers powered by nearby connections.  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: