Photon is a real-time multiplayer game development framework which has server and cloud services. Even though the networking implementation of the Kickstarter demo for LFG: The Fork of Truth was build with Unity networking, it was build with PUN in the back of our minds. An advantage of Photon is that it does not require hosting, so the player who created the room can leave the game without causing crashes at the clients. Also, PUN is more stable and the source code is available to fix any issues you might have. An important note is that the free version of Photon requires Unity Pro for iOS and Android. With Photon Plus a free Unity licence is sufficient.

For our projects, we prefer to implement the networking functionality early during development. This means for each new feature, we make sure it also works over the network. In the end this saves us a lot of time, because implementing it at a later stage results 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:

Installing the Photon Unity Networking package

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.

Installation

Before we can get into the code, we need to install PUN. You can find it here on the Asset Store. This package also contains several demos, which are not required for this tutorial, but can give you a nice idea what the possibilities are.

Once the project is downloaded, open the PUN Wizard (Window > Photon Unity Networking). Fill in your email and press send. You will receive a mail with a link to your account which contains an AppID. Copy this ID and paste it in the field “Your AppID”, set your cloud region and save these settings.

Network connection

Create a new script called NetworkManager and add it to an empty object in the scene. This script will handle the network connection and spawn connected players.

When we start the game, we want to connect to the Photon network. This will enable us to host and join rooms of our game based on the AppID. For the function call PhotonNetwork.ConnectUsingSettings() we can add the version number of the game as a string.

void Start()
{
PhotonNetwork.ConnectUsingSettings("0.1");
}

The boolean PhotonNetwork.connected only checks if we are connected to the Photon network itself, not if we are connected to another player’s server. As long as we’re not connected a text label is drawn to show us what the status is.

After the connection has been established, we need to connect to a room. A room can be created with the function call PhotonNetwork.CreateRoom(), where you have to define a unique room name. In the example below an unique ID is added to the name. Other parameters can be used to hide or close the room or set the maximum number of players.

For all rooms in the list, a button is added so it can be joined. This list is updated via the network message OnReceivedRoomListUpdate(), which is called automatically every time a room is added or removed.

After connecting to a room, either by creating or joining one, the function OnJoinedRoom() is called. Add a debug log here so we can see whether we are actually connected.

This is a good point to do our first test. 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, create a new build. Afterwards launch the game and press “Start Server”. Now we can test our new functionality in the Unity editor. After refreshing your list, another button should appear allowing you to connect the two instances of your game.

Spawning a player

Now that we are able to connect multiple players, we can 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 Photon view component to the player object (Component > Miscellaneous > Photon View). This will enable us to send data packages over the network to synchronize the player. Observe option 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”. Drag the transform component into the observe field. At the bottom the field serialization is now added, where you can state which values from the transform to synchronize. Set its value to “Only Position”.

In the hierarchy, create a folder called “Resources”. In it, place the player object from the scene to make it a prefab. This is needed to instantiate the object on the network.

In the NetworkManager-script add a public game object variable for the player prefab. When OnJoinedRoom() is called, we want to create the player object. Instantiating on a Photon network requires the prefab to be placed in the Resources folder. The prefab parameter for PhotonNetwork.Instantiate() is a string instead of a game object.

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. In the player script change “Monobehaviour” to “Photon.Monobehaviour” to allow Photon networking in this class.

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 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 performed actions.

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. InOnPhotonSerializeView() 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 Photon view component on the player’s prefab. The observed field contains the component that will be synchronized. Drag the component of the player script in the observed field so we can write our own synchronization method.

Add OnPhotonSerializeView() to the player script. This function is automatically called every time it either sends or receives data. If the user is the one updating the object, he writes to the stream. This occurs automatically based on the sendrate. The clients receive the stream and can apply the data that was send to the object. In the example below, the user sends the rigidbody’s position with stream.SendNext() and this is received by the clients with stream.ReceiveNext(). The order of data send should be the same as the order in which the data is received, otherwise values will get 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. 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.

