This tutorial will explain how multiplayer can be implemented using Unity’s networking functionality. We didn’t work on an online multiplayer game before, and here will we describe how we designed the implementation for LFG: The Fork of Truth. This is a four person co-op game where each player controls one of the characters from the LFG comic. Players will be working together by combining their abilities to defeat their enemies and complete quests.

In this tutorial I explain how to create a online multiplayer game using Photon Unity Networking (PUN), instead of the standard Unity networking.

The video below is from the Kickstarter-demo we have made and shows the gameplay and multiplayer functionality which we are going to talk about.

An important decision we made at the start of the project was to implement networking first and all other code later. For each new feature we made sure it worked over the network. In the end this saved us a lot of time, because implementing it at a later stage would probably result in changing a lot of code. To follow this tutorial, a basic understanding of Unity and C# is required.

These are the things we are going to talk about:

Implement server creation and joining an existing host.

Spawning as a player and how objects can be created on the network.

Network communication using State Synchronization and Remote Procedure Calls.

Interpolating and predicting values between data packages.

Networking was a new topic for us and I found this video tutorial very useful to start with. It’s explained in Javascript, but covers the same content as the first three paragraphs.

Creating a server

So let’s get started! In the new Unity project, create a new C# script named “NetworkManager”. Add this empty script to an object in the scene, either the camera or an empty game object. It will handle hosting a server or connecting to an existing server.

To create a server, we need to initialize it on the network and register it to the master server. The initialization requires a maximum amount of players (in this case 4) and a port number (25000). For the server registration the name of the game should be unique, otherwise you might get in trouble with others projects using the same name. The room name can be any name, and in our case we eventually used the player name. Add these lines to the NetworkManager script.

If the server is successfully initialized, OnServerInitialized() will be called. For now, we are happy to get feedback telling us the server is actually initialized.

void OnServerInitialized()
{
Debug.Log("Server Initializied");
}

All we need now is some form of input to let us actually start the server when we want to. To test it out we create buttons using the Unity GUI. We only want to see these buttons if we have not started a server or joined one, so the button will show itself if the user is neither a client nor a server.

Now it is time to test what we developed so far. When starting the project, all you should see now is a start server button (1A) . If you press this button, a message should be shown in the console indicating you just initialized a server. Afterwards the button should disappear (1B).

This MasterServer is run by Unity and could be down due to maintenance. You can download and run your own MasterServer locally. Add to NetworkManager.cs the following:

MasterServer.ipAddress = “127.0.0.1″;

Thanks to jeff77k for this addition!

Joining a server

We now have the functionality to create a server, but can not yet search for existing servers or join one of them. To achieve this, we need to send a request to the master server to get a list of HostData. This contains all data required to join a server. Once the host list is received, a message is sent to the game which triggers OnMasterServerEvent(). This function is called for several events, so we need to add a check to see if the message equals MasterServerEvent.HostListReceived. If this is the case, we can store the host list.

By extending the GUI with some additional buttons, the functions we just created can be called. There will be two buttons now at the start, one to start the server and another to refresh the host list. A new button is created for every server and it will connect the user to the corresponding room.

This is a good point to do another test (2A). One thing to note is that testing multiplayer takes a bit longer, particularly because you always require two instances of the game. To run two instances on the same computer this settings needs to be checked. Go to File > Build Settings > Player Settings > Run in Background and enable it. Now create a new build, launch it and press “Start Server”. Now we can test our new functionality in the Unity editor. After refreshing your list, another button should appear (2B) allowing you to connect the two instances of your game (2C).

Spawning a player

Now we should be able to connect multiple players to one another, we can now extend the code with game mechanics. Set up a simple scene with a floor plane, player and some lighting. Add a rigidbody to the player and freeze the rotations, so we will not get strange behaviour while moving.

Create a Player-script, add it to the player object and add the following code:

Next, add the network view component to the player object (Component > Miscellaneous > Network View). This will enable us to send data packages over the network to synchronize the player. The state synchronization field is automatically set to “reliable delta compressed”. This means that synchronized data will be sent automatically, but only if its value changed. So for example if you move as a player, your position will be updated on the server. By setting it to “off” there is no automatic synchronization at all and you have to do it manually. For now set it to “reliable”, later in this tutorial these options will be discussed in more detail. Add the player object to the hierarchy to make it a prefab, so we can instantiate it on the network.

