Unity Multiplayer Tutorial

For using Networking in Unity. Author: Andrius Kuznecovas Last Revision: 24-MAR-2010


1. Multiplayer Tutorial
1. Introduction 2. Getting Started 3. Creating Your First Client / Server Application 3.1 Preparing Scene 3.2 Creating Scripts & Adding Components 3.2.1 Server & Client 3.2.2 Scene & Objects Instantiation On The Network 3.3 Quick Overview 4. Implementing Multiplayer in Startrooper 4.1 Changing Scene 4.2 Integration 4.2.1 Integrating Objects & Scene 4.2.2 Server & Client 4.3.3 Final Polishing 4.3 Quick Overview 3 3 4 4 5 5 8 10 11 11 11 11 21 37 38

Multiplayer Tutorial
Welcome, Unity User. Here you will learn advanced usage of Unity Networking. Have a great time and we hope that this tutorial will be helpful.

1. Introduction
The aim of this tutorial is to show the essence of Unity Networking. We will show how to create basic and advanced network applications using Master Server/Client, UDP Server/Client and Direct Connect. In this tutorial we will use Unity iPhone 1.6, iPhone 3GS and the StarTrooper demo from the Unity web site.

2. Getting Started
There are a few things that you should know before you start. • You will learn how to: Use basic and advanced components of networking. Create a server and client. Use the Master Server. Use Direct Connect. Use UDP Broadcast Server. Create a simple scene on the network. Convert the StarTrooper game to multiplayer.

3G and GSM connections. 4 . Create a new Prefab and name it Player: Assets -> Create -> Prefab. You can connect between different types of Unity targets. • Important notes: Unity supports .NET 1. Creating Your First Client / Server Application In this chapter.Use other Unity components and more. You can find more information here.1 and 2. iPhone or iPod. 3. Knowledge about networks and how they work. We will create our first example featuring moving objects on the network and basic handshaking between the client and server. For example you can connect from Unity on the desktop to Unity iPhone.6.1. Drag your Cube from the Hierarchy to your Player Prefab in the Project and then delete your Cube from the scene. • You should have: Unity iPhone 1. we will cover the basics needed to create a simple multiplayer application.1 Preparing Scene • Now let’s start with a simple scene. Basic Unity skills. from Unity Web Player to Unity iPhone. Your first steps: Create a new Project. We will use Direct Connect for connection between client and server. The example uses all basic multiplayer components such as: Network and NetworkView. 3. etc. Unity Networking supports wifi. Create a new Cube GameObject: GameObject -> Create other -> Cube. Advanced C# and JavaScript skills. You can disable or enable Networking in: Edit -> Project Settings -> Player -> Enable Unity Networking.

