Search icon CANCEL
Subscription
0
Cart icon
Your Cart (0 item)
Close icon
You have no products in your basket yet
Arrow left icon
Explore Products
Best Sellers
New Releases
Books
Videos
Audiobooks
Learning Hub
Free Learning
Arrow right icon
Unity Multiplayer Games
Unity Multiplayer Games

Unity Multiplayer Games: Take your gaming development skills into the online multiplayer arena by harnessing the power of Unity 4 or 3. This is not a dry tutorial – it uses exciting examples and an enthusiastic approach to bring it all to life.

eBook
€8.99 €29.99
Paperback
€36.99
Subscription
Free Trial
Renews at €18.99p/m

What do you get with Print?

Product feature icon Instant access to your digital eBook copy whilst your Print order is Shipped
Product feature icon Paperback book shipped to your preferred address
Product feature icon Download this book in EPUB and PDF formats
Product feature icon Access this title in our online reader with advanced features
Product feature icon DRM FREE - Read whenever, wherever and however you want
OR
Modal Close icon
Payment Processing...
tick Completed

Shipping Address

Billing Address

Shipping Methods
Table of content icon View table of contents Preview book icon Preview Book

Unity Multiplayer Games

Chapter 1. Unity Networking – The Pong Game

Multiplayer is everywhere. It's a staple of AAA games and small-budget indie offerings alike. Multiplayer games tap into our most basic human desires. Whether it be teaming up with strangers to survive a zombie apocalypse, or showing off your skills in a round of "Capture the Flag" on your favorite map, no artificial intelligence in the world comes close to the feeling of playing with a living, breathing, and thinking human being.

Unity3D has a sizable number of third-party networking middleware aimed at developing multiplayer games, and is arguably one of the easiest platforms to prototype multiplayer games.

The first networking system most people encounter in Unity is the built-in Unity Networking API. This API simplifies a great many tasks in writing networked code by providing a framework for networked objects rather than just sending messages. This works by providing a NetworkView component, which can serialize object state and call functions across the network.

Additionally, Unity provides a Master server, which essentially lets players search among all public servers to find a game to join, and can also help players in connecting to each other from behind private networks.

In this chapter, we will cover:

  • Introducing multiplayer
  • Introducing UDP communication
  • Setting up your own Master server for testing
  • What a NetworkView is
  • Serializing object state
  • Calling RPCs
  • Starting servers and connecting to them
  • Using the Master server API to register servers and browse available hosts
  • Setting up a dedicated server model
  • Loading networked levels
  • Creating a Pong clone using Unity networking

Introducing multiplayer games

Before we get started on the details of communication over the Internet, what exactly does multiplayer entail in a game?

As far as most players are concerned, in a multiplayer game they are sharing the same experience with other players. It looks and feels like they are playing the same game. In reality, they aren't. Each player is playing a separate game, each with its own game state. Trying to ensure that all players are playing the exact same game is prohibitively expensive. Instead, games attempt to synchronize just enough information to give the illusion of a shared experience.

Games are almost ubiquitously built around a client-server architecture, where each client connects to a single server. The server is the main hub of the game, ideally the machine for processing the game state, although at the very least it can serve as a simple "middleman" for messages between clients. Each client represents an instance of the game running on a computer. In some cases the server might also have a client, for instance some games allow you to host a game without starting up an external server program.

While an MMO (Massively Multiplayer Online) might directly connect to one of these servers, many games do not have prior knowledge of the server IPs. For example, FPS games often let players host their own servers. In order to show the user a list of servers they can connect to, games usually employ another server, known as the "Master Server" or alternatively the "Lobby server". This server's sole purpose is to keep track of game servers which are currently running, and report a list of these to clients. Game servers connect to the Master server in order to announce their presence publicly, and game clients query the Master server to get an updated list of game servers currently running.

Alternatively, this Master server sometimes does not keep track of servers at all. Sometimes games employ "matchmaking", where players connect to the Lobby server and list their criteria for a game. The server places this player in a "bucket" based on their criteria, and whenever a bucket is full enough to start a game, a host is chosen from these players and that client starts up a server in the background, which the other players connect to. This way, the player does not have to browse servers manually and can instead simply tell the game what they want to play.

Introducing UDP communication

The built-in Unity networking is built upon RakNet. RakNet uses UDP communication for efficiency.

UDP (User Datagram Protocols) is a simple way to send messages to another computer. These messages are largely unchecked, beyond a simple checksum to ensure that the message has not been corrupted. Because of this, messages are not guaranteed to arrive, nor are they guaranteed to only arrive once (occasionally a single message can be delivered twice or more), or even in any particular order. TCP, on the other hand, guarantees each message to be received just once, and in the exact order they were sent, although this can result in increased latency (messages must be resent several times if they fail to reach the target, and messages must be buffered when received, in order to be processed in the exact order they were sent).

To solve this, a reliability layer must be built on top of UDP. This is known as rUDP (reliable UDP). Messages can be sent unreliably (they may not arrive, or may arrive more than once), or reliably (they are guaranteed to arrive, only once per message, and in the correct order). If a reliable message was not received or was corrupt, the original sender has to resend the message. Additionally, messages will be stored rather than immediately processed if they are not in order. For example, if you receive messages 1, 2, and 4, your program will not be able to handle those messages until message 3 arrives.

Allowing unreliable or reliable switching on a per-message basis affords better overall performance. Messages, such as player position, are better suited to unreliable messages (if one fails to arrive, another one will arrive soon anyway), whereas damage messages must be reliable (you never want to accidentally drop a damage message, and having them arrive in the same order they were sent reduces race conditions).

In Unity, you can serialize the state of an object (for example, you might serialize the position and health of a unit) either reliably or unreliably (unreliable is usually preferred). All other messages are sent reliably.

Setting up the Master Server

Although Unity provide their own default Master Server and Facilitator (which is connected automatically if you do not specify your own), it is not recommended to use this for production. We'll be using our own Master Server, so you know how to connect to one you've hosted yourself.

Firstly, go to the following page:

http://unity3d.com/master-server/

We're going to download two of the listed server components: the Master Server and the Facilitator as shown in the following screenshot:

Setting up the Master Server

The servers are provided in full source, zipped. If you are on Windows using Visual Studio Express, open up the Visual Studio .sln solution and compile in the Release mode. Navigate to the Release folder and run the EXE (MasterServer.exe or Facilitator.exe). If you are on a Mac, you can either use the included XCode project, or simply run the Makefile (the Makefile works under both Linux and Mac OS X).

The Master Server, as previously mentioned, enables our game to show a server lobby to players. The Facilitator is used to help clients connect to each other by performing an operation known as NAT punch-through. NAT is used when multiple computers are part of the same network, and all use the same public IP address. NAT will essentially translate public and private IPs, but in order for one machine to connect to another, NAT punch-through is necessary. You can read more about it here:

http://www.raknet.net/raknet/manual/natpunchthrough.html

The default port for the Master Server is 23466, and for the Facilitator is 50005. You'll need these later in order to configure Unity to connect to the local Master Server and Facilitator instead of the default Unity-hosted servers.

Now that we've set up our own servers, let's take a look at the Unity Networking API itself.

NetworkViews and state serialization

In Unity, game objects that need to be networked have a NetworkView component. The NetworkView component handles communication over the network, and even helps make networked state serialization easier. It can automatically serialize the state of a Transform, Rigidbody, or Animation component, or in one of your own scripts you can write a custom serialization function.

When attached to a game object, NetworkView will generate a NetworkViewID for NetworkView. This ID serves to uniquely identify a NetworkView across the network. An object can be saved as part of a scene with NetworkView attached (this can be used for game managers, chat boxes, and so on), or it can be saved in the project as a prefab and spawned later via Network.Instantiate (this is used to generate player objects, bullets, and so on). Network.Instantiate is the multiplayer equivalent to GameObject.Instantiate — it sends a message over the network to other clients so that all clients spawn the object. It also assigns a network ID to the object, which is used to identify the object across multiple clients (the same object will have the same network ID on every client).

Note

A prefab is a template for a game object (such as the player object). You can use the Instantiate methods to create a copy of the template in the scene.

Spawned network game objects can also be destroyed via Network.Destroy. It is the multiplayer counterpart of GameObject.Destroy. It sends a message to all clients so that they all destroy the object. It also deletes any RPC messages associated with that object.

NetworkView has a single component that it will serialize. This can be a Transform, a Rigidbody, an Animation, or one of your own components that has an OnSerializeNetworkView function. Serialized values can either be sent with the ReliableDeltaCompressed option, where values are always sent reliably and compressed to include only changes since the last update, or they can be sent with the Unreliable option, where values are not sent reliably and always include the full values (not the change since the last update, since that would be impossible to predict over UDP). Each method has its own advantages and disadvantages. If data is constantly changing, such as player position in a first person shooter, in general Unreliable is preferred to reduce latency. If data does not often change, use the ReliableDeltaCompressed option to reduce bandwidth (as only changes will be serialized).

NetworkView can also call methods across the network via Remote Procedure Calls (RPC). RPCs are always completely reliable in Unity Networking, although some networking libraries allow you to send unreliable RPCs, such as uLink or TNet.

Writing a custom state serializer

While initially a game might simply serialize Transform or Rigidbody for testing, eventually it is often necessary to write a custom serialization function. This is a surprisingly easy task.

