How to Integrate Photon Pun Multiplayer Game Engine For Unity

Photon Pun Multiplayer Game Engine For Unity

Photon is a real-time multiplayer game development framework which has server and cloud services. Games are hosted in globally distributed Photon Cloud so we do not need to worry about hosting. All the operations related to multiplayer functionality are handled by Photon. Free version of PUN comes with 20 CCU subscription but you can upgrade it as you need it. More info can be found on their official site (https://www.photonengine.com/en-US/Photon).

In this tutorial we are going to cover in detail the integration of Photon Unity Networking (PUN) with Unity.

Prerequisites

1

Unity 5 or later , you can download from here (https://unity3d.com/)

2

Photon Unity Networking (PUN) package, you can download free package from Asset store from here (https://www.assetstore.unity3d.com/en/#!/content/1786)

In this tutorial we are using Unity 5.6.3.

This tutorial covers following major things:

1

Configure game on photon

2

Setup photon in Unity project

3

Manage connection with photon

4

Synchronization of players

5

Testing

#1

Configure Game on Photon

Step-1 : Login to account

We need an account on Photon to configure our game .Go to https://www.photonengine.com/en-US/Photon. Log in to your account if you already have otherwise you can click on Register to create new account.

Login

[Login with Your id on Photon]

Step-2 : Create and configure game

Create-a-New-App

[Configure game]

Provide necessary info related to your game and create game.

Create-app-provide-info

[Create game]

Step-3 : Get your App Id from dashboard

Get-your-App-ID

[Get app id]

We have completed the setup on Photon side.

#2

Setup Photon in Unity Project

Before we start, download the PUN package from the Asset store (https://www.assetstore.unity3d.com/en/#!/content/1786). After download, open PUN settings to configure the project for photon. You can find PUN settings under window -> Photon Unity Networking -> Highlight server settings.

Photon-Setup-PUN

[Photon Setup]

It will open a setting wizard that allows to configure project with photon. Add your app in in the App id field of PhotonServerSettings. Set your appropriate region. Check AutoJoinLobby so photon will automatically join the player in the lobby.

Photon-Server-Settings

[Photon Server Settings]

#3

Manage connection with Photon

Create a new script called PUNManager and add it to an empty gameobject. Using this script we will handle all server side operations. First of all, we need to connect with Photon. This will enable us to host and join rooms of our game based on the AppID. Call PhotonNetwork.ConnectUsingSettings () method to make connection. We can pass the version number of the game as a string in the argument. Import namespace Photon.

Use following code to connect your project with photon:

using Photon;


public class PUNManager : PunBehaviour

{
    void Awake ()

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

To check status whether we are connected or not, take a Text and show status using PhotonNetwork. connectionStateDetailed. After connection, photon will automatically join player in a lobby. When a player joins a lobby OnJoinedLobby callback is called. After that we need to connect to a room. PhotonNetwork.JoinRandomRoom  is allow to connect players in a random available room. If no room available it will create a new one. We can also use PhotonNetwork.JoinRoom to connect if we already know the room name. In this tutorial we will use JoinRandomRoom. We will create a button that will handle this event. Add the JoinRoom method in buttons click event in Inspector. After connecting to a room, the function OnJoinedRoom is called.

Add following code to PUNManager script:

using UnityEngine;

using System.Collections;

using Photon;

using UnityEngine.UI;



public class PUNManager : PunBehaviour

{
    public GameObject joinRoomBtn;

    public Text status; 

    void Awake ()
    {

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

    {
        joinRoomBtn.SetActive (false);

    } 
void Update ()

{

    if(!PhotonNetwork.connected)

    {

        status.text=PhotonNetwork.connectionStateDetailed.ToString();

    }
}
public override void OnJoinedLobby ()

{
    joinRoomBtn.SetActive (true);

    status.text = "Connected";

    Debug.Log ("Joined lobby: " + PhotonNetwork.lobby.Name);

}
   
public void JoinRoom ()

{

    status.text = "Connecting to room..";

    PhotonNetwork.JoinRandomRoom ();

} 
public override void OnJoinedRoom ()

{
    Debug.Log ("Connected to Room");
}

#4

Synchronization

As now we are able to connect multiple players, add some game coding to the project. Create a plane and position it with (0, -0.5f, 0). Create a cube, name it Player. Create a script PlayerManager that will handle players’ movements.

Add following code in PlayerManager:

public float speed = 3f;

void Update ()

{

    InputMovement ();

}

void InputMovement ()

{
    if(Input.GetKey (KeyCode.W))

    {

        Vector3 pos = new Vector3 (0, 0, speed * Time.deltaTime);

        transform.Translate (pos);

    }

    if(Input.GetKey (KeyCode.S))

    {

        Vector3 pos = new Vector3 (0, 0,-(speed * Time.deltaTime));

        transform.Translate (pos);

    }
    if(Input.GetKey (KeyCode.D))

    {

        Vector3 pos = new Vector3 (speed * Time.deltaTime, 0, 0);

        transform.Translate (pos);

    }
    if(Input.GetKey (KeyCode.A))

    {
        Vector3 pos = new Vector3 (-(speed * Time.deltaTime), 0, 0);

        transform.Translate (pos);
    }
}

Add this script to Player gameobject. Next, add the Photon view component to the player object (Component > 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. 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 project 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 PUNManager-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 gameobject. Also create a gameobject plane that can be activated when connected to room.

public GameObject playerPrefab; 
public GameObject Plane; 

public override void OnJoinedRoom ()

{
    Debug.Log ("Connected to Room");
    joinRoomBtn.SetActive (false);

    Plane.SetActive (true);

    PhotonNetwork.Instantiate (playerPrefab.name, Vector3.zero,
    Quaternion.identity, 0);

}

Make a build of project and run both. You can control both players movement, not just your own. To Solve this issue, we need to check is this a player that instantiated the object? To implement this , we need to check whether the object on the network “is mine” in the player script. Add using Photon; namespace and replace Monobehaviour class with PunBehaviour:

public class Player : PunBehaviour

{

    void Update ()
    {

        if(photonView.isMine)

        {

            InputMovement ();

        }
    } 
}

Again make a build and test your code. You can now control only one of the players.

#5

State Synchronization

For synchronization between players, photon provides a callback OnPhotonSerializeView() that constantly updates values over the network. This method is useful to sync data that changes often like player movement.

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.

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/she writes to the stream. The clients receive the stream and can apply the data that was send to the object. In the example below, the user sends the transform’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.

void OnPhotonSerializeView(PhotonStream stream,PhotonMessageInfo info)

{
    if(stream.isWriting)

        stream.SendNext (transform.position);
    else
        transform.position = (Vector3)stream.ReceiveNext ();
}

#6

Remote Procedure Calls

Another method of network communication is Remote Procedure Calls (RPCs), which is more useful for data that does not constantly change. This approach is only able to send integers, floats, strings, vectors and quaternions. An RPC is sent by calling photonView.RPC(), in which you define the function name and parameters. Also the Photon targets are required: “PhotonTargets.All” sends the data to all players including local player, “PhotonTargets.OthersBuffered” to everyone in the room except yourself. Add [PunRPC] before function implementation to make it a Remote Procedure Call. To implement this we will create a method that will change the color of the material of your own player.

Add following lines of code in PlayerManager script:

void Update ()

{

    if(photonView.isMine)
    {
        InputColorChange ();

        InputMovement ();

    }

} 
private void InputColorChange ()

{
    if(Input.GetKeyDown (KeyCode.R))

        ChangeColorTo (new Vector3 (Random.Range (0f, 1f),
        Random.Range(0f, 1f), Random.Range (0f, 1f)));

    }

}

[PunRPC]

void ChangeColorTo (Vector3 color)

{

    transform.GetComponent<Renderer> ().material.color = new
    Color (color.x, color.y, color.z, 1f);


    if(photonView.isMine)

        photonView.RPC("ChangeColorTo",PhotonTargets.OthersBuffered,color);

}

To send data to all the players in room we need to use PhotonTargets. All as target when calling a RPC method. Take a Text and a button that will handle this event. Add PhotonView component to the PUNManager gameobject from Component > Photon View. Create a method named ChangeTextColor and add it in button click event in inspector.

Add the following code in the PUNManager script:

public Text title;

public GameObject ChangeColorBtn;

void Start ()

{

    joinRoomBtn.SetActive (false);

    Plane.SetActive (false);

    ChangeColorBtn.SetActive (false);
    title.gameObject.SetActive (false);

}
public void ChangeTextColor ()

{
    Vector3 color = new Vector3 (Random.Range (0f, 1f),
    Random.Range (0f, 1f), Random.Range (0f, 1f));


    view.RPC ("ChangeTextColorTo", PhotonTargets.All, color);

}


[PunRPC]

void ChangeTextColorTo (Vector3 color)

{
    title.color = new Color (color.x, color.y, color.z, 1f);

    Debug.Log ("Color Called");

}

Make a new build and test it. Click on the “Change color ” button and you can see that the text color is changing on both side.

#7

Testing

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

You can find the complete code here:

PhotonPun Join Room
Ketan Jogadiya
Ketan is a Unity3D game developer at Nimblechapps, a Unity 2D/3D game development company based in India. He is passionate about new technologies in mobile games. He is game developer by day and writer by night.