OnPhotonSerializeView() needs to be extended 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 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 where we used these 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, vectors and quaternions. So not all parameters can be sent, but this can be solved. For example a color can be send by converting it to a vector or quaternion.

An RPC is sent by calling photonView.RPC(), in which you define the function name and parameters. Also the Photon targets are 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.

Now create another build and if you run the game, you will see the player’s colors can be changed.

Conclusion

I hope you enjoyed reading this short tutorial about Photon Unity Networking.

The game now contains the functionality to create or join a Photon room, instantiate a player after the connecting is established, and move around individually. With these techniques you should be able to extend the game with other mechanics, like player rotations and shooting projectiles.

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

Jens

First very nice tutorial series but I have question regarding OnPhotonSerializeView() method. It doesn’t called every time for me. I can’t able to understand paragraph written above it. Please help me in implementation.

ryan

Very good your text you should be congratulated, I wonder why when a new player connects to the server, I see all the objects in the center, then en sigida osvejo being taken to the correct spots in the world.

heemun

m0rgul

I am actually working on some sort of MMO game and I stumbled upon PUN, it seems nice so I decided to use this.

So far so good 😛 I created 2 scenes, the first is a character customization screen, the second is some sort of an arena where the characters can punch each other in the face.

In the first scene I can select between a male or a female character, chose from 3 wardrobes and color their wardrobes. Upon hitting Save, I store these options in a custom object, serialize the object and store it in PlayerPrefs. I load up the next scene and I’m supposed to apply the options to the spawned character…however…

The prefabs for the players are 1 generic male with 3 wardrobes equiped (subobjects – I pick one and destroy the other two, then apply the color to the chosen one). My problem was (before I came here) that I would see my character as I customized it, but the other characters I just saw as the default prefab.

I actually went through your tutorial with the cubes and it seems to be working well, but I do have one question: if we call the RPC on update, isn’t that somewhat unnecessary? Doesn’t it generate a lot of traffic for nothing? I tried calling the RPC on start, but apparently it doesn’t work like that.

Hi m0rgul, did you say you typed the code in this tutorial and it is working fine for you?

Unfortunately, it doesn’t seem to work for me. It looks like I am having similar issues that also rileydabozo: “When finished, and two people are in the game, it shows two squares, but one never moves, one is yours, then when the other player moves his it shows a flashing box moving”, and Claudio Fernandes: “Very good your text you should be congratulated, I wonder why when a new player connects to the server, I see all the objects in the center, then immediately being taken to the correct spots in the world.” (I very slightly edited both posts.)

This is what I see:
(1) I start the server and one cube is present in the center of the plane.
(2) I can WASD around the cube with no problems and no visible glitches.
(3) In another window I can join the existing room spawned in (1) and a second cube appears in the center of the plane.
(4) Now things look visually incorrect: when I move the first cube, I see the cube appearing in the correct position in the second window, but as soon as I stop WASD-ing around the first cube in the second window immediately moves back to the center of the plane. Viceversa, when I move the second cube, I see it flashing in the first window, but as soon as I stop WASD-ing it around, it goes back to the center of the first window.

Reading articles over the internet, I think I am hitting the issue that PhysX runs independently in both processes (associated to the the windows mentioned above) and they tend to overwrite the position of each other’s cubes. Did you modify the code in this tutorial to fix the issue I just described? If so, can you please share the solution? RIght now, as far as I can tell, this tutorial is nice up to a certain point, and then is conceptually broken.

m0rgul

Yes, it is working for me…furthermore, I managed to solve my own problem using things from this tutorial (tho there is still one thing I haven’t been able to do, and that is make an RPC with more than one parameter – i was trying to send an integer and a Vector 3 and…no luck, I just split that into two functions and it works).

The one problem that I did have was the fact that after spawning the second cube, the first one started falling through the plane…To this moment I still have no idea why, but I don’t really need to know anyway 😛

m0rgul

