Photon Network (Classic) Guida per principianti

Photon Network è un servizio per Unity che consente agli sviluppatori di creare giochi multiplayer in tempo reale.

Fornisce una API potente e facile da usare che la rende perfetta anche per gli sviluppatori alle prime armi.

In questo post, eseguiremo il download dei file necessari, l'impostazione di Photon AppID e la programmazione di un semplice esempio multiplayer.

Parte 1: configurazione della rete Photon

Il primo passo è scaricare il pacchetto Photon Network da Asset Store. Contiene tutti gli script ei file necessari per l'integrazione multiplayer.

  • Apri il tuo Unity progetto quindi vai a Asset Store: (Finestra -> Generale -> AssetStore ) o premere Ctrl+9
  • Cerca "Photon Unity Networking Classic - Free", quindi fai clic sul primo risultato o fai clic qui
  • Importa il pacchetto Photon al termine del download

  • Dopo che il pacchetto è stato importato, devi creare un Photon App ID, questo viene fatto sul loro sito web: https://www.photonengine.com/
  • Crea un nuovo account (o accedi al tuo account esistente)
  • Vai alla pagina Applicazioni facendo clic sull'icona del profilo e poi su "Your Applications" oppure segui questo link: https://dashboard.photonengine.com/en-US/PublicCloud
  • Nella pagina Applicazioni fare clic su "Create new app"

  • Nella pagina di creazione, per Tipo di fotone seleziona "Photon Realtime" e per Nome, digita un nome qualsiasi, quindi fai clic su "Create"

Come puoi vedere, l'applicazione utilizza per impostazione predefinita il piano gratuito. Puoi leggere ulteriori informazioni sui Piani tariffari qui

  • Una volta creata l'applicazione, copia l'ID app che si trova sotto il nome dell'app

  • Torna al tuo progetto Unity quindi vai a Finestra -> Photon Unity Networking -> Procedura guidata PUN
  • Nella procedura guidata PUN fai clic su "Setup Project", incolla il tuo ID app e fai clic "Setup Project"
  • La rete Photon è ora pronta

Parte 2: Creazione di un gioco multiplayer

Ora passiamo alla parte in cui creiamo effettivamente un gioco multiplayer.

Il modo in cui il multiplayer viene gestito in Photon è:

  • Per prima cosa, ci colleghiamo alla Photon Region (es. USA East, Europe, Asia, ecc.) che è anche conosciuta come Lobby.
  • Una volta nella Lobby, richiediamo tutte le stanze create nella regione, quindi possiamo unirci a una delle stanze o creare la nostra stanza.
  • Dopo esserci uniti alla stanza, richiediamo un elenco dei giocatori connessi alla stanza e creiamo un'istanza delle loro istanze del giocatore, che vengono quindi sincronizzate con le loro istanze locali tramite PhotonView.
  • Quando qualcuno lascia la stanza, la sua istanza viene distrutta e viene rimosso dall'elenco dei giocatori.

1. Creazione di una lobby

Iniziamo con la creazione di un MainMenu che conterrà una logica della Lobby (esplorazione delle stanze esistenti, creazione di nuove stanze, ecc.).

  • Crea una nuova scena e chiamala "MainMenu"
  • Crea un nuovo script C# e chiamalo GameLobby
  • Nella scena MainMenu crea un nuovo GameObject. Chiamalo "_GameLobby" e allegagli lo script GameLobby

Ora apri lo script GameLobby.

Innanzitutto, creiamo tutte le variabili necessarie:

    //Our player name
    string playerName = "Player 1";
    //This client's version number. Users are separated from each other by gameversion (which allows you to make breaking changes).
    string gameVersion = "0.9";
    //The list of created rooms
    RoomInfo[] createdRooms = new RoomInfo[0];
    //Use this name when creating a Room
    string roomName = "Room 1";
    Vector2 roomListScroll = Vector2.zero;
    bool joiningRoom = false;