then open the file and create some variables: var remoteIP = "127.0. save your scene and name it MainGame: File -> Save Scene.Create a new Plane and name it Ground: GameObject -> Create other -> Plane.2 Creating Scripts & Adding Components • Next you will learn how to create the server and client.1. Rotation (0. instantiate the scene and objects on the network. Rotation (25. Scale (5.0).5.0). Add this file (by dragging) to the Main Camera object in the Hierarchy. listenPort = 25000.0.1".15.0). Finally. var var var var var remotePort = 25000.2. Everything should look like this image: 3. 3.0).5). useNAT = false.0. Your Plane parameters should be: Position (0.0. 5 . Light parameters: Position (0.1) Shadows -> Type -> Soft Shadows.0. yourIP = "". Scale (1. yourPort = "". move objects. Create a Directional Light: GameObject -> Create other -> Directional Light. and connect everything together.1 Server & Client • We will start with the most important — the creation of Server and Client: Create a new JavaScript file and name it ConnectionGUI: Assets -> Create -> JavaScript.

remoteIP).Label(new Rect(140.100. remotePort).10.port.20.peerType == NetworkPeerType.DontRequireReceiver).Button (new Rect(10.10. // Connecting to the server Network. } } // Fields to insert ip address and port remoteIP = GUI.100.TextField(new Rect(230.Button (new Rect(10.Connect(remoteIP.100.100.20).Now we will create an interface using Unity GUI for creating the server and connecting to it: function OnGUI () { // Checking if you are connected to the server or not if (Network.useNat = useNAT."IP Adress: "+ipaddress+":"+port).TextField(new Rect(120.ipAddress.ToString()))."Start Server")) { Network.player. // Creating server Network.50. if (GUI. listenPort).10."Disconnect")) { // Disconnect from the server Network. remotePort = parseInt(GUI. } } } 6 .useNat = useNAT.ToString()."Connect")) { Network.40.50).Button (new Rect(10.SendMessage("OnNetworkLoadedLevel". } if (GUI. GUI.Disconnect(200).InitializeServer(32.250.Disconnected) { // If not connected if (GUI. } else { // Getting your ip address and port ipaddress = Network.40).20).30).player. // Notify our objects that the level and the network is ready for (var go : GameObject in FindObjectsOfType(GameObject)) { go.30). port = Network.10. SendMessageOptions.remotePort.

function OnConnectedToServer () { // Notify our objects that the level and the network are ready for (var go : GameObject in FindObjectsOfType(GameObject)) go. SendMessageOptions. 7 . Edit your Player Settings (Edit -> Project Settings -> Player) to set up your iPhone Bundle Identifier and switch the Default Screen Orientation to Landscape Right. Try to connect to the server using the IP address you can find on the server screen — if everything goes OK you will see “Disconnect” button and your IP address on both screens. This function is called every time someone successfully connects.• Pay attention to the function below. Note that both applications must be on the same network for everything to work. we notify all objects that the scene and the network are ready. When this happens. Build your project to the iPhone and create a server in the Editor. } • In Play mode your screen should look like these images: • Now you can test your server and client.SendMessage("OnNetworkLoadedLevel".DontRequireReceiver).

2 Scene & Objects Instantiation On The Network • Now we need to add the network components to your Player (Cube) and write code to instantiate it: Select Player Prefab and add a NetworkView: Components -> Miscellaneous -> NetworkView. Network.0). Rotation (0.0. transform. Object parameters: Position (0. 0). function OnNetworkLoadedLevel () { // Instantiating SpaceCraft when Network is loaded Network.5. Your new components should look like this image: • Now we will instantiate your Player and objects on the network: Create a new empty GameObject and name it Spawn.Instantiate(SpaceCraft. Scale (1. } function OnPlayerDisconnected (player : NetworkPlayer) { Network. When the component appears on your object change the State Synchronization parameter to Reliable Delta Compressed.position.0).2. Add a Rigidbody to your Player Prefab: Select Prefab -> Component -> Physics -> Rigidbody. This is needed to show your synchronized transformation to all users. Create a new JavaScript file and name it Instantiate. Open the file and write following code: var SpaceCraft : Transform.rotation.RemoveRPCs(player. transform.1).1.DestroyPlayerObjects(player).3. } 8 . 0).

Select your Spawn object and change the Player parameter to “Player (Transform)” by selecting your prefab from the list.Add this file to your Spawn object: Select Spawn Object -> Component -> Scripts -> Instantiate. create a server and connect to it.100.Find("Player(Clone)").Button(new Rect(20.50). } } Add this file to the Spawn object in the Hierarchy. we will create a simple test: Create a new JavaScript file and name it Control. • If you test your example you will see that the server and each connected user will have their own Player (Cube).transform.5.0). Build your project. Add this code to the file: function OnGUI() { if(GUI.js. Now you can see that everyone can move their own Player (Cube). To be sure. Your editor screen should look like this image: 9 ."up")) { GameObject.position = new Vector3(0.50.

Connect everything together.3. Download the whole NetworkExample project. Use the basic network components. In the next chapter. Create a client.3 Quick Overview Congratulations! You have covered all of the basics needed to create a multiplayer application. we will expand on the ideas that we have already covered and create an advanced networking setup. • You have learned how to: Prepare a basic scene for a multiplayer game. Create a server. 10 . Instantiate a scene and objects on the network. Use Direct Connect.

0).4. Remove the Player Controls script from the object. we will create a server and client. Add a NetworkView Component: Component -> Miscellaneous -> NetworkView. 11 .2 Integration Now we will make our main changes and integrate networking into the project. Select the StarTrooper scene. At the end of this chapter you will be able to fly around and kill other users in multiplayer mode.1 Integrating Objects & Scene • Now we need to create a script that will translate our Rigidbody on the Network: First. In the Trail Render set Materials -> Element 0 -> missleTracer. Remove the Missile Launcher script from the object.cs file to the Plugins folder. We will make more changes here later. MasterServer Connection and UDP Broadcast connection. create two folders: "NetworkFIles" for new scripts and "Plugins"for C# files that should be pre-compiled.2. Let's begin by preparing the scene and SpaceCraft for the network.1). 4. 4.1. Move the NetworkRigidbody. Rotation (0. • Download StarTrooper from the Unity Web Site and make yourself familiar with it. Set Transform parameters: Position (0. Create a new GameObject and name it Spawn: GameObject -> Create Empty. When we are done. Scale (1.11). Implementing Multiplayer in Startrooper In this chapter you will learn how to convert the StarTrooper game from single player to multiplayer. Create a C# file and name it NetworkRigidbody.30. 4.0. Select SpaceCraftFBX in Hierarchy.1 Changing Scene • As the scene we're going to modify was designed for single player game. but we'll start with some basics: Open the downloaded StarTrooper project. We will use complex components and three different connection types: Direct Connect (that you have seen in chapter 4). Add a Trail Render: Component -> Particles -> Trail Render. we need to change it a little bit.

Serialize(ref velocity). Vector3 angularVelocity = rigidbody. 12 . You can use this script on any project you want. Vector3 velocity = rigidbody.1. public class NetworkRigidbody : MonoBehaviour { public double m_InterpolationBackTime = 0. internal Vector3 angularVelocity. // Keep track of what slots are used int m_TimestampCount. just use it where it is required.identity. stream. Vector3 velocity = Vector3.Do not worry much about this file.zero. Vector3 pos.angularVelocity. void OnSerializeNetworkView(BitStream stream. internal struct State { internal internal internal internal } // We store twenty states with "playback" information State[] m_BufferedState = new State[20].isWriting) { Vector3 pos = rigidbody. because it's suitable for all Rigidbody objects. Quaternion rot. Vector3 velocity.5. stream.cs file and use this code: using UnityEngine. stream.velocity. public double m_ExtrapolationLimit = 0. Quaternion rot = Quaternion.position.rotation. using System. NetworkMessageInfo info) { // Send data to server if (stream. double timestamp.Serialize(ref pos).Serialize(ref rot).zero.Collections. Open your NetworkRigidbody. Quaternion rot = rigidbody.Serialize(ref angularVelocity). stream. } // Read data from remote client else { Vector3 pos = Vector3.

// Shift the buffer sideways.timestamp < m_BufferedState[i+1].angularVelocity = angularVelocity. state. m_TimestampCount = Mathf.m_InterpolationBackTime. } } } // We have a window of interpolationBackTime where we basically play // By having interpolationBackTime the average ping. // Use interpolation if the target playback time is present in the buffer if (m_BufferedState[0].i<m_TimestampCount-1.timestamp > interpolationTime) { 13 .Serialize(ref rot). if it is inconsistent you could reshuffel or // drop the out-of-order state. however never exceed the buffer size // Slots aren't actually freed so this just makes sure the buffer is // filled up and that uninitalized slots aren't used. Nothing is done here for (int i=0. // Update used slot count. stream.velocity = velocity.time . stream. // And only if no more data arrives we will use extra polation void Update () { // This is the target playback time of the rigid body double interpolationTime = Network.Serialize(ref velocity).Serialize(ref pos). m_BufferedState.timestamp = info.timestamp. stream. // Check if states are in order.i++) { if (m_BufferedState[i]. stream. state.i>=1.Serialize(ref angularVelocity). state. m_BufferedState[0] = state.timestamp) Debug. deleting state 20 for (int i=m_BufferedState.i--) { m_BufferedState[i] = m_BufferedState[i-1]. } // Record current state in slot 0 State state.Length).pos = pos.Length-1.zero. state.rot = rot. you will usually use interpolation.Vector3 angularVelocity = Vector3.Min(m_TimestampCount + 1. state.Log("State inconsistent").

latest.angularVelocity). rigidbody. // The best playback state (closest to 100 ms old (default time)) State lhs = m_BufferedState[i].localPosition = Vector3. So it uses 14% of rhs. rhs.910 rhs.070 = 0.Slerp(lhs. t). Quaternion angularRotation = Quaternion.Rad2Deg. transform.900 .// Go through buffer and find correct state to play back for (int i=0.900 lhs.rot.timestamp) / length).time is 9. // Use the time between the two slots to determine if interpolation is necessary double length = rhs.910 / 0.magnitude * Mathf.0001) t = (float)((interpolationTime . you would need to do that carefully if (extrapolationLength < m_ExtrapolationLimit) { float axisLength = extrapolationLength * latest. so sampleTime is 9.velocity * extrapolationLength. // As the time difference gets closer to 100 ms t gets closer to 1 in // which case rhs is only used // // // // lhs if (length > 0.localRotation = Quaternion.timestamp.AngleAxis(axisLength.timestamp <= interpolationTime || i == m_TimestampCount-1) { // The state one slot newer (<100ms) than the best playback state State rhs = m_BufferedState[Mathf.070 t is 9.Lerp(lhs.i<m_TimestampCount. 86% of 14 .position = latest. // Don't extrapolation for more than 500 ms.Max(i-1.000. 0)].pos + latest.9. } } } // Use extrapolation else { State latest = m_BufferedState[0].980 length is 0. rhs.angularVelocity.lhs.14.pos.0F. latest. float extrapolationLength = (float)(interpolationTime .time is 9.pos.lhs.rot.timestamp).timestamp . t). // if t=0 => lhs is used directly transform. float t = 0. return.i++) { if (m_BufferedState[i]. Example: Time is 10.

_NetworkRigidbody.enabled = true. Add NetworkRigidbody. Drag your SpaceCraftFBX from the Hierarchy to your new Prefab (SpaceCraft) in Project window. Disable the NetworkRigidbody component (just remove a tick from component).enabled = false. 15 .rot. Create the Tag "SpaceCraft" for your SpaceCraft Prefab: Edit -> Project Settings -> Tags. name it SpaceCraft and move it to your Prefabs folder.velocity = latest. In the Network View component: change the Observed parameter from SpaceCraftFBX (Transform) to SpaceCraftFBX (NetworkRigidbody). Create a new JavaScript file and name it RigidAssign. rigidbody. Delete SpaceCraftFBX from the scene.rigidbody. Add RigidAssign.isMine) { var _NetworkRigidbody : NetworkRigidbody = GetComponent("NetworkRigidbody").rotation = angularRotation * latest. _NetworkRigidbody2.angularVelocity = latest.cs to your selected object: Component -> Scripts -> Network Rigidbody.velocity. } } Create a new Prefab.angularVelocity. rigidbody. } else { name += "Remote".js to your object and edit the file: function OnNetworkInstantiate (msg : NetworkMessageInfo) { if (networkView. } } } } Select your SpaceCraftFBX object in the Hierarchy (not in the Project files). var _NetworkRigidbody2 : NetworkRigidbody = GetComponent("NetworkRigidbody"). We will enable it later with some conditions.

