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

5. Light parameters: Position (0.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.0). Scale (5. Finally. save your scene and name it MainGame: File -> Save Scene.2 Creating Scripts & Adding Components • Next you will learn how to create the server and client.0). Create a Directional Light: GameObject -> Create other -> Directional Light.0. var var var var var remotePort = 25000. and connect everything together. Your Plane parameters should be: Position (0. 5 .0). Rotation (0. move objects. then open the file and create some variables: var remoteIP = "127.1. 3.0. Scale (1.0). instantiate the scene and objects on the network. Rotation (25. listenPort = 25000.0.0. yourIP = "".0. Everything should look like this image: 3. yourPort = "". Add this file (by dragging) to the Main Camera object in the Hierarchy.1".Create a new Plane and name it Ground: GameObject -> Create other -> Plane.2. useNAT = false.1) Shadows -> Type -> Soft Shadows.5).15.

port.TextField(new Rect(230.SendMessage("OnNetworkLoadedLevel".40."Connect")) { Network.remotePort.Disconnected) { // If not connected if (GUI.250.peerType == NetworkPeerType.30).Connect(remoteIP.10."Start Server")) { Network.Button (new Rect(10.100.InitializeServer(32.useNat = useNAT. remotePort).50).ipAddress. } } // Fields to insert ip address and port remoteIP = GUI.40).30).TextField(new Rect(120.remoteIP).100. } if (GUI.100.10.Disconnect(200).useNat = useNAT.10.ToString().ToString())).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.100."IP Adress: "+ipaddress+":"+port). } } } 6 .20."Disconnect")) { // Disconnect from the server Network. } else { // Getting your ip address and port ipaddress = Network. port = Network.50.Button (new Rect(10.player.player.DontRequireReceiver).Button (new Rect(10. remotePort = parseInt(GUI.20). GUI. // Connecting to the server Network. SendMessageOptions.20). if (GUI. // Creating server Network.Label(new Rect(140. listenPort). // Notify our objects that the level and the network is ready for (var go : GameObject in FindObjectsOfType(GameObject)) { go.10.

DontRequireReceiver). Build your project to the iPhone and create a server in the Editor. Edit your Player Settings (Edit -> Project Settings -> Player) to set up your iPhone Bundle Identifier and switch the Default Screen Orientation to Landscape Right. When this happens. SendMessageOptions.• Pay attention to the function below.SendMessage("OnNetworkLoadedLevel". function OnConnectedToServer () { // Notify our objects that the level and the network are ready for (var go : GameObject in FindObjectsOfType(GameObject)) go. we notify all objects that the scene and the network are ready. 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. 7 . Note that both applications must be on the same network for everything to work. } • In Play mode your screen should look like these images: • Now you can test your server and client.

Open the file and write following code: var SpaceCraft : Transform.rotation.1). } 8 .3. 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. When the component appears on your object change the State Synchronization parameter to Reliable Delta Compressed.0. function OnNetworkLoadedLevel () { // Instantiating SpaceCraft when Network is loaded Network.1.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.RemoveRPCs(player.position.DestroyPlayerObjects(player).Instantiate(SpaceCraft.0). } function OnPlayerDisconnected (player : NetworkPlayer) { Network.0).2. This is needed to show your synchronized transformation to all users. Create a new JavaScript file and name it Instantiate. Add a Rigidbody to your Player Prefab: Select Prefab -> Component -> Physics -> Rigidbody. 0). Rotation (0. transform. Network.5. Object parameters: Position (0. 0). transform. Scale (1.

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

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

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

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

Serialize(ref pos). deleting state 20 for (int i=m_BufferedState.time . // Use interpolation if the target playback time is present in the buffer if (m_BufferedState[0].Serialize(ref angularVelocity). m_BufferedState.Min(m_TimestampCount + 1.Log("State inconsistent"). // Shift the buffer sideways.i<m_TimestampCount-1. m_BufferedState[0] = state. stream. stream.angularVelocity = angularVelocity. // 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. } } } // We have a window of interpolationBackTime where we basically play // By having interpolationBackTime the average ping.Vector3 angularVelocity = Vector3. state.timestamp < m_BufferedState[i+1].rot = rot.timestamp.Length-1.Serialize(ref rot). Nothing is done here for (int i=0. m_TimestampCount = Mathf.i>=1.m_InterpolationBackTime.zero. you will usually use interpolation. state. } // Record current state in slot 0 State state.i--) { m_BufferedState[i] = m_BufferedState[i-1]. stream. if it is inconsistent you could reshuffel or // drop the out-of-order state. // Update used slot count.i++) { if (m_BufferedState[i].velocity = velocity. state.pos = pos.timestamp = info. stream. 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.timestamp) Debug. // Check if states are in order.timestamp > interpolationTime) { 13 .Length). state. state.Serialize(ref velocity).

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

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

} function OnPlayerDisconnected (player : NetworkPlayer) { // Removing player if Network is disconnected 16 . • 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.Instantiate(SpaceCraft.js Add the file to the Spawn object in the Hierarchy and write this code to it: var SpaceCraft : Transform. function OnNetworkLoadedLevel () { // Instantiating SpaceCraft when Network is loaded Network. 0).Select your SpaceCraft Prefab again and assign the Tag to it: click on "Untagged" and select "SpaceCraft". transform.position. transform.rotation.

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

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

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

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

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

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

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

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

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

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

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

ip list.useNat = element. Network.UnregisterHost().var name = element. var hostInfo.40).Connect(element. element. hostInfo = "[". if (GUI. // Here we display all IP addresses. } } } } else { if (GUI.400."Disconnect")) { Network.90.useNat) print("Using Nat punchthrough to connect").10. but internally Unity will // do a connection check on all IP addresses in the element. and connect to the // first valid one.Disconnect().gameName + " " + element. MasterServer. else print("Connecting directly to host"). In the GUI we could just display // the first one in order not confuse the end user. there can be multiple in cases where // internal LAN connections are being attempted.hostInfo. } hostInfo = hostInfo + "]". 28 .30). if (Network.ip.Button (new Rect(10.connectedPlayers + " / " + element.(_cnt*50)+90.Button(new Rect(20. } } } • 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.useNat.port).ip) { hostInfo = hostInfo + host + ":" + element.playerLimit. for (var host in element.port + " ".ToString())) { // Enable NAT functionality based on what the hosts if configured to do Network.

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

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

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

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

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

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

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

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

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

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

Sign up to vote on this title
UsefulNot useful