La prossima cosa che dobbiamo fare è abilitare l'accesso automatico alla lobby e alle statistiche della lobby, questo ci consentirà di ricevere l'elenco delle stanze. Questo viene fatto nel void Start().

Inoltre, abilitiamo automaticamenteSyncScene in modo che la scena venga sincronizzata automaticamente una volta entrati nella stanza.

Infine, chiamiamo PhotonNetwork.ConnectUsingSettings per connettersi.

    // Use this for initialization
    void Start()
    {
        //Automatically join Lobby after we connect to Photon Region
        PhotonNetwork.PhotonServerSettings.JoinLobby = true;
        //Enable Lobby Stats to receive the list of Created rooms
        PhotonNetwork.PhotonServerSettings.EnableLobbyStatistics = true;
        //This makes sure we can use PhotonNetwork.LoadLevel() on the master client and all clients in the same room sync their level automatically
        PhotonNetwork.automaticallySyncScene = true;

        if (!PhotonNetwork.connected)
        {
            // Connect to the photon master-server. We use the settings saved in PhotonServerSettings (a .asset file in this project)
            PhotonNetwork.ConnectUsingSettings(gameVersion);
        }
    }

Per sapere se una connessione a Photon Cloud è andata a buon fine dobbiamo implementare queste 2 callback: OnReceivedRoomListUpdate() e OnFailedToConnectToPhoton(parametri oggetto).

    void OnFailedToConnectToPhoton(object parameters)
    {
        Debug.Log("OnFailedToConnectToPhoton. StatusCode: " + parameters + " ServerAddress: " + PhotonNetwork.ServerAddress);
        //Try to connect again
        PhotonNetwork.ConnectUsingSettings(gameVersion);
    }

    void OnReceivedRoomListUpdate()
    {
        Debug.Log("We have received the Room list");
        //After this callback, PhotonNetwork.GetRoomList() becomes available
        createdRooms = PhotonNetwork.GetRoomList();
    }

La prossima è la parte dell'interfaccia utente, dove vengono eseguite la navigazione e la creazione della stanza:

Lobby della rete fotonica

E infine implementiamo altre 4 callback: OnPhotonCreateRoomFailed(), OnPhotonJoinRoomFailed(object[] cause), OnCreatedRoom() e OnJoinedRoom().

Queste richiamate vengono utilizzate per determinare se ci siamo uniti/abbiamo creato la stanza o se ci sono stati problemi durante la connessione.

    void OnPhotonCreateRoomFailed()
    {
        Debug.Log("OnPhotonCreateRoomFailed got called. This can happen if the room exists (even if not visible). Try another room name.");
        joiningRoom = false;
    }

    void OnPhotonJoinRoomFailed(object[] cause)
    {
        Debug.Log("OnPhotonJoinRoomFailed got called. This can happen if the room is not existing or full or closed.");
        joiningRoom = false;
    }

    void OnCreatedRoom()
    {
        Debug.Log("OnCreatedRoom");
        //Set our player name
        PhotonNetwork.playerName = playerName;
        //Load the Scene called GameLevel (Make sure it's added to build settings)
        PhotonNetwork.LoadLevel("GameLevel");
    }

    void OnJoinedRoom()
    {
        Debug.Log("OnJoinedRoom");
    }