Nice tutorial by the way. I have been using Unity and Photon to make a multiplayer game where players can run around and bump into eachother real time. At the moment I am running the physics through the Rigidbody class but it’s not responding to collisions properly. I was thinking about RPCs to register major events like player collisions. Is there any chance you can write something about handling real time collisions over a network?

Riz

Jocko,I was trying to drag the PlayerScript component into the Photonview ‘Observe box’ Component ,but when I put the cursor on the Observe box appears a prohibition sign .
I’m desperate,do you know how to fix it ?

How to create an online multiplayer game with Photon Unity Networking – Paladin Studios

[…]My 4s did same thing. When you get to the end reboot and stuck in Pangu welcome loop plug your head phones into headphone jack and wait, it will break the loop and finish jailbreak… Do it, seriously!!![…]

Dennis

I searched the web for this issue and more people have problems with it. And weird thing is that PhotonNetwork.CreateRoom(roomName + Guid.NewGuid().ToString(“N”), true, true, 5); gives problem at GUID, and there are people that also have this line and it works with them, I have downloaded the PUN from asset store but still don’t work. It’s fixing error after error, that’s why better put a working basic script online. Even the first 4 lines at beginning of this tutorial don’t compile. All is C# so i am not working in a java script.

Devarshi

Marshall

Hi, I’m having a problem. I followed through the tutorial and did synchronization and everything however, when I play with two windows, I can’t see the other player moving. He just sits there on the other screen when I move him.

theOtherHolmes

Same problem as previous two posts, I think. I’m at the stage of the tutorial where it says this: “Afterwards launch the game and press ‘Start Server’. Now we can test our new functionality in the Unity editor. After refreshing your list, another button should appear allowing you to connect the two instances of your game.” I made a test build version, started it, clicked “Start Server,” and that button goes away, but nothing changes on the instance of the app running through the Unity editor (still has its own “Start Server” button). How does the “refreshing your list” part happen?

Specifically defining the TypedLobby and adding the OnConnectedToMaster callback that then calls JoinLobby(lobbyName), don’t forget to also add lobbyName to the last argument on CreateRoom. I’ve used the updated syntax for CreateRoom as well since the way in this how-to has since been deprecated.

Jonie

Hello and sorry for stupid question. I’m complete your example. it’s really good. And now I want to replace my cubes to FirstPersonController from asset. Unfortunately. What I doing wrong? How to make it to work? Guess that problem in PhotonView.isMain() function but I cannot how to add it to my first person player. Whole day and nothing.

Towtow10

ankit dave

hey friends i can’t destroy my gameobject.
i made one simple pong game it work perfect on one player but in two player how can i destroy my ball which is prefab when it collides to empty gameobject
i tried PhotonNetwork.Destroy() but it is not working so i used punrpc

[…] How to create an online multiplayer game with Photon Unity … – In this tutorial we will create a small online multiplayer game using Photon Unity Networking (PUN). This project will be similar to “How to create an online … […]

[…] How to create an online multiplayer game with Photon … – In this tutorial we will create a small online multiplayer game using Photon Unity Networking (PUN). This project will be similar to “How to create an online … […]

Mateo

Hello! I am trying to shoot in direction of Mouse Pos. But teh result always is the same – in some situations Bullet goes to a little bit different pos and the collision is detected on each client in different position. Could someone help me ?

Anyone know of a sample that is a little more friendly by chance. A beginner like myself gets discouraged instantly when it comes to the coding of unity. I’ve gotten the photon networking, looked at the samples even but trying any of it out…. nope. Never works for me. Even paid to get ripped off on fiverr for a guy to look at that angry bots multiplayer sample for me to change it from top down to fps and no go at all still 2 months later.

Henry Choi

I am having a bit strange situation happen. I download and load up PUN at first, typed in all the scripts and I exit Unity. After that, when I startup Unity again, the PUN functions in Component and Window menu went missing and could not load the PUN functions again. Is there anyone have the same issue ?