In the NetworkManager-script add a public game object variable for the player prefab. In the new function SpawnPlayer(), the prefab will be instantiated on the network, so all clients will see this object within their game. It requires a position, rotation and group, so I would suggest creating a spawn point.

The code in OnServerInitialized() and OnConnectedToServer() needs to be changed to spawn the player.

Time for another test! Create a new build and run two instances again. You will notice that you can control all players connected, not just your own. This shows one important aspect that will be used often when creating a multiplayer game: who controls what object?

One way to fix this problem is to build in a check on the player code so it only receives input from the user that instantiated the object. Because we set reliable synchronization on the network view, data is sent automatically across the network and no other information is required.

To implement this method, we need to check whether the object on the network “is mine” in the player script. Add the following if-statement to the Update():

void Update()
{
if (networkView.isMine)
{
InputMovement();
}
}

If you do another test now, you will see you can only control one of the players.

Another solution could be to send all input to the server, which will then convert your data to actual movement and send back your new position to everyone on the network. The advantage is that everything is synchronized on the server. This prevents players from cheating on their local client. A disadvantage of this method is the latency between the client and server, which may result in the user having to wait to see the actions he performed.

State Synchronization

There are two methods of network communication. The first is State Synchronization and the other is Remote Procedure Calls, which will be covered in another paragraph. State Synchronization constantly updates values over the network. This approach is useful for data that changes often, like player movement. In the function OnSerializeNetworkView() the variables are sent or received and will synchronize them quick and simple. To show you how this works, we will write the code that synchronizes the player’s position.

Go to the network view component on the player’s prefab. The observed field contains the component that will be synchronized. The transform is automatically added to this field, which results in the position, rotation and scale being updated depending on the sendrate. Drag the component of the player script in the observed field so we can write our own synchronization method.

Add OnSerializeNetworkView() to the player script. This function is automatically called every time it either sends or receives data. If the user is writing to the stream, it means he is sending the data. By using stream.Serialize() the variable will be serialized and received by other clients. If the user receives the data, the same serialization-function is called and can now be set to store the data locally. Note that the order of variables should be the same for sending and receiving data, otherwise the values will be mixed up.

Make another build and run it. The results should be the same as before, but now we have granted ourselves control over the movement and how the synchronization works.

Interpolation

You might have noticed latency issues between the two instances due to the sendrate. The standard settings in Unity is that a package is being tried to send 15 times per second. For testing purposes, we will change the sendrate. To do this, first go to the network settings at (Edit> Project Settings > Network). Then, change the sendrate to 5, resulting is less data packages being sent. If you would do another build and test, the latency should be clearer.

To smooth the transition from the old to the new data values and fix these latency issues, interpolation can be used. There are several options how this can be implemented. For this tutorial, we will interpolate between the current position and the new position received after synchronization.

OnSerializeNetworkView() needs to be extended with to store all the required data: the current position, new position and delay between updates.

Create a new build and test it, you should now see the transition looks better between updates.

Prediction

Though the transitions look smooth, you notice a small delay between the input and the actual movement. This is because the position is updated after the new data is received. Until we invent time travel, all we can do is predict what is going to happen based on the old data.

One method to predict the next position is by taking the velocity into account. A more accurate end position can be calculated by adding the velocity multiplied by the delay.

After building and testing the game again, you will notice the transitions are still smooth and the latency between your input and the actual movement seem less. There are also a few corner-cases where behaviour might seem a little strange if the latency is too high. If the player starts moving, the other clients still predict you would stand still. Set the sendrate at network settings back to 15 updates per second for better results.

For our Kickstarter demo, we used a navmesh to walk around and this seemed to make interpolation and prediction better. The sendrate was set to 5 and as a user you could barely notice the delay. This tutorial will not go any deeper into the integration of the navmesh, but for your future projects it might be worth to consider this approach.

Remote Procedure Calls