Ed ecco lo script GameLobby.cs finale:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class GameLobby : MonoBehaviour
{
    //Our player name
    string playerName = "Player 1";
    //This client's version number. Users are separated from each other by gameversion (which allows you to make breaking changes).
    string gameVersion = "0.9";
    //The list of created rooms
    RoomInfo[] createdRooms = new RoomInfo[0];
    //Use this name when creating a Room
    string roomName = "Room 1";
    Vector2 roomListScroll = Vector2.zero;
    bool joiningRoom = false;

    // Use this for initialization
    void Start()
    {
        //Automatically join Lobby after we connect to Photon Region
        PhotonNetwork.PhotonServerSettings.JoinLobby = true;
        //Enable Lobby Stats to receive the list of Created rooms
        PhotonNetwork.PhotonServerSettings.EnableLobbyStatistics = true;
        //This makes sure we can use PhotonNetwork.LoadLevel() on the master client and all clients in the same room sync their level automatically
        PhotonNetwork.automaticallySyncScene = true;

        if (!PhotonNetwork.connected)
        {
            // Connect to the photon master-server. We use the settings saved in PhotonServerSettings (a .asset file in this project)
            PhotonNetwork.ConnectUsingSettings(gameVersion);
        }
    }

    void OnFailedToConnectToPhoton(object parameters)
    {
        Debug.Log("OnFailedToConnectToPhoton. StatusCode: " + parameters + " ServerAddress: " + PhotonNetwork.ServerAddress);
        //Try to connect again
        PhotonNetwork.ConnectUsingSettings(gameVersion);
    }

    void OnReceivedRoomListUpdate()
    {
        Debug.Log("We have received the Room list");
        //After this callback, PhotonNetwork.GetRoomList() becomes available
        createdRooms = PhotonNetwork.GetRoomList();
    }

    void OnGUI()
    {
        GUI.Window(0, new Rect(Screen.width/2 - 450, Screen.height/2 - 200, 900, 400), LobbyWindow, "Lobby");
    }

    void LobbyWindow(int index)
    {
        //Connection Status and Room creation Button
        GUILayout.BeginHorizontal();

            GUILayout.Label("Status: " + PhotonNetwork.connectionStateDetailed);

            if(joiningRoom || !PhotonNetwork.connected)
            {
                GUI.enabled = false;
            }

            GUILayout.FlexibleSpace();

            //Room name text field
            roomName = GUILayout.TextField(roomName, GUILayout.Width(250));

            if (GUILayout.Button("Create Room", GUILayout.Width(125)))
            {
                if (roomName != "")
                {
                    joiningRoom = true;

                    RoomOptions roomOptions = new RoomOptions();
                    roomOptions.IsOpen = true;
                    roomOptions.IsVisible = true;
                    roomOptions.MaxPlayers = (byte)10; //Set any number

                    PhotonNetwork.JoinOrCreateRoom(roomName, roomOptions, TypedLobby.Default);
                }
            }

        GUILayout.EndHorizontal();

        //Scroll through available rooms
        roomListScroll = GUILayout.BeginScrollView(roomListScroll, true, true);

            if(createdRooms.Length == 0)
            {
                GUILayout.Label("No Rooms were created yet...");
            }
            else
            {
                for(int i = 0; i < createdRooms.Length; i++)
                {
                    GUILayout.BeginHorizontal("box");
                    GUILayout.Label(createdRooms[i].Name, GUILayout.Width(400));
                    GUILayout.Label(createdRooms[i].PlayerCount + "/" + createdRooms[i].MaxPlayers);

                    GUILayout.FlexibleSpace();
                
                    if (GUILayout.Button("Join Room"))
                    {
                        joiningRoom = true;

                        //Set our Player name
                        PhotonNetwork.playerName = playerName;

                        //Join the Room
                        PhotonNetwork.JoinRoom(createdRooms[i].Name);
                    }
                    GUILayout.EndHorizontal();
                }
            }

        GUILayout.EndScrollView();

        //Set player name and Refresh Room button
        GUILayout.BeginHorizontal();

            GUILayout.Label("Player Name: ", GUILayout.Width(85));
            //Player name text field
            playerName = GUILayout.TextField(playerName, GUILayout.Width(250));

            GUILayout.FlexibleSpace();

            GUI.enabled = PhotonNetwork.connectionState != ConnectionState.Connecting && !joiningRoom;
            if (GUILayout.Button("Refresh", GUILayout.Width(100)))
            {
                if (PhotonNetwork.connected)
                {
                    //We are already connected, simply update the Room list
                    createdRooms = PhotonNetwork.GetRoomList();
                }
                else
                {
                    //We are not connected, estabilish a new connection
                    PhotonNetwork.ConnectUsingSettings(gameVersion);
                }
            }

        GUILayout.EndHorizontal();

        if (joiningRoom)
        {
            GUI.enabled = true;
            GUI.Label(new Rect(900/2 - 50, 400/2 - 10, 100, 20), "Connecting...");
        }
    }

    void OnPhotonCreateRoomFailed()
    {
        Debug.Log("OnPhotonCreateRoomFailed got called. This can happen if the room exists (even if not visible). Try another room name.");
        joiningRoom = false;
    }

    void OnPhotonJoinRoomFailed(object[] cause)
    {
        Debug.Log("OnPhotonJoinRoomFailed got called. This can happen if the room is not existing or full or closed.");
        joiningRoom = false;
    }

    void OnCreatedRoom()
    {
        Debug.Log("OnCreatedRoom");
        //Set our player name
        PhotonNetwork.playerName = playerName;
        //Load the Scene called GameLevel (Make sure it's added to build settings)
        PhotonNetwork.LoadLevel("GameLevel");
    }

    void OnJoinedRoom()
    {
        Debug.Log("OnJoinedRoom");
    }
}