Tip

Downloading the example code

You can download the example code files for all Packt books you have purchased from your account at http://www.packtpub.com. If you purchased this book elsewhere, you can visit http://www.packtpub.com/support and register to have the files e-mailed directly to you.

Here is a script that sends an object's position over the network:

using UnityEngine;
using System.Collections;

public class ExampleUnityNetworkSerializePosition : MonoBehaviour
{
  public void OnSerializeNetworkView( BitStream stream, NetworkMessageInfo info )
  {
    // we are currently writing information to the network
    if( stream.isWriting )
    {
      // send the object's position
      Vector3 position = transform.position;
      stream.Serialize( ref position );
    }
    // we are currently reading information from the network
    else
    {
      // read the first vector3 and store it in 'position'
      Vector3 position = Vector3.zero;
      stream.Serialize( ref position );
    
      // set the object's position to the value we were sent
      transform.position = position;
    }
  }
}

Most of the work is done with BitStream. This is used to check if NetworkView is currently writing the state, or if it is reading the state from the network. Depending on whether it is reading or writing, stream.Serialize behaves differently. If NetworkView is writing, the value will be sent over the network. However, if NetworkView is reading, the value will be read from the network and saved in the referenced variable (thus the ref keyword, which passes Vector3 by reference rather than value).

Using RPCs

RPCs are useful for single, self-contained messages that need to be sent, such as a character firing a gun, or a player saying something in chat.

In Unity, RPCs are methods marked with the [RPC] attribute. This can be called by name via networkView.RPC( "methodName", … ). For example, the following script prints to the console on all machines when the space key is pressed.

using UnityEngine;
using System.Collections;

public class ExampleUnityNetworkCallRPC : MonoBehavior
{
  void Update()
  {
    // important – make sure not to run if this networkView is notours
    if( !networkView.isMine )
      return;

    // if space key is pressed, call RPC for everybody
    if( Input.GetKeyDown( KeyCode.Space ) )
      networkView.RPC( "testRPC", RPCMode.All );
  }

  [RPC]
  void testRPC( NetworkMessageInfo info )
  {
    // log the IP address of the machine that called this RPC
    Debug.Log( "Test RPC called from " + info.sender.ipAddress );
  }
}

Also note the use of NetworkView.isMine to determine ownership of an object. All scripts will run 100 percent of the time regardless of whether your machine owns the object or not, so you have to be careful to avoid letting some logic run on remote machines; for example, player input code should only run on the machine that owns the object.

RPCs can either be sent to a number of players at once, or to a specific player. You can either pass an RPCMode to specify which group of players to receive the message, or a specific NetworkPlayer to send the message to. You can also specify any number of parameters to be passed to the RPC method.

RPCMode includes the following entries:

  • All (the RPC is called for everyone)
  • AllBuffered (the RPC is called for everyone, and then buffered for when new players connect, until the object is destroyed)
  • Others (the RPC is called for everyone except the sender)
  • OthersBuffered (the RPC is called for everyone except the sender, and then buffered for when new players connect, until the object is destroyed)
  • Server (the RPC is sent to the host machine)

Note

Note that, with the exception of RPCMode.All and RPCMode.AllBuffered, a client cannot send an RPC to itself.

Initializing a server

The first thing you will want to set up is hosting games and joining games. To initialize a server on the local machine, call Network.InitializeServer.

This method takes three parameters: the number of allowed incoming connections, the port to listen on, and whether to use NAT punch-through. The following script initializes a server on port 25000 which allows 8 clients to connect:

using UnityEngine;
using System.Collections;

public class ExampleUnityNetworkInitializeServer : MonoBehavior
{
  void OnGUI()
  {
    if( GUILayout.Button( "Launch Server" ) )
    {
      LaunchServer();
    }
  }
  
  // launch the server
  void LaunchServer()
  {
    // Start a server that enables NAT punchthrough,
    // listens on port 25000,
    // and allows 8 clients to connect
    Network.InitializeServer( 8, 25005, true );
  }

  // called when the server has been initialized
  void OnServerInitialized()
  {
    Debug.Log( "Server initialized" );
  }
}

You can also optionally enable an incoming password (useful for private games) by setting Network.incomingPassword to a password string of the player's choice, and initializing a general-purpose security layer by calling Network.InitializeSecurity(). Both of these should be set up before actually initializing the server.

Note