Another method of network communication is Remote Procedure Calls (RPCs), which is more useful for data that does not constantly change. A good example what we used these for in our Kickstarter demo is dialog. In this paragraph we will change the color of a player over the network.

What RPCs do is a function call on a network view component and this component searches the correct RPC function. By adding [RPC] in front of the function, it can be called over the network. This approach is only able to send integers, floats, strings, networkViewIDs, vectors and quaternions. So not all parameters can be sent, but this can be solved. To send a game object, we should add a network view component to this object so we can use its networkViewID. To send a color, we should convert it to a vector or quaternion.

An RPC is sent by calling networkView.RPC(), in which you define the function name and parameters. Also the RPC mode is required: “Server” sends the data to the server only, “Others” to everyone on the server except yourself and “All” sends it to everyone. The last two also have the functionality to set is as buffered, this results in newly connected players receiving all these buffered values. Because we send this data package every frame now, there is no need to buffer it.

To integrate this functionality to our tutorial game, Update() needs to call the function to check for input and change the material to a random color. The RPC function changes the color based on the input and if the player object is controlled by the user, he will send an RPC to all others on the network.

As you might have noticed there are a lot of choices that need to be made along the way, depending on the game’s requirements. This tutorial briefly explained our implementation for the Kickstarter demo of LFG: The Fork of Truth.

If you have any questions, suggestions or comments, feel free to use the comment section below.

Hey, this is a great example and tutorial on how to start with multiplayer games, since it covers most of the basic things needed to understand a networked game. Using state Sync is great for small projects that do not require a high amount of messages constantly being sent/received, using entirely RPCs will help you achieve a more controlled and efficient communication and process environment. Yes having such things as a much more efficient system comes with the cost of having to code more things, and while all this can be a class that is very easily maintainable it is well worth the effort. Not only will you be able to run more instances of your games on the same hardware and will be less taxing on processing power and bandwidth usage. Which, if you want to host your servers on the cloud, can reduce a lot of costs and make all your infrastructure smaller and less complicated. Another thing that can help in the same way is to specialize your code by separating the server exclusive code from the client’s code. Not only will this help in performance but will make most changes very easy to maintain and update to production since you control the server hosts.
I would like to redirect users from my blog who want to start making a multiplayer game to this guide if you don’t mind, I find it concise, and useful for the basics of networking in games. Btw, if you would like to check many more optimizations on multiplayer games, you can check my blog.

Grim

Nathan van Hulst

Very nice Tutorial. However for all other people trying this tutorial. There is one thing that has been forgotten once you try to run a server and client on the same computer. When publishing, turn on at publish settings: “Run in Background” Else you won’t be able to connect to the server, unless you focus on the game instance running the server after client has been connected. Took me few minutes to find that issue 😉

NitroFingers

waldi

oh my god!!! took me an hour to finally read your comment. thank you so much this helped. got confused because the donwload of the entire project worked on one pc!?
File->Build Settings…->Player Settings…-> (Check) Run in Background*
🙂