2. Creazione di un prefabbricato Player

Nei giochi multiplayer, l'istanza Player ha 2 lati: Locale e Remoto.

Un'istanza locale è controllata localmente (da noi).

L'istanza remota, d'altra parte, è una rappresentazione locale di ciò che sta facendo l'altro giocatore. Non dovrebbe essere influenzato dal nostro input.

Per determinare se l'istanza è locale o remota, utilizziamo un componente PhotonView.

PhotonView funge da messenger che riceve e invia i valori che devono essere sincronizzati, ad esempio posizione e rotazione.

Quindi iniziamo creando l'istanza del giocatore (se hai già la tua istanza del giocatore pronta, puoi saltare questo passaggio).

Nel mio caso, l'istanza Player sarà un semplice cubo che viene spostato con i tasti W e S e ruotato con i tasti A e D.

Istanza del lettore di rete Photon

Ed ecco un semplice script del controller:

PlayerController.cs

using UnityEngine;

public class PlayerController : MonoBehaviour
{
    // Update is called once per frame
    void Update()
    {
        //Move Front/Back
        if (Input.GetKey(KeyCode.W))
        {
            transform.Translate(transform.forward * Time.deltaTime * 2.45f, Space.World);
        }
        else if (Input.GetKey(KeyCode.S))
        {
            transform.Translate(-transform.forward * Time.deltaTime * 2.45f, Space.World);
        }

        //Rotate Left/Right
        if (Input.GetKey(KeyCode.A))
        {
            transform.Rotate(new Vector3(0, -14, 0) * Time.deltaTime * 4.5f, Space.Self);
        }
        else if (Input.GetKey(KeyCode.D))
        {
            transform.Rotate(new Vector3(0, 14, 0) * Time.deltaTime * 4.5f, Space.Self);
        }
    }
}

Il passaggio successivo consiste nell'aggiungere un componente PhotonView.

  • Aggiungi un componente PhotonView a Player Instance
  • Crea un nuovo script C#, chiamalo PlayerNetworkSync e aprilo (questo script verrà utilizzato per comunicare tramite PhotonView)

La prima cosa che dobbiamo fare è sostituire MonoBehaviour con Photon.MonoBehaviour. Questo passaggio è necessario per poter utilizzare la variabile photonView memorizzata nella cache invece di utilizzare GetComponent<PhotonView>().

public class PlayerNetworkSync : Photon.MonoBehaviour

Successivamente, possiamo passare a creare tutte le variabili necessarie:

    //List of the scripts that should only be active for the local player (ex. PlayerController, MouseLook etc.)
    public MonoBehaviour[] localScripts;
    //List of the GameObjects that should only be active for the local player (ex. Camera, AudioListener etc.)
    public GameObject[] localObjects;
    //Values that will be synced over network
    Vector3 latestPos;
    Quaternion latestRot;

