In spite of the challenge of creating multiplayer games, they are totally worth it. No AI, no matter how clever, will have the same ingenuity as a human opponent, nor will trash talking an AI be as fun as trash talking your friends. And I haven’t heard of any player starting a relationship with an AI that they met online…movies starring Joaquin Phoenix notwithstanding. :]
In this tutorial, you’ll create a networked multiplayer game that works across different brands of devices. When you show off an iOS and Android game playing together, your co-workers are going to think you’re some kind of crazy wizard!
In the first part of this tutorial series, you will learn the following:
- How to configure and install Google Play Games Services
- How to setup an iOS project to work with Google Play Games Services.
- How to sign in a user.
- How to sign out a user.
You should have the following prerequisites before you move ahead with this tutorial:
- A basic understanding of Unity and C#. All of your coding is going to be in C# — no Objective-C, Swift, or Java here!
- Some experience running a Unity game on an actual device.
- At least two devices to develop on: either iOS, Android, or one of each. Testing a multiplayer game with just one device tends to be rather difficult. :]
- A Google Play Developer account, which you can sign up for at https://play.google.com/apps/publish/. Even if you’re going to test this on iOS only, you’ll still need one of these accounts. Obtaining a Play Developer account will set you back $25 — but you’ll consider it worthwhile once you experience your game in action!
If you are just starting out with Unity, it would be well worth your time to work through Christopher LaPollo’s excellent Unity 2D tutorial series on this site before you tackle an advanced-level Unity tutorial like this one.
Getting Started
It’s time to get familiar with the game you’re going to turn into a multiplayer showcase of awesomeness. Download the starter project for Circuit Racer, The Unity Version here, then unzip the file to extract the project.
In Unity, select File\Open Project. Select Open Other… from the dialog box that appears, and then select the Circuit Racer folder you just unzipped:
Start by loading up the Main Menu scene. From the Assets panel, select MenuStuff and then double-click MainMenu to open up the main menu.
Hit the Play button to try out the game within Unity; select the single player experience, and watch as your car performs death-defying feats as it circles around the track before time runs out!
Oh, by the way — watch out for those crates. :]
Running on a Real Device
It’s time to try this on an actual device. Since you’re reading this on RayWenderlich.com, I’ll assume you’ll want to run this on your favorite iOS device. Readers on RoyWenderlich.com, the Android tutorial site run by Ray’s evil twin, probably have a different experience in mind:
You’ll first need to change the Bundle Identifier of your project to something a little less generic. Go to File\Build Settings, then select iOS. The iOS platform should already be selected as your target platform, but if it isn’t, click the Switch Platform button with iOS selected.
Click Player Settings and the PlayerSettings should appear in your Inspector panel. In the Other Settings area, look for the Bundle Identifier and change it from com.example.CircuitRacerUnity
to something that’s unique to you and appropriate for your provisioning profile, like com.<your company name>.<your user name>.CircuitRacerUnity
:
Now connect your physical device to your Mac. If you haven’t run an Xcode project on a device before, then read up on what’s required in this tutorial first.
Head back to the Build Settings dialog box, which should still be open, and select Build and Run. Unity will ask you to save the generated Xcode project; give it a fun and original name such as CircuitRaceriOS.
Once Unity has finished exporting the project, it will open up the newly created project in Xcode. Xcode will take a few moments to process the file, and it will then run the game on your actual device! However, sometimes Xcode forgets to run the project for you. If this happens, just click the Run button to remind Xcode why it opened in the first place. :]
In addition to the on-screen gamepad support, the game also supports tilt controls — something you probably wouldn’t have noticed when you were playing the game within Unity.
Understanding Circuit Racer
The Circuit Racer game in this tutorial is a mashup of two different tutorials:
- The Circuit Racer SpriteKit game, as presented in the book Games for iOS by Tutorials.
- The Unity 2D intro tutorials on this site by Christopher LaPollo.
This tutorial won’t go into too much detail about how the controls work, how the game knows you’ve completed a circuit, or how the car turns, since that’s been covered by the two aforementioned tutorials and it’s not directly relevant to your goal of turning this into a multiplayer game. Still, feel free to browse through the code, in particular the GameController
, CarController
, and AnalogControl
classes, which will give you a general idea of how things work.
As long as you’re trying out the game on your device, you might as well see what the multiplayer option has to offer. Head back to the main menu and click on the Multiplayer button:
Well, that was underwhelming, to say the least. But the good news is that you’re going to fix it all up!
Introducing Google Play Game Services
Although negotiating network connections between devices behind various firewalls might sound like fun to some, most developers choose to use third-party libraries to handle many of the “complicated but not fun” tasks of initiating multiplayer games. There are lots of choices for third-party frameworks out there, but this tutorial uses Google Play game services for the following reasons:
- It’s free.
- There’s a plug-in available for Unity.
- The plug-in is also free.
Yes, skeptical cereal-eating man, it’s true. Game Center does provide some really good support for real-time multiplayer, but it’s only available for iOS devices. In this day and age, there’s probably a very good chance that even the most hardcore iOS fan has an Android-using friend or two that they’d like to play games with.
One of the advantages of using Unity to make your 2D game is that you can easily export your game to both Android and iOS, and it would be awfully nice if your multiplayer solution followed suit. In the interest of full disclosure, I work for Google and I occasionally play Towerfall with the people who made Google Play game services, so I’m just a teeny bit biased.
Google Play game services includes a number of features for game developers including achievements, leaderboards, saving your game in the cloud, creating quests, and, most importantly for this tutorial, support for turn-based and real-time multiplayer. You won’t implement any of these features right now except for real-time multiplayer. However, once you go through the work to integrate Play services into your game, it’s probably worth checking out some of the other features to see what they can offer you.
Setting Up Google Play Game Services
Okay, I’ll level with you here: there’s a bit of setup required to get things going. But stick with me — once you get through this part, you can go back to writing glorious code!
Registering Your Game
Before you do anything else, you’ll need to register your application with the Google Play Developer Console and get yourself a Client ID. This identifies your app with the service and lets you perform important actions like signing in the user.
Open https://play.google.com/apps/publish/ in your browser. If you don’t have a Google Play developer account, you can set one up at this point:
If you have an account and log in, you will see a screen that either looks like this:
…or one that shows your existing applications:
Either way, get started by clicking the little gamepad icon on the left:
Next, click the Set Up Google Play game services button like so:
If you’ve set up Play game services before, click on the Add a new game button at the top of the screen:
A dialog box will appear with two tabs: the first tab is relevant if you aren’t yet using any Google APIs in your game, and the second is for the case where you’re working with an existing game that already uses Google APIs. You’re definitely in the first situation, so leave the dialog on the first tab.
Name your project appropriately; you can call it whatever you like, as it doesn’t need to be unique. Choose a game category as well; “Racing” is probably a good choice, but hey, it’s a free country. When you’re done, your dialog should look like the following:
Click Continue, and you’ll find yourself on the Game Details page, where you can add a name, a description and some graphics to your game. This are things you’ll have to handle before you publish your game in real life, but for the purposes of this tutorial you can skip filling out these items for now.
Instead, you’ll set up your Client ID. Click on the Linked Apps tab on the left side as shown below:
Next, select iOS from the given options like so:
In the next page that appears, leave Name set to whatever is prefilled for you and leave both iPad and iPhone set to YES. Next, enter the Bundle ID you assigned to your game back in the Build Settings step.
Leave the iTunes App ID blank; you don’t need this for now. Your page should now look like the following:
Below that, you have two separate options in the Multiplayer Settings section to enable turn-based and real-time multiplayer support for your game. But which one do you want?
Turn-Based vs. Real-Time: A Quick Primer
Google Play game services, along with many other game frameworks, supports turn-based and real-time games. Here’s a quick introduction to what each of these mean:
A real-time game requires that everybody be present and playing at the same time; it generally applies to games where all players play at once and see each other’s moves as they happen. Examples of this include first-person shooters, multiplayer online battle arenas, and most sports games.
Turn-based games, on the other hand, are generally played by one person at a time, usually while the other players aren’t active and — gasp! — might not even have their phones with them. With these types of games it might take a day or two for players to take their next turn; as a result, this option is most useful for games that are slower, more contemplative, or involve situations where a single player’s turn can take a long time. Words With Friends and Draw Something are prototypical examples of turn-based games.
Pop quiz, hotshot: you’re making a Texas Hold’em poker game. Should this be turn-based, or real-time? Check your answer below.
In your racing game, you’ll have two players racing against each other on the same playing field, so turn the Real-time multiplayer setting to ON.
Click the Save and continue button at the top of the screen. Next, you’ll see a screen like the one below where you’re asked to authorize your app:
This is where you create your client ID, which is essentially an identifier that identifies your app on Google’s servers. Click the Authorize your app now button.
The first dialog that appears lets you add your company’s name, logo, and home page; these settings determine how your game appears in the various screens that prompt the user to sign in to your application:
For this tutorial, just click Continue.
The next dialog asks for your bundle identifier and App Store ID. It’s been pre-populated with the same information you added previously in Step 1 of the dialog, so you shouldn’t have to do anything here. Simply confirm everything is correct, then click Create client:
Once the page finishes processing, you will see a giant string ending with apps.googleusercontent.com
— this is your Client ID:
Copy and paste this string into a text file somewhere, as you’re going to need it later.
Adding Testers
There’s just one last step before you can get coding. Click the Testing tab on the left side of the screen to go the Testing panel. In the Testing Access section you should see your email address, and possibly those of your other team members like so:
When Play game services are in an unpublished state, they can only by used by accounts that are listed here. Since you’re going to be testing with a few other Google accounts — for your multiplayer game, you’re going to need at least two — click on the Add testers button and add the addresses you’d like to whitelist:
Give yourself a pat on the back — you’re all done with the Google Play setup! :]
Installing the Unity Plugin
Now you need to install the plugin that lets Unity talk to Play Games. Go to https://github.com/playgameservices/play-games-plugin-for-unity and click the “Download ZIP” button on the right:
Once that’s done, unzip the package you downloaded and head back to Unity. Hello, Circuit Racer! Did you miss us? :] To install the plugin, select Assets\Import Package\Custom Package as follows:
Browse to the folder you created, then navigate to the contained current-build folder. Select the file that ends with unity package.
You should now see an Importing package dialog box; all options should be already selected, but if not, simply click the All button. Finally, click Import:
You now have a couple of exciting new folders, and a new “Google Play Games” menu item. If you don’t see the menu item, click on the menu bar to force Unity to refresh its contents. Select Google Play Games\iOS Setup from the menu and you’ll see a dialog box where you need to provide two pieces of information:
- The “Client ID” is the big string you copied earlier in this tutorial.
- The “Bundle ID” is the Bundle ID you’ve been using all along.
Enter the two pieces of information into the dialog and click Setup. You should receive an alert that all is well, at which point you can close the dialog.
Oh shoot — did you forget to copy down the Client ID earlier? No problem; here’s how to find your Client ID again:
- Go back to the Play Developer console at https://play.google.com/apps/publish/.
- Click on Game Services (the little controller icon) on the left.
- Click on your game.
- Click on “Linked Apps”.
- Click on the entry for the iOS version.
- At the bottom, you should see your Client ID next to a label that says, OAuth2 Client ID.
- Copy this value to the clipboard and continue on with the tutorial!
Build and run your project in Xcode (Command-B is a handy keyboard shortcut). If everything goes well, Unity will export your application to Xcode, which will then compile and run your application on your device, and..whoops. What happened?
There are a few modifications to make to your Xcode project before you can run your game on a physical device. Fortunately, the post-build script that Unity runs brings up a dialog box that tells you exactly what you need to do to get things working:
That doesn’t look too serious; you’ll tackle them in order.
Adding Missing Frameworks
In Xcode, select the Unity-iPhone project on the left, and then the Unity-iPhone target in the main panel. Go to Build Phases, expand the Link Binary With Libraries section on the right, and then click the + button to bring up the dialog where you can add all the frameworks you need for this project to run:
The dialog box should list the frameworks you need to include, but at the time of this writing, you’ll need to add the following frameworks:
- AddressBook
- AssetsLibrary
- CoreData
- CoreTelephony
- CoreText
- libc++.dylib
- libz.dylib
- Security
To make your life easier, you can hold down the Command button to select several frameworks at once.
Adding More Frameworks
Now you’ll need to download the missing SDKs. Head to https://developers.google.com/games/services/downloads/. Download and unzip both the the Games C++ SDK and the Google+ iOS SDK as shown below:
Note: Be careful: there is an iOS Games SDK listed on the same page. You don’t want that one, even though you’re building an iOS game. You need to download and use the C++ SDK instead.
Also note that clicking the link to download the Google+ iOS SDK takes you to a separate download page where you need to click the Download zip… link at the bottom of the screen, which in turn takes you to another page where you need to click on the Download the iOS+ SDK button to finally download the package. They, uh… really like having you click on things, apparently! :]
Navigate into the gpg-cpp-sdk folder you just unzipped, go into the iOS subfolder and drag the gpg.framework and GooglePlayGames.bundle files directly into Xcode. In the resulting dialog box, ensure the Unity-iPhone target is checked. It’s up to you whether or not to select the Copy items if needed checkbox; personally, I like doing it, but if you’re the type of person that likes to have all your third-party libraries in one location, you can leave it unchecked.
Next, navigate into the google-plus-ios-sdk folder and drag the GoogleOpenSource.framework, GooglePlus.bundle and GooglePlus.framework files into Xcode where you should see them added to the list of frameworks:
Adding the -ObjC Flag
Finally, head back to XCode and select the Build Settings tab for your target. Find the Other Linker Flags option — it’s in the Linking section. Double click the part that reads: -weak_framework CoreMotion -weak-ISystem, then click the + button at the bottom of the dialog to add the -ObjC
flag. Your project should look like this once you’re done:
-ObjC
step above; it’s a particularly insidious error because your code will compile and build just fine without it. But when you try to make a call, your project will crash with a mysterious unrecognized selector
message.Now you can finally go back and run your game again! Click the Run button, and if all has gone according to plan, you should see your Circuit Racer running on your device.
True, the game looks the same as before — but there’s a whole slew of code and libraries underneath just waiting for you to make use of them! :]
Signing in the Player
Your first task will be to sign in the user when they click the multiplayer button. If you can get that piece working, you know you’ve set up the game services correctly and you can move on to the more interesting parts of developing a multiplayer game.
Create a new class to handle some of your multiplayer code. Double-click the Scripts folder in the Unity Assets panel. Then right-click the assets panel, select Create\C# Script and name the new script MultiplayerController. Double-click to edit the script in your code editor of choice.
Add the two following imports to the beginning of the file:
using GooglePlayGames; using GooglePlayGames.BasicApi.Multiplayer; |
Next, find the following line:
public class MultiplayerController : MonoBehaviour { |
…and replace it with the following:
public class MultiplayerController { |
Then delete the two boilerplate Start()
and Update()
methods.
If you’re fairly new to Unity, you might not recognize what’s happening here. You’re creating a piece of code that won’t be attached as a component to a Game Object; instead, you’re creating the object purely in code. With this approach you can’t use MonoBehavior
as your base class; you can either use ScriptableObject
as your base class, or, as in this situation, nothing at all.
Next, add the following content your class so that it’s created as a singleton:
private static MultiplayerController _instance = null; private MultiplayerController() { // Code to initialize this object would go here } public static MultiplayerController Instance { get { if (_instance == null) { _instance = new MultiplayerController(); } return _instance; } } |
Not sure what a singleton is? It’s a common coding pattern where you create one (and only one) instance of an object that can be easily referenced by other classes in your project.
In Unity, creating a class as a singleton can be useful when you need to use it across multiple scenes. The UserPreferences
object is a good example, because it’s an object you need to access from the main menu, the game, and many other sub-menu scenes. Similarly, you’ll need to access MultiplayerController
from both the main menu as well as in the game, so it helps to implement this as a singleton as well.
In order to access the object in the future, you won’t look for specific game objects or create new objects. Instead, you’ll call the static MultiplayerController.Instance
method, which either creates the singleton if it doesn’t yet exist, or returns the one that’s already been instantiated.
Note: Want to find out more about singletons? Check out Eli Ganem’s excellent iOS Design Patterns tutorial on this site for more detail.
Some coding aficionados aren’t terribly fond of singletons, and claim that singletons suffer from overuse. That’s a debate for another time — right now, you’ve got a game to make! :]
Next, modify the code inside MultiplayerController()
as follows:
private MultiplayerController() { PlayGamesPlatform.DebugLogEnabled = true; PlayGamesPlatform.Activate (); } |
The first line sets your logs to be nice and verbose, and the second line tells the PlayGamesPlatform
to initialize itself.
Next, add the following method to your class:
public void SignInAndStartMPGame() { if (! PlayGamesPlatform.Instance.localUser.authenticated) { PlayGamesPlatform.Instance.localUser.Authenticate((bool success) => { if (success) { Debug.Log ("We're signed in! Welcome " + PlayGamesPlatform.Instance.localUser.userName); // We could start our game now } else { Debug.Log ("Oh... we're not signed in."); } }); } else { Debug.Log ("You're already signed in."); // We could also start our game now } } |
This is your basic sign-in logic. Calling PlayGamesPlatform.Instance
brings up Google Play’s own singleton instance. The first if
statement checks to see if the local player is signed in. If not, then you call the platform’s Authenticate
method, which attempts to sign the user in.
Also notice that the argument you’re passing to Authenticate
is, itself, a function. Since Authenticate
might take a while to execute, you pass in a callback function to execute when Authenticate
has completed; this is similar to blocks in Objective-C.
Now that you have a method to sign the user in, you need a place from which to call it.
Open up MainMenuScript.cs
, found in the MenuStuff folder. Find the following line in OnGUI()
:
Debug.Log("We would normally load a multiplayer game here"); |
…and replace it with the following:
MultiplayerController.Instance.SignInAndStartMPGame(); |
This calls SignInAndStartMPGame()
which you created just a moment ago.
Build your project:
Don’t select Replace, unless you feel like re-doing all those setup steps in the Fixing Your Xcode Project section all over again. I know I don’t!
Once you have built your project, run the app. Click the Multiplayer button once your application launches; you’ll be taken to a screen where you’re asked to sign in. Use one of the accounts you listed as a “Tester” account earlier in the tutorial. Click Accept and you’ll be taken back to Circuit Racer where you should see a “Welcome back!” graphic appear at the top of the screen and a “We’re signed in! Welcome
You’ve signed in — looks like everything is hooked up and authenticating correctly!
Improving the Sign-In Process
Stop the application and start it again in Xcode. Notice that you’re no longer signed in, but when you click the Multiplayer button this time around, you’ll see the “welcome back” message without having to go through the sign-in process. What’s going on?
If you’ve signed in previously, haven’t explicitly signed out, and the game isn’t asking for any new permissions, then Google Play Games assumes it’s okay to sign you in again without showing you another sign-in dialog, which would get pretty annoying over time.
You can take advantage of this fact by “silently” signing the user in when the application starts. Go back to your MultiplayerController.cs
script and add the following new method:
public void TrySilentSignIn() { if (! PlayGamesPlatform.Instance.localUser.authenticated) { PlayGamesPlatform.Instance.Authenticate ((bool success) => { if (success) { Debug.Log ("Silently signed in! Welcome " + PlayGamesPlatform.Instance.localUser.userName); } else { Debug.Log ("Oh... we're not signed in."); } }, true); } else { Debug.Log("We're already signed in"); } } |
This looks awfully similar to the code in SignInAndStartMPGame
; the difference is that you have a true
argument at the end of your Authenticate
call, which instructs the Play service to try to sign the user in silently. That is, Google Play will check to see, “Is this a case where I can sign the user in without having to show them a dialog?” If so, it will sign the user in silently.
You’ll need to call this from somewhere. Add the following line to the end of Start()
in MainMenuScript
:
MultiplayerController.Instance.TrySilentSignIn(); |
Build and run your project from within Unity; this time, the application should start up and, after a moment or two, you’ll see the welcome back notification and the console text indicating that you’re signed in. Tap the Multiplayer button and you should see a line in your console log noting that you’re already signed in.
Adding a Sign-Out Button
Since you’re knee-deep in the authentication code, you may as well add a sign-out button too. You wouldn’t typically put such a button on the main screen, as people tend to sign out infrequently enough that you could put this in a settings screen somewhere. However, I find it useful to quickly sign in and out of the app while testing during development.
Add the following public variable to the top of your MainMenuScript
class:
public Texture2D signOutButton; |
This public variable will hold a reference to your button image.
Next, add the following code to the end of OnGUI()
, outside of the for
loop:
if (MultiplayerController.Instance.IsAuthenticated()) { if (GUI.Button(new Rect(Screen.width - (buttonWidth * 0.75f), Screen.height - (buttonHeight * 0.75f), buttonWidth * 0.75f, buttonHeight * 0.75f), signOutButton)) { MultiplayerController.Instance.SignOut(); } } |
This code checks if there is currently a signed-in user; if so, it displays a button to sign the user out. If the player taps that button, the app calls SignOut()
on your MultiplayerController
.
Next, you need to implement those two new methods that you’re calling in your MultiplayerController
; fortunately, they’re pretty straightforward. Go back to MultiplayerController.cs
and add the following two methods:
public void SignOut() { PlayGamesPlatform.Instance.SignOut (); } public bool IsAuthenticated() { return PlayGamesPlatform.Instance.localUser.authenticated; } |
Note: Are you wondering why you’re going through the trouble of calling SignOut()
in MultiplayerController
, which then calls SignOut()
on the Play Games platform — instead of simply calling PlayGamesPlatform.Instance.SignOut()
directly in your MainMenuScript
?
This is because you’re trying to follow the general principle of encapsulation (or “information hiding”) and keep all related logic together in one class, rather than spread it out over many classes. If you wanted to swap out Play Games for a completely different multiplayer service, you’d only have to modify MultiplayerController
; you wouldn’t have to change any of your other code.
Go back to Unity and open up the MainMenu scene; click on MainMenuGameObject
in the hierarchy panel, and you’ll see that the Main Menu Script now has a Sign Out Button entry that is currently undefined. Click on the circle next to the input text field and select btn-signout from the game’s assets as shown below:
Build and run your app; test the sign-in and sign-out process a few times to make sure it works consistently. The sign-out option will come in quite handy as you test your app with multiple players down the road.
Where to Go From Here?
The end result of all your hard work is that a lot of the setup work is done and you can finally get around to the fun parts of building a multiplayer game.
In Part 2 of this tutorial series, you’ll tackle the actual multiplayer aspects of the game. You’ll add matchmaking capabilities so you can connect your games client with other players across the room — or across the world. You’ll add the opponent’s car to the racetrack, and you’ll begin to send and receive gameplay messages so that you can get these cars racing!
You can download the completed project for this part over here. Just make sure add your own Google API Client ID.
In the meantime, if you have any questions or comments, please feel free to join in the discussion below!
Creating a Cross-Platform Multiplayer Game in Unity — Part 1 is a post from: Ray Wenderlich
The post Creating a Cross-Platform Multiplayer Game in Unity — Part 1 appeared first on Ray Wenderlich.