0).position.rotation.Select your SpaceCraft Prefab again and assign the Tag to it: click on "Untagged" and select "SpaceCraft". transform. function OnNetworkLoadedLevel () { // Instantiating SpaceCraft when Network is loaded Network. • Your SpaceCraft Prefab should look like this image: The SpaceCraft is now prepared for the Network! Create a new JavaScript file and name it Instantiate. } function OnPlayerDisconnected (player : NetworkPlayer) { // Removing player if Network is disconnected 16 .Instantiate(SpaceCraft.js Add the file to the Spawn object in the Hierarchy and write this code to it: var SpaceCraft : Transform. transform.

FindWithTag("SpaceCraft").currentRotation * Vector3. pos.y.0. // Convert the angle into a rotation var currentRotation : Quaternion = Quaternion.Debug. var rotationDamping : float = 3. var wantedHeight : float = target.y + height. heightDamping * dt). var currentRotationAngle : float = transform. Select the Main Camera object in the Hierarchy. // Set the height of the camera 17 . } Select the Spawn object in the Hierarchy and set the Space Craft parameter to SpaceCraft (Transform) (select your SpaceCraft Prefab from list). var currentHeight : float = transform.position.FindWithTag("SpaceCraft")) { if (!target) target = GameObject.y. Open the Smooth Follow script and change everything to: var var var var target : Transform. // Calculate the current rotation angles var wantedRotationAngle : float = target. height : float = 5.Lerp (currentHeight.0.y = currentHeight. currentRotationAngle.y. Network. // Damp the rotation around the y-axis var dt : float = Time.position. Network.deltaTime.forward * distance.transform. // Set the position of the camera on the x-z plane to: // distance meters behind the target //transform. var pos : Vector3 = target.DestroyPlayerObjects(player). currentRotationAngle = Mathf.LerpAngle (currentRotationAngle.0.eulerAngles. 0). distance : float = 10.0.eulerAngles. rotationDamping * dt).position.RemoveRPCs(player. heightDamping : float = 2. wantedHeight.Euler (0.position . 0).Log("Server destroying player").position = target. wantedRotationAngle. // Damp the height currentHeight = Mathf. function LateUpdate () { if(GameObject.

} } Add the Player Controls script to the Main Camera and open it to edit.zero. } guiSpeedElement = GameObject. var accelerator : Vector3 = iPhoneInput.acceleration. } else { iPhoneSettings. normalizedSpeed. function Awake () { if (horizontalOrientation) { iPhoneSettings.screenOrientation = iPhoneScreenOrientation.rigidbody. // Always look at the target transform.Portrait.LandscapeLeft.screenOrientation = iPhoneScreenOrientation.0. var craft : GameObject. var sensitivity : float = 0. Change everything to this code: var turnSpeed : float = 3. var maxTurnLean : float = 70.FindWithTag("SpaceCraft")) { GameObject. 0. private var euler : Vector3 = Vector3.AddRelativeForce(0.LookAt (target). var horizontalOrientation : boolean = true. normalizedSpeed * (forwardForce*3)). var maxTilt : float = 50.2.Find("speed"). var forwardForce : float = 5. guiSpeedElement.0.0.transform.transform. var guiSpeedElement : Transform.position = new Vector3 (0. 18 .5. private var normalizedSpeed : float = 0.0.position = pos.FindWithTag("SpaceCraft"). 0). } function FixedUpdate () { if(GameObject.

transform.x.phase == iPhoneTouchPhase. } 19 .Moved) { normalizedSpeed = evt.rotation.x * turnSpeed.y = t.x = -accelerator.z = Mathf.Lerp(euler. We will add a timer for shooting and some small changes as well.Euler(euler). } // Rotate turn based on acceleration euler. 0). function FixedUpdate() { timer++. do some extra smoothing on it euler. Set the Missile parameter to missilePrefab.z.Lerp(euler. do some extra smoothing on it euler. GameObject. sensitivity). 0. guiSpeedElement.rotation = Quaternion. var timer : int = 0.height. normalizedSpeed. // Since we set absolute lean position.if (horizontalOrientation) { var t : float = accelerator. } } function Update () { for (var evt : iPhoneTouch in iPhoneInput. -accelerator.x.x * maxTurnLean.touches) { if (evt. accelerator.Lerp (transform.2). // Apply rotation and apply some smoothing var rot : Quaternion = Quaternion.2).y / Screen.x = Mathf. Open the Missile Launcher script to edit. } } } Add a Missile Launcher script to the Main Camera.y * maxTilt. var missile : GameObject. accelerator.position = new Vector3 (0. rot.position.y += accelerator.FindWithTag("SpaceCraft"). 0.y. accelerator. // Since we set absolute lean position.

camera.0) .Instantiate (missile. position = GameObject. if (collision. } } } Create the new tag "Missile" and assign it to missilePrefab.normal * 5. We will make the ability to kill other SpaceCraft: var explosion : GameObject.identity).FindWithTag("SpaceCraft"). Instantiate (explosion. Physics.FindWithTag("SpaceCraft").transform.collider..point + (contact.gameObject.collider).transform.tag == "SpaceCraft"))&&(collision.point + (contact.TransformPoint (position).FindWithTag("SpaceCraft").transform.0) as GameObject. } Destroy (gameObject).gameObject. contact.position = GameObject.main.gameObject.0) . Select the missilePrefab and open the MissileTrajectory script to edit. // instantiating missile var thisMissile : GameObject = Network. -0. collision. 1) * 10.function Update () { if ((Input.contacts[0].Find("Spawn").normal * 5. contact.IgnoreCollision(thisMissile.transform.tag != "Missile")) { var contact : ContactPoint = collision.gameObject. function OnCollisionEnter(collision : Collision) { if(GameObject. position. GameObject.GetMouseButtonDown (0))&&(timer>10)) { // if SpaceCraft exists if(GameObject. } 20 .2.FindWithTag("SpaceCraft")) { var position : Vector3 = new Vector3(0. Quaternion.rotation).rotation.gameObject.tag == "Untagged")||(collision.FindWithTag("SpaceCraft")) { if(((collision.tag == "SpaceCraft") { Instantiate (explosion. timer = 0. GameObject.transform.0.position.

} } You have prepared the Scene and Objects for the Network. for example: • PS: you can create a folder and put all scenes together in one place.2. This scene will be used for Master Server. Add all your scenes to the build settings: File -> Build Settings -> Add Open Scene. The scene will be used for choosing server type. We will be creating three different types of servers.0)) * 720. Create a new scene and save it as UDPServer. This scene will be used for UDP Broadcast connection. We will use this scene when player has disconnected to clear up leftovers before new connection.forward + Vector3(0. 21 . Create a new scene and save it as ServerChoose: File -> New Scene. 4.TransformDirection (Vector3.0). Create a new scene and save it as MasterServer.0. save as EmptyScene.2 Server & Client Now it's time to create the server for this game. File -> Save Scene.} } function FixedUpdate () { if(GameObject.1.AddForce (transform.FindWithTag("Missile")) { rigidbody. Create a new scene again. Your scene "ServerChoose" should be first in the list.

if(GUI.height/2)-130.(Screen.width/2)-100. We will use this scene and this script for choosing a type of server and connection. networkView.LoadLevel("UDPServer"). } if(GUI.height/2)-40.LoadLevel("StarTrooper")."UDP Connection")) { Application."STAR-TROOPER MULTIPLAYER DEMO").50). GUI. Add the NetworkLevelLoad script to the ConnectionGUI object and open to edit: // Keep track of the last level prefix (increment each time a new level loads) private var lastLevelPrefix = 0. We will use this script for loading the StarTrooper scene and objects to the network.Button(new Rect((Screen.LoadLevel("MasterServer"). function Awake () { // Network level loading is done in a seperate channel.(Screen."Master Server Connection")) { Application.Label(new Rect((Screen.200.Button(new Rect((Screen.200.30). Add this script to the Main Camera and add the following code: function OnGUI() { GUI.50)."SELECT CONNECTION TYPE"). Create a new Empty GameObject and name it ConnectionGUI.(Screen.Open the ServerChoose scene. 22 . } if(GUI.width/2)-100.50). Create a new JavaScript file and name it NetworkLevelLoad.200.width/2)-100.Button(new Rect((Screen.group = 1.Label(new Rect((Screen.(Screen.height/2)+20. } } Open the MasterServer scene. Create a new JavaScript file and name it Menu.50).height-30).(Screen."Direct Connection")) { Application.height/ 2)-100.width-220).200. DontDestroyOnLoad(this).220.width/2)-80.