Note that incoming connections does not mean maximum player count, since it does not include the host (for example, if you allow 8 players to connect, it's possible for 9 players to play in the same room—8 clients plus the host).

Connecting to a server

To connect to a server you know the IP address of, you can call Network.Connect.

The following script allows the player to enter an IP, a port, and an optional password and attempts to connect to the server:

using UnityEngine;
using System.Collections;

public class ExampleUnityNetworkingConnectToServer : MonoBehavior
{
  private string ip = "";
  private string port = "";
  private string password = "";

  void OnGUI()
  {
    GUILayout.Label( "IP Address" );
    ip = GUILayout.TextField( ip, GUILayout.Width( 200f ) );

    GUILayout.Label( "Port" );
    port = GUILayout.TextField( port, GUILayout.Width( 50f ) );

    GUILayout.Label( "Password (optional)" );
    password = GUILayout.PasswordField( password, '*',GUILayout.Width( 200f ) );

    if( GUILayout.Button( "Connect" ) )
    {
      int portNum = 25005;

      // failed to parse port number – a more ideal solution is tolimit input to numbers only, a number of examples can befound on the Unity forums
      if( !int.TryParse( port, out portNum ) )
      {
        Debug.LogWarning( "Given port is not a number" );
      }
      // try to initiate a direct connection to the server
      else
      {
        Network.Connect( ip, portNum, password );
      }
    }
  }

  void OnConnectedToServer()
  {
    Debug.Log( "Connected to server!" );
  }

  void OnFailedToConnect( NetworkConnectionError error )
  {
    Debug.Log( "Failed to connect to server: " +error.ToString() );
  }

}
Connecting to a server

Connecting to the Master Server

While we could just allow the player to enter IP addresses to connect to servers (and many games do, such as Minecraft), it's much more convenient to allow the player to browse a list of public servers. This is what the Master Server is for.

Now that you can start up a server and connect to it, let's take a look at how to connect to the Master Server you downloaded earlier. First, make sure both the Master Server and Facilitator are running. I will assume you are running them on your local machine (IP is 127.0.0.1), but of course you can run these on a different computer and use that machine's IP address. Keep in mind, if you want the Master Server publicly accessible, it must be installed on a machine with a public IP address (it cannot be in a private network).

Let's configure Unity to use our Master Server rather than the Unity-hosted test server. The following script configures the Master Server and Facilitator to connect to a given IP (by default 127.0.0.1):

using UnityEngine;
using System.Collections;

public class ExampleUnityNetworkingConnectToMasterServer : MonoBehaviour
{
  // Assuming Master Server and Facilitator are on the same machine
  public string MasterServerIP = "127.0.0.1";

  void Awake()
  {
    // set the IP and port of the Master Server to connect to
    MasterServer.ipAddress = MasterServerIP;
    MasterServer.port = 23466;

    // set the IP and port of the Facilitator to connect to
    Network.natFacilitatorIP = MasterServerIP;
    Network.natFacilitatorPort = 50005;
  }
}

Registering a server with the Master Server

Now that you've configured the Master Server, it's time to register a server with it. This is easy to do.

Immediately after making a call to Network.InitializeServer, make another call to MasterServer.RegisterHost. This call connects to the Master Server and tells it to display our server in the public game list.

The RegisterHost function takes three parameters, all strings: gameTypeName, gameName, and comment. The game type name is used to separate different game listings from each other. For example, if two games use the same Master Server, they would both supply different game type names in order to avoid getting listings for the other game. The game name is the name of the host server, for example "John's server". The comment is a general purpose data string, essentially anything can be stored here. For example you could store data about the server (such as map rotation, available modes, and so on) and display these to the user while they browse the lobby.

Because RegisterHost is a separate call from InitializeServer, you can simply omit the call to RegisterHost to implement private or LAN-style servers.

Note

You can call RegisterHost more than once while a server is running to update the information stored on the Master Server. For example, if the server changes to a new level, you might call RegisterHost again to update the lobby.

Browsing available servers

To browse the available servers, call MasterServer.RequestHostList. This takes one single parameter: the game type name (this is the same game type name you passed to RegisterHost).

This does not return anything, instead the result will be asynchronously downloaded, and the last known list of servers can be accessed via MasterServer.PollHostList. Additionally, to ensure you aren't using old data, you can call MasterServer.ClearHostList. For example, if the user hits the Refresh button in the lobby you might clear the host list and then request a new list from the Master Server.

The following script shows a lobby for users to browse available servers and connect to them:

using UnityEngine;
using System.Collections;

public class ExampleUnityNetworkingBrowseServers : MonoBehavior
{
  // are we currently trying to download a host list?
  private bool loading = false;

  // the current position within the scrollview
  private Vector2 scrollPos = Vector2.zero;

  void Start()
  {
    // immediately request a list of hosts
    refreshHostList();
  }

  void OnGUI()
  {
    if( GUILayout.Button( "Refresh" ) )
    {
      refreshHostList();
    }

    if( loading )
    {
      GUILayout.Label( "Loading..." );
    }
    else
    {
      scrollPos = GUILayout.BeginScrollView( scrollPos, GUILayout.Width( 200f ), GUILayout.Height( 200f ) );

      HostData[] hosts = MasterServer.PollHostList();
      for( int i = 0; i < hosts.Length; i++ )
      {
        if( GUILayout.Button( hosts[i].gameName, GUILayout.ExpandWidth( true ) ) )
        {
          Network.Connect( hosts[i] );
        }
      }
      if( hosts.Length == 0 )
      {
        GUILayout.Label( "No servers running" );
      }

      GUILayout.EndScrollView();
    }
  }

  void refreshHostList()
  {
    // let the user know we are awaiting results from the master server
    loading = true;
    MasterServer.ClearHostList();
    MasterServer.RequestHostList( "GameTypeNameHere" );
  }

  // this is called when the Master Server reports an event to the client – for example, server registered successfully, host list received, etc
  void OnMasterServerEvent( MasterServerEvent msevent )
  {
    if( msevent == MasterServerEvent.HostListReceived )
    {
      // received the host list, no longer awaiting results
      loading = false;
    }
  }
}

The preceding code will list available servers registered to the Master Server. Clicking one of the buttons will call the Network.Connect function and connect to the corresponding server, and clicking on Refresh will display a Loading... message while results are fetched from the Master Server. There are a number of improvements and other tweaks that can be made to this code, left as an exercise for the reader:

  • Refresh the host list every few seconds. This should be done transparently, without displaying a "Loading" message.
  • Allow the user to add servers to a "favorites" list (possibly saved as CSV to PlayerPrefs), if your game allows players to run dedicated servers.
  • If the user attempts to connect to a password-protected game (HostData.passwordProtected is true), display a password entry field.
  • Save game information such as map, mode, and so on in the Comments field when registering a server, and allow the user to filter server results.

Setting up a dedicated server model

Many games allow players to host their own dedicated servers, as separate applications from the game client. Some games even allow players to modify the behavior of the server through scripting languages, allowing player-run servers to employ novel behaviors not originally designed into the game.

Let's see how we can set up a similar system in Unity. I will not be covering modding, although readers can look up Lua scripting in Unity—there are a number of resources on the topic.

Servers in Unity

Most games have a specialized "server" build, which contains much the same code as the client, designed to run as a dedicated server. This allows the server to process the same logic as the client.

Unity, however, does not directly support this concept out of the box. Unity Pro does allow builds to be run in "headless mode", which runs the game without initializing any graphics, resources, but the server runs the exact same code as the client. The game must be designed to operate in both server and client mode.

To do this, we'll take advantage of a compiler feature known as "conditional compilation". This allows us to wrap code in special tags which allows us to strip out entire sections of code when compiling. This way, our server-only code will only be included in server builds, and our client-only code will only be included in client builds.

Compiler directives

The first thing we will do, is figure out how the application knows whether it is a client or a server. We will use a compiler directive to do this.

If you are using Unity 4, you can go to Edit | Project Settings | Player and under Other Settings is a section that allows you to define these.

However, for any version prior to Unity 4, you'll have to define these yourself. To do this, create a new text file in the Assets folder and name it smcs.rsp. Open Notepad and type:

-define:SERVER

This creates a global symbol define for your C# scripts. You would use the symbol like this:

#if SERVER
  //code in here will not be compiled if SERVER isn't defined
#endif

You might consider writing an editor script which replaces the contents of this file (when compiling for the client, it would replace SERVER with CLIENT, and vice versa). It is important to note that changes to this file will not automatically recompile, when changing the file you should save one of your scripts. Your editor script might do this automatically, for example it could call AssetDatabase.Refresh( ImportAssetOptions.ForceUpdate ).

Now that we can detect whether the application was built as a server or a client, we'll need some way for the server to act as autonomously as possible. The server should have a configuration file which allows the user to set, for example, network settings before the server runs. This book will not cover how to load the configuration file (XML or JSON are recommended), but once these are loaded the server should immediately initialize and register itself with the Master Server using the data in the configuration file (for example, server name, maximum connections, listen port, password, and so on).

Setting up a server console without Pro

Usually, a game server is a console application. This is nearly possible in Unity if you have purchased a Pro license, by appending the -batchmode argument to the executable (actually, Unity does not create a console window, instead the game simply runs in the background). If you do have Pro, feel free to skip this section. However, if you own a free license, you'll need to get a bit creative.

We want the server to use as few resources as possible. We can create a script that turns off rendering of the scene when running in server mode. This won't completely disable the rendering system (as running in command line would), but it does significantly reduce the GPU load of the server.

using UnityEngine;
using System.Collections;

public class DisableServerCamera : MonoBehavior
{
#if SERVER
  void Update()
  {
    // culling mask is a bitmask – setting all bits to zero means render nothing
    camera.cullingMask = 0;
  }
#endif
}

This script can be attached to a camera, and will cause that camera to not render anything when running on the server.

Next we're going to set up a console-type display for our server. This "console" will hook into the built-in Debug class and display a scrolling list of messages. We'll do this via Application.RegisterLogCallback.

using UnityEngine;
using System.Collections;
using System.Collections.Generic;

// contains data about the logged message
struct LogMessage
{
  public string message;
  public LogType type;
}

public class CustomLog : MonoBehavior
{
  // how many past log messages to store
  public int MaxHistory = 50;

  // a list of stored log messages
  private List<LogMessage> messages = new List<LogMessage>();

  // the position within the scroll view
  private Vector2 scrollPos = Vector2.zero;

  void OnEnable()
  {
    // register a custom log handler
    Application.RegisterLogCallback( HandleLog );
  }
  void OnDisable()
  {
    // unregister the log handler
    Application.RegisterLogCallback( null );
  }

  void OnGUI()
  {
    scrollPos = GUILayout.BeginScrollView( scrollPos, GUILayout.ExpandWidth( true ), GUILayout.ExpandHeight( true ) );

    //draw each debug log – switch colors based on log type
    for( int i = 0; i < messages.Count; i++ )
    {
      Color color = Color.white;
      if( messages[i].type == LogType.Warning )
      {
        color = Color.yellow;
      }
      else if( messages[i].type != LogType.Log )
      {
        color = Color.red;
      }

      GUI.color = color;
      GUILayout.Label( messages[i].message );
    }

    GUILayout.EndScrollView();
  }

  void HandleLog( string message, string stackTrace, LogType type )
  {
    // add the message, remove entries if there's too many
    LogMessage msg = new LogMessage();
    msg.message = message;
    msg.type = type;

    messages.Add( msg );
    
    if( messages.Count >= MaxHistory )
    {
      messages.RemoveAt( 0 );
    }
    // scroll to the newest message by setting to a huge amount
    // will automatically be clamped
    scrollPos.y = 1000f;
  }
}

Now the user can see the debug information being printed as the server runs—very useful indeed.

You should strive for as much code reuse as possible in fact, if your game allows players to host a game from inside the client, most of the same code will already work with a few minor differences:

  • As previously mentioned, the server starts up automatically with a configuration loaded from the user-editable files (unlike the client).
  • The server does not spawn any player objects of its own, unlike the client.
  • The server does not have any UIs or menus to display to the user beyond the log dump. Beyond starting up the server and shutting it down, there is zero interaction with the server application.

Loading networked levels

There are a few tricks to loading networked levels in the Unity game engine. If you just use Application.LoadLevel, you'll encounter a number of issues; specifically you may find that a client connecting to the game won't see any objects that were instantiated via Network.Instantiate. The reason for this is because the level loading process doesn't happen instantly—it actually takes two frames to complete. This occurs after the list of networked objects was received, so the load process will delete them.

Note that Application.LoadLevel is purely client side. Unity imposes no limitations on which level a client or server loads in a networked game. In fact, it's entirely possible that you might have different levels within a networked session, and this is what Network.SetLevelPrefix is for. Each of these levels is assigned some kind of "ID" that uniquely identifies the level. Before loading the level you would use Network.SetLevelPrefix. This essentially separates players into channels, so all players with level prefix 0 are separate from players with level prefix 1, for example.

Note that if your game needs all clients to load the same level, you'll have to ensure this yourself. If a client has a different level loaded than the host, without setting the level prefix to something different than the host, the client might see some odd situations, such as players floating or sunk into the ground (a player could be standing on a bridge in one level, and a different level at the same position might have a building; so the player would appear to be clipped into the building).

The correct way to load levels in a networked game, is to first disable the network queue, load the level, wait two frames, and then re-enable the network queue. This means any incoming messages will not be processed, and will instead be buffered until the new level has completely finished loading.

Let's write a simple network level loader that will handle all of these for us. It's designed as a singleton so we don't need one present in the scene (one will automatically be created):

using UnityEngine;
using System.Collections;

public class NetworkLevelLoader : MonoBehavior
  {
  // implements singleton-style behavior
  public static NetworkLevelLoader Instance
  {
    get
    {
      // no instance yet? Create a new one
      if( instance == null )
      {
        GameObject go = new GameObject( "_networkLevelLoader" );
        // hide it to avoid cluttering up the hieararchy
        go.hideFlags = HideFlags.HideInHierarchy;
        instance = go.AddComponent<NetworkLevelLoader>();

        // don't destroy it when a new scene loads
        GameObject.DontDestroyOnLoad( go );
      }
      return instance;
    }
  }
  private static NetworkLevelLoader instance;

  public void LoadLevel( string levelName, int prefix = 0 )
  {
    StopAllCoroutines();
    StartCoroutine( doLoadLevel( levelName, prefix ) );
  }

  // do the work of pausing the network queue, loading the level, waiting, and then unpausing
  IEnumerator doLoadLevel( string name, int prefix )
  {
    Network.SetSendingEnabled( 0, false );
    Network.isMessageQueueRunning = false;

    Network.SetLevelPrefix( prefix );
    Application.LoadLevel( name );
    yield return null;
    yield return null;

    Network.isMessageQueueRunning = true;
    Network.SetSendingEnabled( 0, true );
  }
}

You can now replace any calls to Application.LoadLevel with NetworkLevelLoader.Instance.LoadLevel. For example, the server might call an RPC which loads the level via the helper class we just wrote, as a buffered RPC so that all clients connecting will automatically load the level.

Note

If your server needs to change level during the connection, for example, in many FPS games players can vote on a new map at the end of a round, things get a bit more complicated. The server should first delete all networked objects belonging to players, remove RPCs from all players (via Network.RemoveRPCs), and then call the load-level RPC.

Creating a multiplayer Pong game

Now that we've covered the basics of using Unity Networking, we're going to apply them to creating a multiplayer Pong clone.

The game will play pretty much as standard Pong. Players can choose their name, and then view a list of open servers (full rooms will not be shown). Players can also host their own game.

Once in a game, players bounce a ball back and forth until it hits the opponent's side. Players get one point for this, and the ball will reset and continue bouncing. When a player hits 10 points, the winner is called, the scores are reset, and the game continues. While in a match with no other players, the server will inform the user to wait. If a player leaves, the match is reset (if the host leaves, the other player is automatically disconnected).

Preparing the Field

First, create a cube (by navigating to GameObject | Create Other | Cube) and scale it to 1 x 1 x 4. Name it Paddle and set the Tag to Player. Check the Is Trigger box on the collider.

Preparing the Field

Our ball will detect when it hits the trigger zone on the player paddle, and reverse direction. We use triggers because we don't necessarily want to simulate the ball realistically with the Unity physics engine (we get far less control over the ball's physics, and it may not behave exactly as we would like).