Quindi nel vuoto Start() controlliamo se il giocatore è locale o remoto utilizzando photonView.isMine:

    // Use this for initialization
    void Start()
    {
        if (photonView.isMine)
        {
            //Player is local
        }
        else
        {
            //Player is Remote
            for(int i = 0; i < localScripts.Length; i++)
            {
                localScripts[i].enabled = false;
            }
            for (int i = 0; i < localObjects.Length; i++)
            {
                localObjects[i].SetActive(false);
            }
        }
    }

La sincronizzazione effettiva viene eseguita tramite il callback di PhotonView: OnPhotonSerializeView(flusso PhotonStream, informazioni PhotonMessageInfo):

    void OnPhotonSerializeView(PhotonStream stream, PhotonMessageInfo info)
    {
        if (stream.isWriting)
        {
            //We own this player: send the others our data
            stream.SendNext(transform.position);
            stream.SendNext(transform.rotation);
        }
        else
        {
            //Network player, receive data
            latestPos = (Vector3)stream.ReceiveNext();
            latestRot = (Quaternion)stream.ReceiveNext();
        }
    }

In questo caso, inviamo solo la posizione e la rotazione del giocatore, ma puoi utilizzare l'esempio sopra per inviare qualsiasi valore necessario per essere sincronizzato sulla rete, ad alta frequenza.

I valori ricevuti vengono quindi applicati nel void Update():

    // Update is called once per frame
    void Update()
    {
        if (!photonView.isMine)
        {
            //Update remote player (smooth this, this looks good, at the cost of some accuracy)
            transform.position = Vector3.Lerp(transform.position, latestPos, Time.deltaTime * 5);
            transform.rotation = Quaternion.Lerp(transform.rotation, latestRot, Time.deltaTime * 5);
        }
    }

Ecco lo script PlayerNetworkSync.cs finale:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class PlayerNetworkSync : Photon.MonoBehaviour
{
    //List of the scripts that should only be active for the local player (ex. PlayerController, MouseLook etc.)
    public MonoBehaviour[] localScripts;
    //List of the GameObjects that should only be active for the local player (ex. Camera, AudioListener etc.)
    public GameObject[] localObject;
    //Values that will be synced over network
    Vector3 latestPos;
    Quaternion latestRot;

    // Use this for initialization
    void Start()
    {
        if (photonView.isMine)
        {
            //Player is local
        }
        else
        {
            //Player is Remote
            for(int i = 0; i < localScripts.Length; i++)
            {
                localScripts[i].enabled = false;
            }
            for (int i = 0; i < localObject.Length; i++)
            {
                localObject[i].SetActive(false);
            }
        }
    }

    void OnPhotonSerializeView(PhotonStream stream, PhotonMessageInfo info)
    {
        if (stream.isWriting)
        {
            //We own this player: send the others our data
            stream.SendNext(transform.position);
            stream.SendNext(transform.rotation);
        }
        else
        {
            //Network player, receive data
            latestPos = (Vector3)stream.ReceiveNext();
            latestRot = (Quaternion)stream.ReceiveNext();
        }
    }

    // Update is called once per frame
    void Update()
    {
        if (!photonView.isMine)
        {
            //Update remote player (smooth this, this looks good, at the cost of some accuracy)
            transform.position = Vector3.Lerp(transform.position, latestPos, Time.deltaTime * 5);
            transform.rotation = Quaternion.Lerp(transform.rotation, latestRot, Time.deltaTime * 5);
        }
    }
}
  • Aggiungi lo script PlayerNetworkSync.cs a PlayerInstance e assegnalo ai componenti osservati di PhotonView.
  • Assegna il PlayerCntroller.cs al "Local Scripts" e assegna i GameObject (che vuoi disattivare per i giocatori remoti) al "Local Objects"

  • Salva PlayerInstance in Prefab e spostalo nella cartella denominata Resources (se non esiste tale cartella, creane una). Questo passaggio è necessario per poter generare oggetti multiplayer sulla rete.

3. Creazione di un livello di gioco