LoadLevel("EmptyScene"). because all those objects will get deleted anyway Network. // Load level with incremented level prefix (for view IDs) networkView.RemoveRPCsInGroup(1). yield. RPCMode. yield.SetSendingEnabled(0. // All network views loaded from a level will get a prefix into their NetworkViewID. // This will prevent old updates from clients leaking into a newly created scene. Network. Network.SetSendingEnabled(0.RemoveRPCsInGroup(0). // Once the level is loaded. // There is no reason to send any more data over the network on the default channel. RPC's and other state update attached to objects in the level are allowed to fire Network. lastLevelPrefix = levelPrefix. true). // Now the level has been loaded and we can start sending out data Network.isMessageQueueRunning = false.Disconnected) { if (GUI. levelPrefix : int) { Debug. lastLevelPrefix + 1).30).peerType != NetworkPeerType.Log("Loading level " + level + " with prefix " + levelPrefix).100. // We need to stop receiving because first the level must be loaded. false).RPC( "LoadLevel". } } } @RPC function LoadLevel (level : String. Application."StarTrooper")) { // Make sure no old RPC calls are buffered and then send load level command Network.10. // Allow receiving data again Network.AllBuffered. // because we are about to load the level. "StarTrooper".isMessageQueueRunning = true. } function OnGUI () { // When network is running (server or client) then display the level "StarTrooper" if (Network.Button(new Rect(350.SetLevelPrefix(levelPrefix).LoadLevel(level).Application. // Notify our objects that the level and the network is ready 23 .