We will also line our playing field in trigger boxes. For these you can duplicate the paddle four times and form a large rectangle outlining the playing field. The actual size doesn't matter so much, as long as the ball has room to move around. We will add two more tags for these boundaries: Boundary and Goal. The two boxes on the top and bottom of the field are tagged as Boundary, the two boxes on the left and right are tagged as Goal.

Preparing the Field

When the ball hits a trigger tagged Boundary, it reverses its velocity along the z axis. When the ball hits a trigger tagged Player, it reverses its velocity along the x axis. And when a ball hits a trigger tagged Goal, the corresponding player gets a point and the ball resets.

Let's finish up the playing field before writing our code:

  1. Firstly, set the camera to Orthographic and position it at (0, 10, 0). Rotate it 90 degrees along the x axis until it points straight down, and change its Orthographic Size to a value large enough to frame the playing field (in my case, I set it to 15). Set the camera's background color to black.
  2. Create a directional light that points straight down. This will illuminate the paddles and ball to make them pure white.
  3. Finally, duplicate the player paddle and move it to the other half of the field.

The Ball script

Now we're going to create the Ball script. We'll add the multiplayer code later, for now this is offline only:

using UnityEngine;
using System.Collections;

public class Ball : MonoBehavior
{
  // the speed the ball starts with
  public float StartSpeed = 5f;

  // the maximum speed of the ball
  public float MaxSpeed = 20f;

  // how much faster the ball gets with each bounce
  public float SpeedIncrease = 0.25f;

  // the current speed of the ball
  private float currentSpeed;

  // the current direction of travel
  private Vector2 currentDir;

  // whether or not the ball is resetting
  private bool resetting = false;

  void Start()
  {
    // initialize starting speed
    currentSpeed = StartSpeed;

    // initialize direction
    currentDir = Random.insideUnitCircle.normalized;
  }

  void Update()
  {
    // don't move the ball if it's resetting
    if( resetting )
      return;

    // move the ball in the current direction
    Vector2 moveDir = currentDir * currentSpeed * Time.deltaTime;
    transform.Translate( new Vector3( moveDir.x, 0f, moveDir.y ) );
  }

  void OnTriggerEnter( Collider other )
  {
    if( other.tag == "Boundary" )
    {
      // vertical boundary, reverse Y direction
      currentDir.y *= -1;
    }
    else if( other.tag == "Player" )
    {
      // player paddle, reverse X direction
      currentDir.x *= -1;
    }
    else if( other.tag == "Goal" )
    {
      // reset the ball
      StartCoroutine( resetBall() );
      // inform goal of the score
      other.SendMessage( "GetPoint", SendMessageOptions.DontRequireReceiver );
    }

    // increase speed
    currentSpeed += SpeedIncrease;
    
    // clamp speed to maximum
    currentSpeed = Mathf.Clamp( currentSpeed, StartSpeed, MaxSpeed );
  }

  IEnumerator resetBall()
  {
    // reset position, speed, and direction
    resetting = true;
    transform.position = Vector3.zero;
    
    currentDir = Vector3.zero;
    currentSpeed = 0f;
    // wait for 3 seconds before starting the round
    yield return new WaitForSeconds( 3f );

Start();

    resetting = false;
  }
}

To create the ball, as before we'll create a cube. It will have the default scale of 1 x 1 x 1. Set the position to origin (0, 0, 0). Add a rigidbody component to the cube, untick the Use Gravity checkbox, and tick the Is Kinematic checkbox. The Rigidbody component is used to let our ball get the OnTriggerEnter events. Is Kinematic is enabled because we're controlling the ball ourselves, rather than using Unity's physics engine.

Add the new Ball component that we just created and test the game. It should look something like this:

The Ball script

You should see the ball bouncing around the field. If it hits either side, it will move back to the center of the field, pause for 3 seconds, and then begin moving again. This should happen fairly quickly, because the paddles aren't usable yet (the ball will often bounce right past them).

The Paddle script

Let's add player control to the mix. Note that at the moment player paddles will both move in tandem, with the same controls. This is OK, later we'll disable the player input based on whether or not the network view belongs to the local client (this is what the AcceptsInput field is for):

using UnityEngine;
using System.Collections;

public class Paddle : MonoBehavior
{
  // how fast the paddle can move
  public float MoveSpeed = 10f;

  // how far up and down the paddle can move
  public float MoveRange = 10f;

  // whether this paddle can accept player input
  public bool AcceptsInput = true;

  void Update()
  {
    // does not accept input, abort
    if( !AcceptsInput )
      return;

    //get user input
    float input = Input.GetAxis( "Vertical" );
    
    // move paddle
    Vector3 pos = transform.position;
    pos.z += input * MoveSpeed * Time.deltaTime;

    // clamp paddle position
    pos.z = Mathf.Clamp( pos.z, -MoveRange, MoveRange );

    // set position
    transform.position = pos;
  }
}

You can now move the paddles up and down, and bounce the ball back and forth. The ball will slowly pick up speed as it bounces, until it hits either of the goals. When that happens, the round resets.

Keeping score

What we're going to do now is create a scorekeeper. The scorekeeper will keep track of both players' scores, and will later keep track of other things, such as whether we're waiting for another player to join:

using UnityEngine;
using System.Collections;

public class Scorekeeper : MonoBehavior
{
  // the maximum score a player can reach
  public int ScoreLimit = 10;

  // Player 1's score
  private int p1Score = 0;

  // Player 2's score
  private int p2Score = 0;

  // give the appropriate player a point
  public void AddScore( int player )
  {
    // player 1
    if( player == 1 )
    {
      p1Score++;
    }
    // player 2
    else if( player == 2 )
    {
      p2Score++;
    }

    // check if either player reached the score limit
    if( p1Score >= ScoreLimit || p2Score >= ScoreLimit )
    {
      // player 1 has a better score than player 2
      if( p1score > p2score )
        Debug.Log( "Player 1 wins" );
      // player 2 has a better score than player 1
      if( p2score > p1score )
        Debug.Log( "Player 2 wins" );
      // both players have the same score - tie
      else
        Debug.Log( "Players are tied" );

      // reset scores and start over
      p1Score = 0;
      p2Score = 0;
    }
  }
}

Now our scorekeeper can keep score for each player, let's make the goals and add points with a Goal script. It's a very simple script, which reacts to the GetPoint message sent from the ball upon collision to give the other player a point:

using UnityEngine;
using System.Collections;

public class Goal : MonoBehavior
{
  // the player who gets a point for this goal, 1 or 2
  public int Player = 1;

  // the Scorekeeper
  public Scorekeeper scorekeeper;

  public void GetPoint()
  {
    // when the ball collides with this goal, give the player a point
    scorekeeper.AddScore( Player );
  }
}

Attach this script to both goals. For player 1's goal, set the Player to 2 (player 2 gets a point when the ball lands in player 1's goal), for player 2's goal, set the Player to 1 (player 1 gets a point when the ball lands in player 2's goal).

The game is almost completely functional now (aside from multiplayer). One problem is that we can't tell that points are being given until the game ends, so let's add a score display.