Heya guys, just a suggestion: You don’t need to manually check to see whether the host list has been received in Update() every frame. Unity provides the OnMasterServerEvent() function for MonoBehaviours, which will be called for a number of events, including receiving the host list from the master server (http://docs.unity3d.com/Documentation/ScriptReference/MonoBehaviour.OnMasterServerEvent.html). Simply check the one argument that is received by that event to see if it equals MasterServerEvent.HostListReceived and you’ll know exactly when it has arrived.

That way you also don’t need to build any custom time out timer or anything (because in your current code ‘isRefreshingHostList’ would never return to being false if there are 0 hosts.

Nice write-up though, and good to see another Dutch studio working on a promising project!

I am having an issue with this tutorial. I am going through and get up to the part where I connect two players to each other and the problem I am having is that the position isn’t updating and on the client the player always look to be under the floor and flashing when they do move to the position they are in.
I have worked out that it is because the observed network view is set to the player position whereas in the downloaded example file it is set to a C# script how do I fix this?

Jens van de Water

If you’re at the stage of “spawning a player”, the player’s transform should be in the observed field. This is set automatically and does not require code to update the position.

For “state synchronization” the script needs to be added to the network view’s observed field. To do this, first add the C# script to the player object, so the component appears in the inspector. Now you are able to drag this component into the observed-field (overwriting the transform).

Yes, that’s it… I would suggest to put in the BOLD that you have to drag the component from the object not the script from the project 🙂 which was my case, and I wasn’t able to understand what’s wrong with it … 😉

Marcelo de Freitas Rigon

This tutorial is exactly what I’ve been looking for to get my feet under me with netcode. Thanks for writing it! One thing that tripped me up and might be useful to clarify; I’d assumed in the seralization section that you were in essence ‘asking’ the stream for a variable with a given name in the deserialization portion, so I didn’t catch on that it’s order, not name, that’s important. I spent a while trying to debug things before realizing that the clients were getting position and velocity swapped for things they don’t own. Explaining what’s going on there in more detail might be useful to people.

Stein Damen

Nathan

Hey nice tutorial, although i am having an issue with connecting both instances to the server. From within the build i can connect, but when i try to connect within the unity editor i get an error stating “Its not possible to register a host until it is running.” Help would be great 🙂

José Francisco Junior

This was really helpful for me- I learned how easy networking actually is! Thanks for the tips. By the way, I also converted all the scripts here to JavaScript, so if you’d like a JS version of both scripts I can provide such.

memoi

In case people are like me (did not learn the basics) and stumble upon the error “Player must be a prefab in the project view”.

– Drag the cube (Player) from Hierarchy in Assets.
– Delete the cube from the scene.
– In hierarchy select the Game Object where your Network Manager Script is attached.
– Drag the cube (Player) from Assets to the playerPrefab variable in the Inspector.

Daniel S

Daniel S

DAMN !! -.- ok i fixed it ! you should have mentioned that we have to drop the “player” script COMPONENT into the OBSERVED slot of the Network view -.-‘
When i followed this tutorial the standard value of the observed slot where “Player(Transform)” .. this does NOT work for me -.-‘

Dan

It’s not exactly standard procedure so drag an inspector script into a slot on its own gameobject in that way. Normally these slots are pre-typed, so you can drag on the gameobject itself and it’ll auto assign.

The tutorial needs to make this clearer.

Jens van de Water

At what point in the tutorial are you? Would make it easier to find possible errors. One idea that comes to mind is the synchronized values in OnSerializeNetworkView() are switched. If you send 2 Vector3 values (position first, velocity second) then you should receive them in the same order! Otherwise these values are mixed up. This would lead to the problem you’re describing, but it could be something else entirely.

Troy

This is excellent! Thank you guys so much for this tutorial – I’m super glad I backed your game on Kickstarter, and was actually disappointed it never got off the ground – I hope you guys find your funding and are able to make the game anyway!

Banini

I have one problem btw. One I connect 3 player, the last one move very quickly. I think, He get information of other player at the start (every one in the same x,y,z, so he fly on one direction. How to fix that ?

I had a similar problem even with 2 players: When I joined to the server the two cubes started at the spawn position in the joining player’s window which resulted to a collision making them bounce off each other. This started to happen after adding the Interpolation or Prediction, not sure which one. I fixed it like this:

Jens van de Water

Sorry, I don’t fully understand the problem you’re having. If you think it is due to the data received when joining, you chould check out whether or not it is buffered. For example, with RPCMode.OthersBuffered, a player that connects later will still receive this information.

Kararan

I followed the entire tutorial but it does not work, I am 100% positive I did everything correctly, and follow the tutorial about 4 times now to no avail. My issue is that when I build and run > start a server > join the server with the editor > start walking around in the non editor (the game) I do not see it updating the positions at all.

It only updates the position when I click on the window. So for example I move around in the editor, then click on the game I will see the character from the editor teleport to that position, it’s the same the other way around, it doesn’t update until it’s an active window.

I even went as far as copying the code from the project files provided but I have the same issue. Also maybe worth to note is that when I press on the “RoomName” to join the server nothing happens, unless i switch to the game, then back to the editor or the other way around, only then it will join.

mr.Doom

Devoted

If I wanted to would it be possible to put the server into a completely different build? So this way there is only one host. That handles lets say 1,000 players max.. ? Is it this method not good enough.

Ryan

First, Thanks so much for this awesome tutorial. I’m having a couple of problems though. I’m using my own character movement script which uses update to control all the character movement so I just put your if statement in the update function with the brackets surrounding everything in the function. It seems to be working fine, I just wanted to mention it in case it is the cause of one of these problems. So the first problem is that when I use two instances of the game, one to start and one to join a server, the player prefab drops in just fine on both but then when I switch from one instance of the build to another, I’m still only controlling the player prefab who most recently entered the server. The second problem is that I can’t implement the last half of this tutorial because Unity will not let me use a script in the “observed” portion of the Network View component. Very confusing. I’m in the latest Unity pro build (4.3.1f1). Thanks so much for this tutorial again and for taking some time to answer people’s questions. It’s going above and beyond. Any help is appreciated.

Ryan

Actually, it appears to be working correctly. I added in some objects to stand the players next to to see what was going on. Strangely, the cameras are switched. So the person who joins the server takes over the host camera and the host takes over the joined player’s camera… really weird. I have the camera embedded into the player prefab. Dunno if that’s why. Again… plz help!! 😉

Yeah, I have the same problem, sadly. I’m working on it right now and I have been working on it for about a week now. If you do find a solution, please share. If you’d like to talk with me about solving this issue, let me know.

I got it working. What you need to do is disable your camera and everything else. If you have mouse look, make sure you make a new script (or edit your current one) so that it doesn’t move the camera unless networkView.isMine. Do this in the Update() and Start() functions.

Now that you have it so that it checks if the camera is theirs and you also have it so that all components are disabled, do the following: Enable Character Controller, your MouseLook with networkView.isMine checking if its theirs, Mesh Renderer for changing the color of your object, Network View (Both on your prefab and the main camera attached to it), and now, make a new script that will also be enabled called “Network Instantiate”.

Network Instantiate should be on your prefab and the main camera attacked. The if statements can be used to differentiate between the two because your main camera will not have a character controller but it will have a camera component. Now that its enabled as soon as the prefab is instantiated, it belongs to whoever enabled it.

Ryan

Ryan

I’m not a coder by any stretch of the imagination. I’m sure I’m not just supposed to copy and paste the code you put for the Network Instantiate script but I’m not sure what I’m supposed to be inserting.

Ryan

Xirre,
Sorry. I figured it out. Looked up the proper usage of GetComponent in the docs and applied it to all the disabled components in the camera. I’m testing now and it appears to be working!. Thanks again.

Pykee

I would like some help with this as well. Any time a person joins my game, he/she takes control my my character and vise versa. How can I maintain camera and movement control when someone joins my game?

Jens van de Water

A quick notice from my side: Sorry for not replying to the questions lately, we are very busy at the moment and unable to help. I hope to have time as soon as possible to answer any remaining questions. Thanks for the compliments and support so far! 🙂

Thegameboy

I had the same problem. The funny but yet disturbing thing is that you’re not making that server. This tutorial’s code connects to Unity’s main server, so you’re picking up other people’s projects. Just change the typeName variable to something unique.

Paul

Thanks for the info. That is a little scary. I ran some code that just looked for available servers and six other servers showed up in addition to mine. I have since changed my code so it only looks for my unique server name.
I hope there’s no internet vulnerability involved with this type of thing.

Henris

Also how to change spawning player location? I tried putting transform.position = Vector.3(153,214,142) between the player.spawn()
But it teleport`s my network manager thing (plane) to the specified location, not the new player.
Help Please 🙂

Tuna

I think my problem is related. When it’s synced, it assumes the other box’s position from the origin of the spawn. So at 0 second, any other players joining to the server, will have all the other boxes(along with his own) at the origin(in this case, at 0, 5, 0). Since it’s rigidbodies, it’ll cause all the boxes to collide with each other. At first the other boxes will fly out, but it’ll go back to its correct synced position(since, on the server side, they don’t start off from the origin).

Spawning at the origin is not the problem, I think. The problem is, sync-ing other cubes always starts from the origin, instead of immediately taking their real current position. Whatever I change it to(changing to transform.localPosition) doesn’t fix it.

[…] small online multiplayer game using Photon Unity Networking (PUN). This project will be similar to “How to create an online multiplayer game with Unity”, but instead of the standard Unity networking, we will use […]

jeff77k

This tutorial uses the Master Server run by Unity which … maybe up … or maybe not. But you can download and run your own Master Server locally. Then in NetworkManager.cs add:

MasterServer.ipAddress = “127.0.0.1”;

There is lots of info out there on running your own Master Server and what the heck the Master Server is in first place =] . This hung me up the first time I tried to run this, otherwise great tutorial!

Alex

This is a great tutorial! I have one question though, for some reason when I try the movement interpolation, you cannot see the other players moving. However, the strange part is that if you turn away from the player so that the other play is no longer in the camera’s view, and then you look back, the position will now be updated to the actual current position. The other play will then remain to not move until you look away and look back again (somewhat like slender). I do not have any sort of script that is checking whether the player is looking at another player or not, so that cannot be the problem.

Baggaiver

Hi there, nice tutorial.
But i have a problem: i’m at “Spawning a player” and when i try to run 2 istances of the build i have the two player swapped each other.
I’m using the standard assets’ first person controller with the if(networkView.isMine) statement in every kind of Update of every script attached.
I’ve also tried a solution in an old comment above but nothing changed.
Can someone help me?

Steven

Great tutorial, but I’ve run into a wall implementing this with Physics2D.

I was able to get the players connected and spawning, and could see them both moving, until I got to the State Synchronization section. I can still connect, and see both players, but the clients only see their own player moving, the other player’s position is never updated.

This is the original State Synchronization code. What needs changing for Physics2D?

Does anyone know how to implement this in 2D? i changed rigidbody to rigidbody2D and it works fine but the second player slowly moves from where he is instantiated down to the ground. he cant be controlled and the other player is only visible in short blinks.

if i comment out the line: rigidbody2D.position = Vector3.Lerp(syncStartPos, syncEndPos, syncTime / syncDelay); The second player falls normally but i obviously dont have smooth motion.

Does anyone know what i can change in this line to make it work in 2D?
any suggestions warmly welcomed. seriously helpful tutorial btw!

Hello,
I have a similar project but i’m having problems when one of the players are behind a firewall or using a router with the port that i’m using closed. Do you know how can i workaround this? I heard about the NAT Punchthrough but i didn’t found a helpful (and easy) tutorial.
Thank you in advance, your tutorial is very good 🙂

Y05h1

how do I add auto respawn to this? When the players cube goes off the edge, after falling passed -10 in Y I would like to destroy the player the spawn again.
I added the folowing in the if test to the Update method in the NetworkManager:

Network.Destroy (playerPrefab.networkView.viewID);
SpawnPlayer();

but this results in error .. View ID Allocation: 0 not found during lookup

Hi. I am very new to the unity and I need to create a simple two-player game for my class’ final project. I plan to make a rock-paper-scissor game. What I want to know is how can I get the other layer input key (so I can determine who the winner is)? Thx. Anyway, great tutorial.

This is amazing! Thank you! I am stuck and i’m not sure why. Whenever player 2 joins player 1 and player 2 swap controls. (I am controlling my friends character on his screen and he has control of my screen). Has anyone ran into this??

Erik Sillén

Gad Hadd

Can anyone answer my question??
I had added an option to quit the server i.e. Network.disconnect();
Now when i disconnect, the player cubes remain there only and not get destroyed.
Can anyone tell me what to add to the script to destroy all player objects when server (not a client) is closed??? So that on clicking Start Server a new game is started…

Zan97

Thanks for this tutorial, but i have a problem: if i use the command “MasterServer.ipAddress = “127.0.0.1”;” to start the server in local, i get this error: “Failed to connect to master server at 127.0.0.1:23466”.
What can i do to resolve this? Thank you

[…] How to create an online multiplayer game with unity This tutorial will explain how multiplayer can be implemented using unity’s networking functionality. we didn’t work on an online multiplayer game before, and. […]

Nishanth Kumar

Hi i have been strucked in one aspect could anyone help me?.Assume 2 players joined in game and they travelled up to some distance then how to make 3rd player appear in the position of previous 2 players position.