Creating a Cross-Platform Multi-Player Game in Unity — Part 2

In the second part of this tutorial, you’ll write the code to have users connect with each other and then get a race started! By Todd Kerpelman.

Leave a rating/review
Save for later
Share
You are currently viewing page 3 of 6 of this article. Click here to view the first page.

Running on iOS

Here’s how to get your game running on two iOS devices:

  1. Stop the app in XCode.
  2. Plug your second device in to your computer.
  3. Run the app again in Xcode on your second device. You don’t need to re-export from Unity; just hit Run in Xcode.
  4. Once your game is running, sign in to your second iOS device with a different Google account than the one you are using on your first device, and one that has been listed as a Tester account under the Game Services section of the Play Developer console.
  5. Once you’ve signed in, start a multiplayer game; the debug messages will tell you that you’ve joined the multiplayer lobby for your game.
  6. Go back to your first device, start up your Circuit Racer app and request to join a multiplayer game.

After a few moments, you’ll see that your two accounts are connected with some resultant console output like the following:

DEBUG: New participants connected: p_CKq_go_Vq4CMchAB,p_CJXhuOmO0se-BRAB
DEBUG: Fully connected! Transitioning to active state.
DEBUG: Entering state: ActiveState
DEBUG: Entering internal callback for RealTimeEventListenerHelper#InternalOnRoomStatusChangedCallback

We are connected to the room! I would probably start our game now.

As far as the Google Play library is concerned, you’re in a real multiplayer game — you just can’t see your opponent’s car or actually interact with the game in any way. Boo. In fact, if you click on the Multiplayer button again, it won’t work because the service thinks you’re still in a game. You’ll need to kill both apps and restart them again to join another multiplayer game.

The important thing is that the Google Play games service has matched up the two accounts on your devices and, in theory, you could start sending messages between the devices.

Since there aren’t any magazines lying around the multiplayer waiting room of your game to read while you wait for another player to join, It might be nice to add a little UI to your game that shows the progress of connecting to the multiplayer game.

Adding a Simple Waiting Room UI

The Google Play Games library you’ve incorporated into your Xcode build has some built-in support for a creating multiplayer waiting room UI. For some reason though, the Unity plug-in doesn’t take advantage of it. I suspect that most real game developers would prefer to build their own waiting room interface with their own themed graphics, which is the approach you’ll follow in this tutorial.

Open MainMenuScript.cs and add the following public variable.

public GUISkin guiSkin;

This variable holds the image that will be the background of your dialog box. You will set this image using the Unity inspector.

Now add the following two instance variables:

private bool _showLobbyDialog;
private string _lobbyMessage;

These variables track whether you should display the lobby dialog box and what message it should contain.

Now, add the following setter method to update _lobbyMessage:

public void SetLobbyStatusMessage(string message) {
    _lobbyMessage = message;
}

Next, add the method below:

public void HideLobby() {
    _lobbyMessage = "";
    _showLobbyDialog = false;
}

The above method simply instructs the Main Menu to stop showing the lobby interface.

Next, open MultiplayerController.cs and add the following public variable:

public MainMenuScript mainMenuScript;

The mainMenuScript variable holds a reference to your mainMenuScript. By doing so, you can call the public methods that you just added.

Replace ShowMPStatus() with the following::

private void ShowMPStatus(string message) {
    Debug.Log(message);
    if (mainMenuScript != null) {
        mainMenuScript.SetLobbyStatusMessage(message);
    }
}

With the changes above, instead of just printing debug messages to the console, you’re telling your mainMenuScript that it should be showing message as a lobby status message.

Now you need to display these messages. Go back to MainMenuScript.cs and replace the following lines in OnGUI():

} else if (i == 1) {
    RetainedUserPicksScript.Instance.multiplayerGame = true;
    MultiplayerController.Instance.SignInAndStartMPGame();
}

…with the following code:

} else if (i == 1) {
    RetainedUserPicksScript.Instance.multiplayerGame = true;
    _lobbyMessage = "Starting a multi-player game...";
    _showLobbyDialog = true;
    MultiplayerController.Instance.mainMenuScript = this;
    MultiplayerController.Instance.SignInAndStartMPGame();
}