Displaying the score to the player

Create two 3D Text objects as children of the scorekeeper. Name them p1Score and P2Score, and position them on each side of the field:

Displaying the score to the player

Let's make the scorekeeper display the player scores:

using UnityEngine;
using System.Collections;

public class Scorekeeper : MonoBehavior
{
  // the maximum score a player can reach
  public int ScoreLimit = 10;

  // the display test for player 1's score
  public TextMesh Player1ScoreDisplay;

  // the display text for player 2's score
  public TextMesh Player2ScoreDisplay;

  // Player 1's score
  private int p1Score = 0;

  // Player 2's score
  private int p2Score = 0;

  // give the appropriate player a point
  public void AddScore( int player )
  {
    // player 1
    if( player == 1 )
    {
      p1Score++;
    }
    // player 2
    else if( player == 2 )
    {
      p2Score++;
    }

    // check if either player reached the score limit
    if( p1Score >= ScoreLimit || p2Score >= ScoreLimit )
    {
      // player 1 has a better score than player 2
      if( p1Score > p2Score )
        Debug.Log( "Player 1 wins" );
      // player 2 has a better score than player 1
      if( p2Score > p1Score )
        Debug.Log( "Player 2 wins" );
      // both players have the same score - tie
      else
        Debug.Log( "Players are tied" );

      // reset scores and start over
      p1Score = 0;
      p2Score = 0;
    }

    // display each player's score
    Player1ScoreDisplay.text = p1Score.ToString();
 
   Player2ScoreDisplay.text = p2Score.ToString();
  }
}

The score is now displayed properly when a player gets a point. Be sure to give it a test run—the ball should bounce around the field, and you should be able to deflect the ball with the paddle. If the ball hits player 1's goal, player 2 should get 1 point, and vice versa. If one player gets 10 points, both scores should reset to zero, the ball should move back to the center of the screen, and the game should restart.

With the most important gameplay elements complete, we can start working on multiplayer networking.

Networking the game

For testing purposes, let's launch a network game as soon as the level is launched:

using UnityEngine;
using System.Collections;

public class RequireNetwork : MonoBehavior
{
  void Awake()
  {
    if( Network.peerType == NetworkPeerType.Disconnected )
      Network.InitializeServer( 1, 25005, true );
  }
}

If we start this level without hosting a server first, it will automatically do so for us in ensuring that the networked code still works.

Now we can start converting our code to work in multiplayer.

Let's start by networking the paddle code:

using UnityEngine;
using System.Collections;

public class Paddle : MonoBehavior
{
  // how fast the paddle can move
  public float MoveSpeed = 10f;

  // how far up and down the paddle can move
  public float MoveRange = 10f;

  // whether this paddle can accept player input
  public bool AcceptsInput = true;
  // the position read from the network
  // used for interpolation
  private Vector3 readNetworkPos;

  void Start()
  {
    // if this is our paddle, it accepts input
    // otherwise, if it is someone else's paddle, it does not
    AcceptsInput = networkView.isMine;
  }

  void Update()
  {
    // does not accept input, interpolate network pos
    if( !AcceptsInput )
    {
      transform.position = Vector3.Lerp( transform.position, readNetworkPos, 10f * Time.deltaTime );

      // don't use player input
      return;
    }

    //get user input
    float input = Input.GetAxis( "Vertical" );
    
    // move paddle
    Vector3 pos = transform.position;
    pos.z += input * MoveSpeed * Time.deltaTime;

    // clamp paddle position
    pos.z = Mathf.Clamp( pos.z, -MoveRange, MoveRange );

    // set position
    transform.position = pos;
  }

  void OnSerializeNetworkView( BitStream stream )
  {
    // writing information, push current paddle position
    if( stream.isWriting )
    {
      Vector3 pos = transform.position;
      stream.Serialize( ref pos );
    }
    // reading information, read paddle position
    else
    {
      Vector3 pos = Vector3.zero;
      stream.Serialize( ref pos );
      readNetworkPos = pos;
    }
  }
}

The paddle will detect whether it is owned by the local player or not. If not, it will not accept player input, instead it will interpolate its position to the last read position value over the network.

By default, network views will serialize the attached transform. This is OK for testing, but should not be used for production. Without any interpolation, the movement will appear very laggy and jerky, as positions are sent a fixed number of times per second (15 by default in Unity Networking) in order to save on bandwidth, so snapping to the position 15 times per second will look jerky. In order to solve this, rather than instantly snapping to the new position we smoothly interpolate towards it. In this case, we use the frame delta multiplied by a number (larger is faster, smaller is slower), which produces an easing motion; the object starts quickly approaching the target value, slowing down as it gets closer.

When serializing, it either reads the position and stores it, or it sends the current transform position, depending on whether the stream is for reading or for writing.

Now, add a Network View to one of your paddles, drag the panel component attached to the Paddle into the Observed slot, and make it a prefab by dragging it into your Project pane.

Next, delete the paddles in the scene, and create two empty game objects where the paddles used to be positioned. These will be the starting points for each paddle when spawned.

Spawning paddles

Next, let's make the scorekeeper spawn these paddles. The scorekeeper, upon a player connecting, will send an RPC to them to spawn a paddle:

using UnityEngine;
using System.Collections;

public class Scorekeeper : MonoBehavior
{
  // the maximum score a player can reach
  public int ScoreLimit = 10;

  // the start points for each player paddle
  public Transform SpawnP1;
  public Transform SpawnP2;

  // the paddle prefab
  public GameObject paddlePrefab;

  // the display test for player 1's score
  public TextMesh Player1ScoreDisplay;

  // the display text for player 2's score
  public TextMesh Player2ScoreDisplay;

  // Player 1's score
  private int p1Score = 0;

  // Player 2's score
  private int p2Score = 0;

  void Start()
  {
    if( Network.isServer )
    {
      // server doesn't trigger OnPlayerConnected, manually spawn
      Network.Instantiate( paddlePrefab, SpawnP1.position, Quaternion.identity, 0 );
    }
  }

void OnPlayerConnected( NetworkPlayer player )
  {
    // when a player joins, tell them to spawn
    networkView.RPC( "net_DoSpawn", player, SpawnP2.position );
  }

  [RPC]
  void net_DoSpawn( Vector3 position )
  {
    // spawn the player paddle
    Network.Instantiate( paddlePrefab, position, Quaternion.identity, 0 );
  }

  // give the appropriate player a point
  public void AddScore( int player )
  {
    // player 1
    if( player == 1 )
    {
      p1Score++;
    }
    // player 2
    else if( player == 2 )
    {
      p2Score++;
    }

    // check if either player reached the score limit
    if( p1Score >= ScoreLimit || p2Score >= ScoreLimit )
    {
      // player 1 has a better score than player 2
      if( p1Score > p2Score )
        Debug.Log( "Player 1 wins" );
      // player 2 has a better score than player 1
      if( p2Score > p1Score )
        Debug.Log( "Player 2 wins" );
      // both players have the same score - tie
      else
        Debug.Log( "Players are tied" );

      // reset scores and start over
      p1Score = 0;
      p2Score = 0;
    }

    // display each player's score
    Player1ScoreDisplay.text = p1Score.ToString();
    Player2ScoreDisplay.text = p2Score.ToString();
  }
}