GameLevel è una scena che viene caricata dopo essere entrato nella stanza ed è dove si svolge tutta l'azione.

  • Crea una nuova scena e chiamala "GameLevel" (oppure se vuoi mantenere un nome diverso, assicurati di cambiare il nome in questa riga PhotonNetwork.LoadLevel("GameLevel"); in GameLobby.cs).

Nel mio caso, userò una semplice scena con un aereo:

  • Ora crea un nuovo script e chiamalo RoomController. Questo script gestirà la logica all'interno della stanza (come generare i giocatori, mostrare l'elenco dei giocatori, ecc.).

Iniziamo definendo le variabili necessarie:

    //Player instance prefab, must be located in the Resources folder
    public GameObject playerPrefab;
    //Player spawn point
    public Transform spawnPoint;

Per istanziare il player prefabbricato usiamo PhotonNetwork.Instantiate:

    // Use this for initialization
    void Start()
    {
        //In case we started this demo with the wrong scene being active, simply load the menu scene
        if (!PhotonNetwork.connected)
        {
            UnityEngine.SceneManagement.SceneManager.LoadScene("MainMenu");
            return;
        }

        //We're in a room. spawn a character for the local player. it gets synced by using PhotonNetwork.Instantiate
        PhotonNetwork.Instantiate(playerPrefab.name, spawnPoint.position, Quaternion.identity, 0);
    }

E una semplice interfaccia utente con un pulsante "Leave Room" e alcuni elementi aggiuntivi come il nome della stanza e l'elenco dei giocatori connessi:

    void OnGUI()
    {
        if (PhotonNetwork.room == null)
            return;

        //Leave this Room
        if (GUI.Button(new Rect(5, 5, 125, 25), "Leave Room"))
        {
            PhotonNetwork.LeaveRoom();
        }

        //Show the Room name
        GUI.Label(new Rect(135, 5, 200, 25), PhotonNetwork.room.Name);

        //Show the list of the players connected to this Room
        for (int i = 0; i < PhotonNetwork.playerList.Length; i++)
        {
            //Show if this player is a Master Client. There can only be one Master Client per Room so use this to define the authoritative logic etc.)
            string isMasterClient = (PhotonNetwork.playerList[i].IsMasterClient ? ": MasterClient" : "");
            GUI.Label(new Rect(5, 35 + 30 * i, 200, 25), PhotonNetwork.playerList[i].NickName + isMasterClient);
        }
    }

Infine, implementiamo un altro callback di PhotonNetwork chiamato OnLeftRoom() che viene chiamato quando lasciamo la stanza:

    void OnLeftRoom()
    {
        //We have left the Room, return to the MainMenu
        UnityEngine.SceneManagement.SceneManager.LoadScene("MainMenu");
    }

Ed ecco lo script RoomController.cs finale:

using UnityEngine;

public class RoomController : MonoBehaviour
{
    //Player instance prefab, must be located in the Resources folder
    public GameObject playerPrefab;
    //Player spawn point
    public Transform spawnPoint;

    // Use this for initialization
    void Start()
    {
        //In case we started this demo with the wrong scene being active, simply load the menu scene
        if (!PhotonNetwork.connected)
        {
            UnityEngine.SceneManagement.SceneManager.LoadScene("MainMenu");
            return;
        }

        //We're in a room. spawn a character for the local player. it gets synced by using PhotonNetwork.Instantiate
        PhotonNetwork.Instantiate(playerPrefab.name, spawnPoint.position, Quaternion.identity, 0);
    }

    void OnGUI()
    {
        if (PhotonNetwork.room == null)
            return;

        //Leave this Room
        if (GUI.Button(new Rect(5, 5, 125, 25), "Leave Room"))
        {
            PhotonNetwork.LeaveRoom();
        }

        //Show the Room name
        GUI.Label(new Rect(135, 5, 200, 25), PhotonNetwork.room.Name);

        //Show the list of the players connected to this Room
        for (int i = 0; i < PhotonNetwork.playerList.Length; i++)
        {
            //Show if this player is a Master Client. There can only be one Master Client per Room so use this to define the authoritative logic etc.)
            string isMasterClient = (PhotonNetwork.playerList[i].IsMasterClient ? ": MasterClient" : "");
            GUI.Label(new Rect(5, 35 + 30 * i, 200, 25), PhotonNetwork.playerList[i].NickName + isMasterClient);
        }
    }