With this script we will create a Master Server/Client and some GUI to use it.Undetermined. private var hostListRefreshTimeout = 10. var gameName = "YourGameName".LoadLevel("EmptyScene").0. } 24 .DontRequireReceiver). SendMessageOptions. private var natCapable : ConnectionTesterStatus = ConnectionTesterStatus. } } function OnDisconnectedFromServer () { Application.i++) { go[i].0. private var doneTesting = false.0.length. private var timeoutHostList = 0. for (var i=0.100).0.SendMessage("OnNetworkLoadedLevel".dedicatedServer = true. private var timer : float = 0.width-300.var go : Transform[] = FindObjectsOfType(Transform). } @script RequireComponent(NetworkView) • Ensure that this script has added a NetworkView automatically. private var probingPublicIP = false. otherwise add NetworkLevelLoad from the menu (Component->Scripts->NetworkLevelLoad). var serverPort = 25002. Create a new JavaScript file and name it MasterServerGUI.0.300. private var lastHostListRequest = -1000. private var windowRect = Rect (Screen. function OnFailedToConnectToMasterServer(info: NetworkConnectionError) { Debug. private var filterNATHosts = false. private var testMessage = "Undetermined NAT capabilities". Add this file to the ConnectionGUI object and open the script to edit: DontDestroyOnLoad(this).Log(info). // Enable this if not running a client on the server machine // MasterServer.i<go_len. var go_len = go. private var hideTest = false.

} function Update() { // If test is undetermined.PrivateIPNoNATPunchthrough: testMessage = "Cannot do NAT punchthrough. break. 25 .TestConnection().HavePublicAddress()) Debug. report the results in a label and react to the results accordingly natCapable = Network. filtering NAT enabled hosts for client connections. switch (natCapable) { case ConnectionTesterStatus. } function OnGUI () { ShowGUI(). doneTesting = false. break.Log("This machine has a public IP address").function OnFailedToConnect(info: NetworkConnectionError) { Debug.Log(info).Log("This machine has a private IP address").Undetermined: testMessage = "Undetermined NAT capabilities".TestConnection(). filterNATHosts = true. case ConnectionTesterStatus." +" local LAN games only. } } function TestConnection() { // Start/Poll the connection test. doneTesting = true.Error: testMessage = "Problem determining NAT capabilities". keep running if (!doneTesting) { TestConnection(). } function Awake () { // Start connection test natCapable = Network.". case ConnectionTesterStatus. else Debug. // What kind of IP does this machine have? TestConnection also indicates this in the // test results if (Network.

useNat = true.time > timer) { probingPublicIP = false. // If no NAT punchthrough test has been performed on this public IP. break.useNat = false.Network.TestConnectionNAT(). break. doneTesting = true. probingPublicIP = true. // This case is a bit special as we now need to check if we can // use the blocking by using NAT punchthrough case ConnectionTesterStatus. else testMessage = "NAT punchthrough capable.useNat = true. // reset Network. Network.useNat = false." +" running a server is impossible. case ConnectionTesterStatus.Log("Testing if firewall can be circumnvented").PublicIPIsConnectable: testMessage = "Directly connectable public IP address.". break. 26 ." +" NAT punchthrough can circumvent the firewall. // clients should enable this based on if the host requires it Network. force a test if (!probingPublicIP) { Debug. doneTesting = true.". timer = Time.PrivateIPHasNATPunchThrough: if (probingPublicIP) testMessage = "Non-connectable public IP address (port "+ serverPort +" blocked). natCapable = Network. // NAT functionality is enabled in case a server is started. case ConnectionTesterStatus.PublicIPPortBlocked: testMessage = "Non-connectble public IP address (port " + serverPort +" blocked).time + 10.". } break. doneTesting = true.useNat = true.". } // NAT punchthrough test was performed but we still get blocked else if (Time. Enabling NAT punchthrough functionality. doneTesting = true. Network.

var _cnt : int = 0. "stuff".RequestHostList (gameName).30).RegisterHost(gameName.InitializeServer(32.ClearHostList(). got " + natCapable.TestConnection(true)."Refresh available Servers") || Time. probingPublicIP = false. default: testMessage = "Error in test routine.30). serverPort).PollHostList().realtimeSinceStartup > lastHostListRequest + hostListRefreshTimeout) { MasterServer.10. Debug. break.90. } // Refresh hosts if (GUI. "profas chat test"). MasterServer.Button(new Rect(10." +"it must be started to check server accessibility.realtimeSinceStartup.Log("Refresh Click").10. MasterServer.case ConnectionTesterStatus.Button (new Rect(100.peerType == NetworkPeerType. } var data : HostData[] = MasterServer.useNat) ) { 27 .".120.PublicIPNoServerStarted: testMessage = "Public IP address but server not initialized."Start Server")) { Network. } } function ShowGUI() { if (GUI. for (var element in data) { // Do not display NAT enabled games if we cannot do NAT punchthrough if ( !(filterNATHosts && element.Log("Redoing connection test").30). MasterServer.Button(new Rect(10. Restart connection test when ready.40. doneTesting = false.210.updateRate = 3. natCapable = Network.Disconnected) { // Start a new server if (GUI. lastHostListRequest = Time."Retest connection")) { Debug. } if (Network.

var hostInfo.ip list.playerLimit.port).useNat = element.Disconnect().30).ip) { hostInfo = hostInfo + host + ":" + element. else print("Connecting directly to host"). element. In the GUI we could just display // the first one in order not confuse the end user. // Here we display all IP addresses."Disconnect")) { Network.40).10.Button(new Rect(20.useNat.90.useNat) print("Using Nat punchthrough to connect"). Network.400.port + " ". and connect to the // first valid one.(_cnt*50)+90. if (Network. for (var host in element. } hostInfo = hostInfo + "]". there can be multiple in cases where // internal LAN connections are being attempted.hostInfo.ToString())) { // Enable NAT functionality based on what the hosts if configured to do Network. if (GUI.Button (new Rect(10.UnregisterHost().gameName + " " + element. } } } • Now you can test your project using the Master Server: Go to Edit -> Project Settings -> Player and set up your iPhone Bundle Identifier and change the Default Screen Orientation to Landscape Left. } } } } else { if (GUI. hostInfo = "[". 28 . MasterServer.Connect(element.var name = element.connectedPlayers + " / " + element. but internally Unity will // do a connection check on all IP addresses in the element.ip.

Connect to the server from the iPhone.Build your project to the iPhone. Your screens should look like these images: We have finished our Master Server! 29 . Click on the StarTrooper button in the Editor or on the iPhone. The server should appear on iPhone (if not. Run the Server scene in the Editor. click Refresh available Servers). Click on Start Server on Editor. Click on Master Server Connection in the Editor and on the iPhone.

private IPEndPoint receivePoint. (Or assign it after you write the following code).0. int listenPort = 25001. Create a new C# file and name it UDPConnectionGUI. } 30 . Assign the NetworkLevelLoad Script file to the UDPServer object.0". public void Update() { if(clear_list++>200) { server_name = "".Collections. Assign the UDPConnectionGUI file to the UDPServer object. Assign a NetworkView Component to the UDPServer object and change the Observed parameter to UDPServer (Transform). Move the UDPConnection file to the Plugins folder.0. private UdpClient client. public class UDPConnectionGUI : MonoBehaviour { private UdpClient server. string ip = "0. bool youServer = false. private bool connected = false. Create a new empty GameObject and name it UDPServer.255. private int clear_list = 0. System.Sockets.Threading.255". using using using using System.Net.UDP Broadcast connection: Open the UDPServer scene. System. clear_list = 0.Net. System.255. string ip_broadcast = "255. private string server_name = "".• Now we need to create a second connection type . Open UDPConnectionGUI and write the following: using UnityEngine. private private private private private string port = "6767".

31 . } public void start_client() { bool continueLoop =true.ToInt32(port)).GetBytes(Network. if(connected) { server_name = "".Text. LoadClient().ASCIIEncoding encode = new System. byte[] sendData = encode. Thread startClient = new Thread(new ThreadStart(start_client)).ASCIIEncoding().Text. startClient.Start().Parse(ip).Convert.System. } public void LoadClient() { client = new UdpClient(System. server_name = encode. break. System.ASCIIEncoding encode = new System.} public void Start() { Debug.Receive(ref receivePoint).Text.ipAddress. receivePoint = new IPEndPoint(IPAddress.Close().Text.Log("Start"). } } } catch {} } public void start_server() { try { while(true) { System.player. try { while(continueLoop) { byte[] recData = client.GetString(recData).ToString()). client.ASCIIEncoding().Convert.ToInt32(port)).

server. youServer = false.200. } } } else { if(GUI.ToInt32(port)).ToInt32(port)). } if(server_name!="") { if(GUI. } } } } 32 .Parse(ipaddress).Sleep(100). ip = ipaddress. startServer.Close().InitializeServer(32. server = new UdpClient(System.Send(sendData.100. listenPort).10."Start Server")) { youServer = true.server_name)) { connected = true. client.Button(new Rect(10.100.System.10.Connect(server_name.System.server.sendData. listenPort). string ipaddress = Network.ipAddress. } } catch {} } void OnGUI() { if(!youServer) { if(GUI.ToInt32(port)).Button(new Rect(10. receivePoint = new IPEndPoint(IPAddress.30).ip_broadcast."Disconnect")) { Network.Disconnect().Convert.Convert.30).Button(new Rect(20.100. Thread startServer = new Thread(new ThreadStart(start_server)). Network. Thread.Start().Length.50). LoadClient().player.Close(). Network.Convert.ToString().

Your screens should look like these images: We have finished our UDP Broadcast server! • Now we need to create a third connection type . Assign the ConnectionGUI.0. var yourIP = "". Start the server from the Editor by clicking Start Server.• Now you can test this connection type: Set your Player Settings (if you haven't changed them yet) as instructed above. Connect to the server from the iPhone when the IP address button appears. Play the UDPServer scene in the Editor.js file to the Main Camera and write the following: var remoteIP = "127. 33 . var yourPort = "". var useNAT = false.0.Direct Connect: Open the StarTrooper scene. Create a new JavaScript file and name it ConnectionGUI.enabled = false. function Awake() { if (FindObjectOfType(MasterServerGUI)) this.1". var remotePort = 25000. Build the project to the iPhone. var listenPort = 25000.

Button (new Rect(10. if (GUI.40.ToString().ipAddress.ToString()))."Connect")) { Network. SendMessageOptions.SendMessage("OnNetworkLoadedLevel".Label(new Rect(140.DontRequireReceiver).remoteIP).20).10. } function OnGUI () { if (Network. listenPort). } if (GUI.20).10. } else { // If connected // Getting your ip address and port ipaddress = Network.Disconnected) { // If not connected if (GUI. // Creating server Network.useNat = useNAT.50).100. GUI.100.TextField(new Rect(120.InitializeServer(32.TextField(new Rect(230.Button (new Rect( remotePort = parseInt(GUI."Start Server")) { Network.if(FindObjectOfType(UDPConnectionGUI)) this.100.50. port = Network. // Connecting to the server Network.player.40).30). remotePort).Button (new Rect(10.port.30)."Disconnect")) { // Disconnect from the server Network. // Notify our objects that the level and the network is ready for (var go : GameObject in FindObjectsOfType(GameObject)) { go.remotePort."IP Adress: "+ipaddress+":"+port).peerType == NetworkPeerType. } } remoteIP = GUI.Disconnect(200).10.player.Connect(remoteIP.enabled = false.250.useNat = useNAT. } 34 .

SendMessageOptions. _NetworkLevelLoad.LoadLevel(Application.enabled != false) Application. else { var _NetworkLevelLoad : NetworkLevelLoad = FindObjectOfType(NetworkLevelLoad). Play the StarTrooper scene in the Editor.SendMessage("OnNetworkLoadedLevel". Build the project to the iPhone.OnDisconnectedFromServer(). } function OnDisconnectedFromServer () { if (this. Start the server from the Editor by clicking Start Server. Your screens should look like these images: 35 .DontRequireReceiver).loadedLevel).} } function OnConnectedToServer() { // Notify our objects that the level and the network is ready for (var go : GameObject in FindObjectsOfType(GameObject)) go. Click Direct Connection in the Editor and on the iPhone. } } • Now you can test this connection type: Set your Player Settings (if you haven't changed them yet) as instructed above. You can get the IP address from the Editor Player screen. Click Connect. Enter the IP address on the iPhone in the IP field.

We have finished our Client and Server with three connection types. 36 .

Max Turn Lean: 70. Select the missilePrefab Prefab and set the Transform Scale parameters to: Select the Main Camera in the Hierarchy and change the public parameters in the Player Controls script: Turn Speed: 3.8.3 Final Polishing • Our project is almost finished now.1) to Vector3(0. Sensitivity: 0. Max Tilt: 50. Forward Force: 20.4. In the Main Camera open MissileLauncher.8. To make our game more playable we need to add some final polish: Open the StarTrooper scene.2.3.5.-0. At line 16.0.8.js script.1. Select the SpaceCraft Prefab and set the Transform Scale parameters to: 1. so feel free to make your own modifications or new features.8. Your last screen should look something like this image: • PS: These last modifications are based only on my taste. 37 .-0. change the vector from Vector3(0.1.

Create a Master Server/Client. Convert a single player game into multiplayer. Create a Server/Client using UDP Broadcast connection. • You have learned how to: Prepare a scene and object for the network.4. Download the whole MultiPlayer StarTrooper Project 38 .3 Quick Overview Congratulations! You have finished your MultiPlayer StarTrooper project. Use advanced Unity Components. Use the main Network Components. Translate a scene and objects on the network. Create a Server/Client using Direct Connection.

Sign up to vote on this title
UsefulNot useful