At the moment, when you start the game, one paddle spawns for player 1, but player 2 is missing (there's nobody else playing). However, the ball eventually flies off toward player 2's side, and gives player 1 a free point.

The networked ball

Let's keep the ball frozen in place when there's nobody to play against, or if we aren't the server. We're also going to add networked movement to our ball:

using UnityEngine;
using System.Collections;

public class Ball : MonoBehavior
{
  // the speed the ball starts with
  public float StartSpeed = 5f;

  // the maximum speed of the ball
  public float MaxSpeed = 20f;

  // how much faster the ball gets with each bounce
  public float SpeedIncrease = 0.25f;

  // the current speed of the ball
  private float currentSpeed;

  // the current direction of travel
  private Vector2 currentDir;

  // whether or not the ball is resetting
  private bool resetting = false;

  void Start()
  {
    // initialize starting speed
    currentSpeed = StartSpeed;

    // initialize direction
    currentDir = Random.insideUnitCircle.normalized;
  }

  void Update()
  {
    // don't move the ball if it's resetting
    if( resetting )
      return;

    // don't move the ball if there's nobody to play with
    if( Network.connections.Length == 0 )
      return;

    // move the ball in the current direction
    Vector2 moveDir = currentDir * currentSpeed * Time.deltaTime;
    transform.Translate( new Vector3( moveDir.x, 0f, moveDir.y ) );
  }

  void OnTriggerEnter( Collider other )
  {
    // bounce off the top and bottom walls
    if( other.tag == "Boundary" )
    {
      // vertical boundary, reverse Y direction
      currentDir.y *= -1;
    }
    // bounce off the player paddle
    else if( other.tag == "Player" )
    {
      // player paddle, reverse X direction
      currentDir.x *= -1;
    }
    // if we hit a goal, and we are the server, give the appropriate player a point
    else if( other.tag == "Goal" && Network.isServer )
    {
      // reset the ball
      StartCoroutine( resetBall() );
      // inform goal of the score
      other.SendMessage( "GetPoint", SendMessageOptions.DontRequireReceiver );
    }

    // increase speed
    currentSpeed += SpeedIncrease;
    
    // clamp speed to maximum
    currentSpeed = Mathf.Clamp( currentSpeed, StartSpeed, MaxSpeed );
  }
  IEnumerator resetBall()
  {
    // reset position, speed, and direction
    resetting = true;
    transform.position = Vector3.zero;

    currentDir = Vector3.zero;
    currentSpeed = 0f;

    // wait for 3 seconds before starting the round
    yield return new WaitForSeconds( 3f );

    Start();

    resetting = false;
  }

  void OnSerializeNetworkView( BitStream stream )
  {
    //write position, direction, and speed to network
    if( stream.isWriting )
    {
      Vector3 pos = transform.position;  
      Vector3 dir = currentDir;
      float speed = currentSpeed;
      stream.Serialize( ref pos );
      stream.Serialize( ref dir );
      stream.Serialize( ref speed );
    }
    // read position, direction, and speed from network
    else
    {
      Vector3 pos = Vector3.zero;
      Vector3 dir = Vector3.zero;
      float speed = 0f;
      stream.Serialize( ref pos );
      stream.Serialize( ref dir );
      stream.Serialize( ref speed );
      transform.position = pos;
      currentDir = dir;
      currentSpeed = speed;  
    }
  }
}

The ball will stay put if there's nobody to play against, and if someone we're playing against leaves, the ball will reset to the middle of the field. The ball will also work correctly on multiple machines at once (it is simulated on the server, and position/velocity is relayed to clients). Add NetworkView to the ball and have it observe the Ball component.

Networked scorekeeping

There is one final piece of the puzzle that is keeping score. We're going to convert our AddScore function to use an RPC, and if a player leaves we will also reset the scores:

using UnityEngine;
using System.Collections;

public class Scorekeeper : MonoBehavior
{
  // the maximum score a player can reach
  public int ScoreLimit = 10;

  // the start points for each player paddle
  public Transform SpawnP1;
  public Transform SpawnP2;

  // the paddle prefab
  public GameObject paddlePrefab;

  // the display test for player 1's score
  public TextMesh Player1ScoreDisplay;

  // the display text for player 2's score
  public TextMesh Player2ScoreDisplay;

  // Player 1's score
  private int p1Score = 0;

  // Player 2's score
  private int p2Score = 0;

  void Start()
  {
    if( Network.isServer )
    {
      // server doesn't trigger OnPlayerConnected, manually spawn
      Network.Instantiate( paddlePrefab, SpawnP1.position, Quaternion.identity, 0 );

      // nobody has joined yet, display "Waiting..." for player 2
      Player2ScoreDisplay.text = "Waiting...";
    }
  }

void OnPlayerConnected( NetworkPlayer player )
  {
    // when a player joins, tell them to spawn
    networkView.RPC( "net_DoSpawn", player, SpawnP2.position );

    // change player 2's score display from "waiting..." to "0"
    Player2ScoreDisplay.text = "0";
  }

  void OnPlayerDisconnected( NetworkPlayer player )
  {
    // player 2 left, reset scores
    p1Score = 0;
    p2Score = 0;

    // display each player's scores
    // display "Waiting..." for player 2
    Player1ScoreDisplay.text = p1Score.ToString();
    Player2ScoreDisplay.text = "Waiting...";
  }

  void OnDisconnectedFromServer( NetworkDisconnection cause )
  {
    // go back to the main menu
    Application.LoadLevel( "Menu" );
  }

  [RPC]
  void net_DoSpawn( Vector3 position )
  {
    // spawn the player paddle
    Network.Instantiate( paddlePrefab, position, Quaternion.identity, 0 );
  }
  // call an RPC to give the player a point
  public void AddScore( int player )
  {
    networkView.RPC( "net_AddScore", RPCMode.All, player );
  }

  // give the appropriate player a point
  [RPC]
  public void net_AddScore( int player )
  {
    // player 1
    if( player == 1 )
    {
      p1Score++;
    }
    // player 2
    else if( player == 2 )
    {
      p2Score++;
    }

    // check if either player reached the score limit
    if( p1Score >= ScoreLimit || p2Score >= ScoreLimit )
    {
      // player 1 has a better score than player 2
      if( p1Score > p2Score )
        Debug.Log( "Player 1 wins" );
      // player 2 has a better score than player 1
      if( p2Score > p1Score )
        Debug.Log( "Player 2 wins" );
      // both players have the same score - tie
      else
        Debug.Log( "Players are tied" );

      // reset scores and start over
      p1Score = 0;
      p2Score = 0;
    }

    // display each player's score
    Player1ScoreDisplay.text = p1Score.ToString();
    Player2ScoreDisplay.text = p2Score.ToString();
  }
}

Our game is fully networked at this point. The only problem is that we do not yet have a way to connect to the game. Let's write a simple direct connect dialog which allows players to enter an IP address to join.

Note

With direct IP connect, note that NAT punch-through is not possible. When you use the Master Server, you can pass either HostData or GUID of a host which will perform NAT punch-through.

The Connect screen

The following script shows the player IP and Port entry fields, and the Connect and Host buttons. The player can directly connect to an IP and Port, or start a server on the given Port. By using direct connect we don't need to rely on a master server, as players directly connect to games via IP. If you wanted to, you could easily create a lobby screen for this instead of using direct connect (allowing players to browse a list of running servers instead of manually typing IP address). To keep things simpler, we'll omit the lobby screen in this example:

using UnityEngine;
using System.Collections;

public class ConnectToGame : MonoBehavior
{
  private string ip = "";
  private int port = 25005;

  void OnGUI()
  {
    // let the user enter IP address
    GUILayout.Label( "IP Address" );
    ip = GUILayout.TextField( ip, GUILayout.Width( 200f ) );

    // let the user enter port number
    // port is an integer, so only numbers are allowed
    GUILayout.Label( "Port" );
    string port_str = GUILayout.TextField( port.ToString(), GUILayout.Width( 100f ) );
    int port_num = port;
    if( int.TryParse( port_str, out port_num ) )
      port = port_num;
    // connect to the IP and port
    if( GUILayout.Button( "Connect", GUILayout.Width( 100f ) ) )
    {
      Network.Connect( ip, port );
    }

    // host a server on the given port, only allow 1 incoming connection (one other player)
    if( GUILayout.Button( "Host", GUILayout.Width( 100f ) ) )
    {
      Network.InitializeServer( 1, port, true );
    }
  }

  void OnConnectedToServer()
  {
    Debug.Log( "Connected to server" );
    // this is the NetworkLevelLoader we wrote earlier in the chapter – pauses the network, loads the level, waits for the level to finish, and then unpauses the network
    NetworkLevelLoader.Instance.LoadLevel( "Game" );
  }

  void OnServerInitialized()
  {
    Debug.Log( "Server initialized" );
    NetworkLevelLoader.Instance.LoadLevel( "Game" );
  }
}

With this, we now have a complete, fully functional multiplayer Pong game. Players can host games, as well as join them if they know the IP.

When in a game as the host, the game will wait for another player to show up before starting the game. If the other player leaves, the game will reset and wait again. As a player, if the host leaves it goes back to the main menu.

Left arrow icon Right arrow icon

Key benefits

  • Create a variety of multiplayer games and apps in the Unity 4 game engine, still maintaining compatibility with Unity 3.
  • Employ the most popular networking middleware options for Unity games
  • Packed with ideas, inspiration, and advice for your own game design and development

Description

Unity is a game development engine that is fully integrated with a complete set of intuitive tools and rapid workflows used to create interactive 3D content. Multiplayer games have long been a staple of video games, and online multiplayer games have seen an explosion in popularity in recent years. Unity provides a unique platform for independent developers to create the most in-demand multiplayer experiences, from relaxing social MMOs to adrenaline-pumping competitive shooters. A practical guide to writing a variety of online multiplayer games with the Unity game engine, using a multitude of networking middleware from player-hosted games to standalone dedicated servers to cloud multiplayer technology. You can create a wide variety of online games with the Unity 4 as well as Unity 3 Engine. You will learn all the skills needed to make any multiplayer game you can think of using this practical guide. We break down complex multiplayer games into basic components, for different kinds of games, whether they be large multi-user environments or small 8-player action games. You will get started by learning networking technologies for a variety of situations with a Pong game, and also host a game server and learn to connect to it.Then, we will show you how to structure your game logic to work in a multiplayer environment. We will cover how to implement client-side game logic for player-hosted games and server-side game logic for MMO-style games, as well as how to deal with network latency, unreliability, and security. You will then gain an understanding of the Photon Server while creating a star collector game; and later, the Player.IO by creating a multiplayer RTS prototype game. You will also learn using PubNub with Unity by creating a chatbox application. Unity Multiplayer Games will help you learn how to use the most popular networking middleware available for Unity, from peer-oriented setups to dedicated server technology.

Who is this book for?

If you are a developer who wants to start making multiplayer games with the Unity game engine, this book is for you. This book assumes you have some basic experience with programming. No prior knowledge of the Unity IDE is required.

What you will learn

  • Use Unity networking for in-game player-hosted servers
  • Create cloud-based games with Photon Cloud
  • Employ dedicated servers for massive multiuser environments
  • Make game logic server-authoritative
  • Deal with latency and unreliable networks
  • Use PubNub for HTTP-based push messaging
  • Employ Player.IO to persist game data to the cloud
  • Use various forms of networked entity interpolation
Estimated delivery fee Deliver to Norway

Standard delivery 10 - 13 business days

€11.95

Premium delivery 3 - 6 business days

€16.95
(Includes tracking information)

Product Details

Country selected
Publication date, Length, Edition, Language, ISBN-13
Publication date : Dec 05, 2013
Length: 242 pages
Edition : 1st
Language : English
ISBN-13 : 9781849692328
Vendor :
Unity Technologies
Languages :
Tools :

What do you get with Print?

Product feature icon Instant access to your digital eBook copy whilst your Print order is Shipped
Product feature icon Paperback book shipped to your preferred address
Product feature icon Download this book in EPUB and PDF formats
Product feature icon Access this title in our online reader with advanced features
Product feature icon DRM FREE - Read whenever, wherever and however you want
OR
Modal Close icon
Payment Processing...
tick Completed

Shipping Address

Billing Address

Shipping Methods
Estimated delivery fee Deliver to Norway

Standard delivery 10 - 13 business days

€11.95

Premium delivery 3 - 6 business days

€16.95
(Includes tracking information)

Product Details

Publication date : Dec 05, 2013
Length: 242 pages
Edition : 1st
Language : English
ISBN-13 : 9781849692328
Vendor :
Unity Technologies
Languages :
Tools :

Packt Subscriptions

See our plans and pricing
Modal Close icon
€18.99 billed monthly
Feature tick icon Unlimited access to Packt's library of 7,000+ practical books and videos
Feature tick icon Constantly refreshed with 50+ new titles a month
Feature tick icon Exclusive Early access to books as they're written
Feature tick icon Solve problems while you work with advanced search and reference features
Feature tick icon Offline reading on the mobile app
Feature tick icon Simple pricing, no contract
€189.99 billed annually
Feature tick icon Unlimited access to Packt's library of 7,000+ practical books and videos
Feature tick icon Constantly refreshed with 50+ new titles a month
Feature tick icon Exclusive Early access to books as they're written
Feature tick icon Solve problems while you work with advanced search and reference features
Feature tick icon Offline reading on the mobile app
Feature tick icon Choose a DRM-free eBook or Video every month to keep
Feature tick icon PLUS own as many other DRM-free eBooks or Videos as you like for just €5 each
Feature tick icon Exclusive print discounts
€264.99 billed in 18 months
Feature tick icon Unlimited access to Packt's library of 7,000+ practical books and videos
Feature tick icon Constantly refreshed with 50+ new titles a month
Feature tick icon Exclusive Early access to books as they're written
Feature tick icon Solve problems while you work with advanced search and reference features
Feature tick icon Offline reading on the mobile app
Feature tick icon Choose a DRM-free eBook or Video every month to keep
Feature tick icon PLUS own as many other DRM-free eBooks or Videos as you like for just €5 each
Feature tick icon Exclusive print discounts

Frequently bought together


Stars icon
Total 116.97
Unity 4.x Game Development by Example: Beginner's Guide
€37.99
Unity Multiplayer Games
€36.99
Unity Character Animation with Mecanim
€41.99
Total 116.97 Stars icon
Banner background image

Table of Contents

8 Chapters
1. Unity Networking – The Pong Game Chevron down icon Chevron up icon
2. Photon Unity Networking – The Chat Client Chevron down icon Chevron up icon
3. Photon Server – Star Collector Chevron down icon Chevron up icon
4. Player.IO – Bot Wars Chevron down icon Chevron up icon
5. PubNub – The Global Chatbox Chevron down icon Chevron up icon
6. Entity Interpolation and Prediction Chevron down icon Chevron up icon
7. Server-side Hit Detection Chevron down icon Chevron up icon
Index Chevron down icon Chevron up icon

Customer reviews

Top Reviews
Rating distribution
Full star icon Full star icon Full star icon Half star icon Empty star icon 3.6
(11 Ratings)
5 star 27.3%
4 star 45.5%
3 star 0%
2 star 18.2%
1 star 9.1%
Filter icon Filter
Top Reviews

Filter reviews by




Ed Smith Mar 14, 2014
Full star icon Full star icon Full star icon Full star icon Full star icon 5
As a lone Unity 3D Independent Game Developer, I often struggle with the enormity of tasks required to conceptualize, build, release, and market a successful game or app. Being self-taught, I don’t have practical experience in any one field that I fall back on… I learn as i go. That also applies for funding my projects. There are no bank loans, no investors, no pooling of financial resources other than what I skim off the top of the family income that pays the bills and feeds the kids.So, I’m pretty much just like 98% of the people reading this right now…Earlier this year, I was given the opportunity by Packt Publishing to review their recently release book, Unity Multiplayer Games, and I jumped at the chance! Creating Multiplayer functionality in my games has always been a goal, but seemed such a monumental task. I had previously held off on tackling that topic while I focused on refining my knowledge of Javascript, C#, 3D Modeling & Animation, 2D Graphics, GUI Programming, etc. Having created several PC, Web, and Mobile games over the past 6 months, learning more and more with each new project, i decided that it was about time I step it up a notch and expose myself to the Mystic Voodoo that was Networked Game Building.I’m always hesitant to purchase these books… many just teach you how to build that one specific type or style of game, or “Insert the Code from the Sample Project” and don’t really explain what blocks of code do or how we can use them, which is what we really need to learn.Within Unity Multiplayer Games, I was relieved to find that this was not the case. In fact, there are FOUR different methods of adding multi-player functionality to FOUR different example games! Methods include integrating systems such as Unity’s own built in Networking API, Exit Games Photon Unity Networking API, Photon Server, Player.IO Cloud Solutions, and PubNub’s HTTP system.In addition to the well laid out, highly detailed, and thoroughly explained examples above, the book also helps clear up topics such as Entity Interpolation, Prediction, and Hit Detection, the lifeblood of any multiplayer game!Having only a passing familiarity with business servers and networking, I was able to easily follow the information, explanations, and instructions given. The very few times I thought I might not be “getting it” a quick Google Search of that specific topic answered my questions.Remember kids; “Google It Before Asking It!”Personally, I found this book both informative and invaluable in assisting my understanding of a topic I was hesitant to jump into. I can’t wait to add an entirely new dimension of functionality and diversity to my game projects!
Amazon Verified review Amazon
Lex Jan 20, 2015
Full star icon Full star icon Full star icon Full star icon Full star icon 5
I am currently using this book as a textbook for one of my classes and I can say that it is easier to understand than many of my other textbooks
Amazon Verified review Amazon
Rusty Jan 09, 2014
Full star icon Full star icon Full star icon Full star icon Full star icon 5
I've been a professional software developer for just over twenty years now, and in that time I've been involved in a number of projects ranging from simple single-user desktop utilities to high-concurrency/high-availability enterprise solutions used by some of the nation's largest equipment manufacturers, and most recently independent game development. I've worked with a large variety of tools, programming languages, development environments, and target platforms.One of the important lessons I've learned during all of this is that the right book can help immeasurably in getting up to speed quickly when working with something new. In my opinion, Unity Multiplayer Games is one such book.It's not an exhaustive reference, but it does cover everything you need to get started with multiplayer networking quickly. It covers not only Unity's built-in networking infrastructure, but also the most popular third-party libraries (all of which are free or have free or trial versions). There's some overlap in some of the third-party libraries that are covered, but the book also includes coverage of non-overlapping and complementary libraries for requirements such as storing game data remotely.If you are looking for a good treatment on multiplayer networking for Unity, I highly recommend this book.
Amazon Verified review Amazon
Magnus Mar 03, 2014
Full star icon Full star icon Full star icon Full star icon Empty star icon 4
If you are unsure where to start with networking in Unity, this book is a great book for you.While the approach is fairly straight-forward, and accessible; the advanced user will find a few things lacking.This book covers a number of 3rd party tools, the life-blood of Unity3d, and gives fairly simple instruction on using them.There are a few gotchas:The first of which is that the ‘Built-in’ unity networking, Raknet, is out of date in unity 4.x and requires quite a bit of work to use in practice… to use the newest RAKnet packages requires a Source licence for Unity3d, not something the beginner has or wants.The second of which is that there are very little Advance topics and material in this book, Making the leap from this book to more advanced networking, less experienced users may have difficulty.Overall, I think this is a good place to start if you are a beginner, however you will need additional intermediate material, before becoming comfortable with advance multiplayer techniques.
Amazon Verified review Amazon
Rock Feb 17, 2014
Full star icon Full star icon Full star icon Full star icon Empty star icon 4
Unity Multiplayer Games by Alan Stagner, published by Packt Publishing is a really interesting and relevant read for anyone interested in multiplayer game programming. Even if you are not the biggest Unity buff, the book explores a lot of great concepts that are critical to designing and implementing multiplayer video games. It even talks about Source engine and how popular games like Titanfall and CounterStrike use interpolation.Anyone familiar with Unity 3 or 4 and basic C# can pick up this book and learn some very awesome skills that are relevant to the games industry. As someone who is currently just getting started in game development professionally, I can attest the topics covered in this book are very relevant. With the way the industry is moving progressively to an “always-online, always-connected” state, these skills are critical to have if you want to stand out while looking for a programming or design job.Right from chapter 1, you start with UDP client to client design and implementation using the native Unity networking API. The book does a great job explaining why this primitive technique would be used over others, and does so for every type thereafter. This is critical because there is no silver bullet for online video games. Every game is different and has different requirements for gameplay. Some games take turns, other games do not need super low latency communication, while other games are high-paced and are much better with each second of delay shaved off. The book explains these trade offs well. In this case, client/client (peer to peer) communication is nice due to low latency but has a myriad of inherent problems such as the ability of the host client being able to change data on his end and cheating. The book accomplishes what it sets out to do: explore various relevant networking techniques for video games and lay a great foundation for you to get your multiplayer project started and up off the ground.Pros-Highly relevant topics for game design today and the future-Good explanations of why each method would be used over the others-Demonstrates how to get each method up and running in Unity C#, even if a little barebones-Awesome Pong tutorial acts as a foundation for any kind of project-Do not need Unity ProCons-Not many screenshots or proper details to the tutorials-Doesn’t always explain terminology-Arguably poor code writing ethic such as naming conventions, typos-Lack of a overall conclusion
Amazon Verified review Amazon
Get free access to Packt library with over 7500+ books and video courses for 7 days!
Start Free Trial

FAQs

What is the delivery time and cost of print book? Chevron down icon Chevron up icon

Shipping Details

USA:

'

Economy: Delivery to most addresses in the US within 10-15 business days

Premium: Trackable Delivery to most addresses in the US within 3-8 business days

UK:

Economy: Delivery to most addresses in the U.K. within 7-9 business days.
Shipments are not trackable

Premium: Trackable delivery to most addresses in the U.K. within 3-4 business days!
Add one extra business day for deliveries to Northern Ireland and Scottish Highlands and islands

EU:

Premium: Trackable delivery to most EU destinations within 4-9 business days.

Australia:

Economy: Can deliver to P. O. Boxes and private residences.
Trackable service with delivery to addresses in Australia only.
Delivery time ranges from 7-9 business days for VIC and 8-10 business days for Interstate metro
Delivery time is up to 15 business days for remote areas of WA, NT & QLD.

Premium: Delivery to addresses in Australia only
Trackable delivery to most P. O. Boxes and private residences in Australia within 4-5 days based on the distance to a destination following dispatch.

India:

Premium: Delivery to most Indian addresses within 5-6 business days

Rest of the World:

Premium: Countries in the American continent: Trackable delivery to most countries within 4-7 business days

Asia:

Premium: Delivery to most Asian addresses within 5-9 business days

Disclaimer:
All orders received before 5 PM U.K time would start printing from the next business day. So the estimated delivery times start from the next day as well. Orders received after 5 PM U.K time (in our internal systems) on a business day or anytime on the weekend will begin printing the second to next business day. For example, an order placed at 11 AM today will begin printing tomorrow, whereas an order placed at 9 PM tonight will begin printing the day after tomorrow.


Unfortunately, due to several restrictions, we are unable to ship to the following countries:

  1. Afghanistan
  2. American Samoa
  3. Belarus
  4. Brunei Darussalam
  5. Central African Republic
  6. The Democratic Republic of Congo
  7. Eritrea
  8. Guinea-bissau
  9. Iran
  10. Lebanon
  11. Libiya Arab Jamahriya
  12. Somalia
  13. Sudan
  14. Russian Federation
  15. Syrian Arab Republic
  16. Ukraine
  17. Venezuela
What is custom duty/charge? Chevron down icon Chevron up icon

Customs duty are charges levied on goods when they cross international borders. It is a tax that is imposed on imported goods. These duties are charged by special authorities and bodies created by local governments and are meant to protect local industries, economies, and businesses.

Do I have to pay customs charges for the print book order? Chevron down icon Chevron up icon

The orders shipped to the countries that are listed under EU27 will not bear custom charges. They are paid by Packt as part of the order.

List of EU27 countries: www.gov.uk/eu-eea:

A custom duty or localized taxes may be applicable on the shipment and would be charged by the recipient country outside of the EU27 which should be paid by the customer and these duties are not included in the shipping charges been charged on the order.

How do I know my custom duty charges? Chevron down icon Chevron up icon

The amount of duty payable varies greatly depending on the imported goods, the country of origin and several other factors like the total invoice amount or dimensions like weight, and other such criteria applicable in your country.

For example:

  • If you live in Mexico, and the declared value of your ordered items is over $ 50, for you to receive a package, you will have to pay additional import tax of 19% which will be $ 9.50 to the courier service.
  • Whereas if you live in Turkey, and the declared value of your ordered items is over € 22, for you to receive a package, you will have to pay additional import tax of 18% which will be € 3.96 to the courier service.
How can I cancel my order? Chevron down icon Chevron up icon

Cancellation Policy for Published Printed Books:

You can cancel any order within 1 hour of placing the order. Simply contact customercare@packt.com with your order details or payment transaction id. If your order has already started the shipment process, we will do our best to stop it. However, if it is already on the way to you then when you receive it, you can contact us at customercare@packt.com using the returns and refund process.

Please understand that Packt Publishing cannot provide refunds or cancel any order except for the cases described in our Return Policy (i.e. Packt Publishing agrees to replace your printed book because it arrives damaged or material defect in book), Packt Publishing will not accept returns.

What is your returns and refunds policy? Chevron down icon Chevron up icon

Return Policy:

We want you to be happy with your purchase from Packtpub.com. We will not hassle you with returning print books to us. If the print book you receive from us is incorrect, damaged, doesn't work or is unacceptably late, please contact Customer Relations Team on customercare@packt.com with the order number and issue details as explained below:

  1. If you ordered (eBook, Video or Print Book) incorrectly or accidentally, please contact Customer Relations Team on customercare@packt.com within one hour of placing the order and we will replace/refund you the item cost.
  2. Sadly, if your eBook or Video file is faulty or a fault occurs during the eBook or Video being made available to you, i.e. during download then you should contact Customer Relations Team within 14 days of purchase on customercare@packt.com who will be able to resolve this issue for you.
  3. You will have a choice of replacement or refund of the problem items.(damaged, defective or incorrect)
  4. Once Customer Care Team confirms that you will be refunded, you should receive the refund within 10 to 12 working days.
  5. If you are only requesting a refund of one book from a multiple order, then we will refund you the appropriate single item.
  6. Where the items were shipped under a free shipping offer, there will be no shipping costs to refund.

On the off chance your printed book arrives damaged, with book material defect, contact our Customer Relation Team on customercare@packt.com within 14 days of receipt of the book with appropriate evidence of damage and we will work with you to secure a replacement copy, if necessary. Please note that each printed book you order from us is individually made by Packt's professional book-printing partner which is on a print-on-demand basis.

What tax is charged? Chevron down icon Chevron up icon

Currently, no tax is charged on the purchase of any print book (subject to change based on the laws and regulations). A localized VAT fee is charged only to our European and UK customers on eBooks, Video and subscriptions that they buy. GST is charged to Indian customers for eBooks and video purchases.

What payment methods can I use? Chevron down icon Chevron up icon

You can pay with the following card types:

  1. Visa Debit
  2. Visa Credit
  3. MasterCard
  4. PayPal
What is the delivery time and cost of print books? Chevron down icon Chevron up icon

Shipping Details

USA:

'

Economy: Delivery to most addresses in the US within 10-15 business days

Premium: Trackable Delivery to most addresses in the US within 3-8 business days

UK:

Economy: Delivery to most addresses in the U.K. within 7-9 business days.
Shipments are not trackable

Premium: Trackable delivery to most addresses in the U.K. within 3-4 business days!
Add one extra business day for deliveries to Northern Ireland and Scottish Highlands and islands

EU:

Premium: Trackable delivery to most EU destinations within 4-9 business days.

Australia:

Economy: Can deliver to P. O. Boxes and private residences.
Trackable service with delivery to addresses in Australia only.
Delivery time ranges from 7-9 business days for VIC and 8-10 business days for Interstate metro
Delivery time is up to 15 business days for remote areas of WA, NT & QLD.

Premium: Delivery to addresses in Australia only
Trackable delivery to most P. O. Boxes and private residences in Australia within 4-5 days based on the distance to a destination following dispatch.

India:

Premium: Delivery to most Indian addresses within 5-6 business days

Rest of the World:

Premium: Countries in the American continent: Trackable delivery to most countries within 4-7 business days

Asia:

Premium: Delivery to most Asian addresses within 5-9 business days

Disclaimer:
All orders received before 5 PM U.K time would start printing from the next business day. So the estimated delivery times start from the next day as well. Orders received after 5 PM U.K time (in our internal systems) on a business day or anytime on the weekend will begin printing the second to next business day. For example, an order placed at 11 AM today will begin printing tomorrow, whereas an order placed at 9 PM tonight will begin printing the day after tomorrow.


Unfortunately, due to several restrictions, we are unable to ship to the following countries:

  1. Afghanistan
  2. American Samoa
  3. Belarus
  4. Brunei Darussalam
  5. Central African Republic
  6. The Democratic Republic of Congo
  7. Eritrea
  8. Guinea-bissau
  9. Iran
  10. Lebanon
  11. Libiya Arab Jamahriya
  12. Somalia
  13. Sudan
  14. Russian Federation
  15. Syrian Arab Republic
  16. Ukraine
  17. Venezuela