    void OnLeftRoom()
    {
        //We have left the Room, return to the MainMenu
        UnityEngine.SceneManagement.SceneManager.LoadScene("MainMenu");
    }
}
  • Infine, crea un nuovo GameObject nella scena GameLevel e chiamalo "_RoomController"
  • Collega lo script RoomController all'oggetto _RoomController
  • Assegna il prefabbricato PlayerInstance e una trasformazione SpawnPoint ad esso, quindi salva la scena
  • Aggiungi sia MainMenu che GameLevel alle Impostazioni di costruzione.

4. Fare una build di prova

Ora è il momento di creare una build e testarla:

Sharp Coder Lettore video

Tutto funziona come previsto!

Bonus

RPC

In Photon Network, RPC sta per Remote Procedure Call, è usato per chiamare una funzione sui client remoti che si trovano nella stessa stanza (puoi leggere di più a riguardo qui).

Gli RPC hanno molti usi, ad esempio, diciamo che devi inviare un messaggio di chat a tutti i giocatori nella stanza. Con gli RPC, è facile da fare.

[PunRPC]
void ChatMessage(string senderName, string messageText)
{
    Debug.Log(string.Format("{0}: {1}", senderName, messageText));
}

Nota il [PunRPC] prima della funzione. Questo attributo è necessario se si prevede di chiamare la funzione tramite RPC.

Per chiamare le funzioni contrassegnate come RPC, è necessario un PhotonView. Esempio di chiamata:

PhotonView photonView = PhotonView.Get(this);
photonView.RPC("ChatMessage", PhotonTargets.All, PhotonNetwork.playerName, "Some message");

Suggerimento pro: se il tuo script è un Photon.MonoBehaviour o Photon.PunBehaviour puoi usare: this.photonView.RPC().

Proprietà personalizzate

In Photon Network, Proprietà personalizzate è una tabella hash che può essere assegnata al giocatore o alla stanza.

Ciò è utile quando è necessario impostare dati persistenti che non devono essere modificati frequentemente (ad es. nome della squadra del giocatore, modalità di gioco della stanza, ecc.).

Innanzitutto, devi definire una tabella hash, che viene eseguita aggiungendo la riga seguente all'inizio dello script:

//Replace default Hashtables with Photon hashtables
using Hashtable = ExitGames.Client.Photon.Hashtable; 

L'esempio seguente imposta le proprietà della stanza denominate "GameMode" e "AnotherProperty":

        //Set Room properties (Only Master Client is allowed to set Room properties)
        if (PhotonNetwork.isMasterClient)
        {
            Hashtable setRoomProperties = new Hashtable();
            setRoomProperties.Add("GameMode", "FFA");
            setRoomProperties.Add("AnotherProperty", "Test");
            PhotonNetwork.room.SetCustomProperties(setRoomProperties);
        }

        //Will print "FFA"
        print((string)PhotonNetwork.room.CustomProperties["GameMode"]);
        //Will print "Test"
        print((string)PhotonNetwork.room.CustomProperties["AnotherProperty"]);

Le proprietà del giocatore sono impostate in modo simile:

        //Set our Player's property
        Hashtable setPlayerProperties = new Hashtable();
        setPlayerProperties.Add("PlayerHP", (float)100);
        PhotonNetwork.player.SetCustomProperties(setPlayerProperties);

        //Will print "100"
        print((float)PhotonNetwork.player.CustomProperties["PlayerHP"]);

Per rimuovere una proprietà specifica basta impostarne il valore su null.

        //Remove property called "PlayerHP" from Player properties
        Hashtable setPlayerProperties = new Hashtable();
        setPlayerProperties.Add("PlayerHP", null);
        PhotonNetwork.player.SetCustomProperties(setPlayerProperties);