Here you set _showLobbyDialog to true, which indicates you’re ready to show a dialog box; you also tell MultiplayerController that this is the class to which it should send lobby status messages.

Next, at the top of OnGUI() wrap that entire for loop inside another if block as follows:

if (!_showLobbyDialog) {
    for (int i = 0; i < 2; i++) {

        // Lots of GUI code goes here,,,

    }
}

This hides the menu buttons [when the lobby dialog box appears. Even though the dialog box will cover the buttons, they'll still remain clickable — which could lead to some unexpected behavior from your game! :]

Add the following code to the end of OnGUI():

if (_showLobbyDialog) {
    GUI.skin = guiSkin;
    GUI.Box(new Rect(Screen.width * 0.25f, Screen.height * 0.4f, Screen.width * 0.5f, Screen.height * 0.5f), _lobbyMessage);
}

This displays the dialog box on the screen.

Head back to Unity and go to the MainMenu scene. Select MainMenuGameObject, click the little target next to Gui Skin, and then select GameGuiSkin from the Assets dialog:

Setting your gui skin

This sets the skin of your game using the public guiSkin variable you declared at the start of this section.

Build and run your game; this time you should see your online connection status as a nice series of dialog boxes:

Lobby Dialog

That's a little more comforting to a player who can't view the Xcode console log! :]

However, you're still stuck with this dialog box that never goes away. As nice as the dialog is, playing an actual game would be a lot more fun.

Adhering to the Delegate Pattern

Some of you may have noticed that the code above violates the principle of encapsulation. MultiplayerController directly communicates with MainMenuScript, which means the two classes are highly coupled. If you wrote some really awesome code in MultiplayerController and wanted to reuse it in your new exciting boat racing game "Circuit Sailor", you'd be in a hard spot.

In much the same way that you use the delegate pattern in Objective-C to logically separate one class from another, you can create an interface in C# to do the same thing.

Head back to Unity and open the Scripts folder in your assets panel. Right-click the panel and select Create\C# Script. Name the script MPInterfaces, then open up the file for editing.

Replace the entire file (yep, the whole thing) with the following code:

public interface MPLobbyListener {
    void SetLobbyStatusMessage(string message);
    void HideLobby();
}

Here you declare an interface, which as you'll recall is sort of like a contract. Any class that implements this interface promises that it will add any requisite methods.

Go back to MultiplayerController and modify the following variable:

public MainMenuScript mainMenuScript;

...to the following:

public MPLobbyListener lobbyListener;

You're replacing mainMenuScript, which was tied directly to a MainMenuScript class, with an instance of lobbyListener instead. You're saying you don't care what kind of class this is, so long as it implements the MPLobbyListener interface.

Modify ShowMPStatus so that mainMenuScript variable is replaced by a lobbyListener variable:

private void ShowMPStatus(string message) {
    Debug.Log(message);
    if (lobbyListener != null) {
        lobbyListener.SetLobbyStatusMessage(message);
    }   
}

The following code decouples the current object from the MainMenuScript since the code now works upon an interface as opposed to an instance.

Now open MainMenuScript.cs and modify the class declaration at the beginning of the file as follows:

public class MainMenuScript : MonoBehaviour, MPLobbyListener {

This change indicates that the class implements the MPLobbyListener interface.

Now find the following line in OnGUI():

MultiplayerController.Instance.mainMenuScript = this;

..and modify it as follows:

MultiplayerController.Instance.lobbyListener = this;

This last bit of code removes the last reference of the MainMenuScript and uses the new interface instead.

Since MainMenuScript already implements the required methods, it's fulfilled its contract with the MPLobbyListener interface.

If you were to build and run your app at this point, it would still look and run the same, But you could feel confident developing it further knowing that you have some nicely encapsulated code under the hood! :]

RefactoringLikeABoss

Todd Kerpelman

Contributors

Todd Kerpelman

Author

Over 300 content creators. Join our team.