Przewodnik dla początkujących sieci Photon (klasyczny).

Photon Network to usługa dla Unity, która umożliwia programistom tworzenie gier wieloosobowych w czasie rzeczywistym.

Zapewnia potężne i łatwe w użyciu API, co czyni go idealnym nawet dla początkujących programistów.

W tym poście omówimy pobieranie niezbędnych plików, konfigurowanie Photon AppID i programowanie prostego przykładu gry wieloosobowej.

Część 1: Konfiguracja sieci Photon

Pierwszym krokiem jest pobranie pakietu Photon Network z pliku Asset Store. Zawiera wszystkie niezbędne skrypty i pliki do integracji w trybie wieloosobowym.

  • Otwórz swój projekt Unity i przejdź do Asset Store: (Okno -> Ogólne -> AssetStore) lub naciśnij Ctrl+9
  • Wyszukaj „Photon Unity Networking Classic - Free”, a następnie kliknij pierwszy wynik lub kliknij tutaj
  • Zaimportuj pakiet Photon po zakończeniu pobierania

  • Po zaimportowaniu pakietu musisz utworzyć identyfikator aplikacji Photon, można to zrobić na ich stronie internetowej: https://www.photonengine.com/
  • Utwórz nowe konto (lub zaloguj się na istniejące konto)
  • Przejdź do strony Aplikacje, klikając ikonę profilu, a następnie "Your Applications" lub kliknij ten link: https://dashboard.photonengine.com/en-US/PublicCloud
  • Na stronie Aplikacje kliknij "Create new app"

  • Na stronie tworzenia w polu Typ fotonu wybierz "Photon Realtime", a w polu Nazwa wpisz dowolną nazwę i kliknij "Create"

Jak widać, aplikacja domyślnie korzysta z planu Free. Więcej informacji na temat planów cenowych znajdziesz tutaj

  • Po utworzeniu aplikacji skopiuj identyfikator aplikacji znajdujący się pod nazwą aplikacji

  • Wróć do swojego projektu Unity, a następnie przejdź do Okno -> Photon Unity Sieć -> Kreator PUN
  • W kreatorze PUN kliknij "Setup Project", wklej identyfikator aplikacji, a następnie kliknij "Setup Project"
  • Sieć Photon jest już gotowa

Część 2: Tworzenie gry wieloosobowej

Przejdźmy teraz do części, w której właściwie tworzymy grę wieloosobową.

Sposób obsługi trybu wieloosobowego w Photonie jest następujący:

  • Najpierw łączymy się z regionem Photon (np. USA Wschód, Europa, Azja itp.), który jest również znany jako Lobby.
  • Będąc w Lobby, prosimy o wszystkie Pokoje utworzone w regionie, następnie możemy dołączyć do jednego z Pokoi lub stworzyć własny Pokój.
  • Po dołączeniu do pokoju prosimy o listę graczy podłączonych do pokoju i tworzymy instancje ich odtwarzaczy, które następnie są synchronizowane z ich lokalnymi instancjami za pomocą PhotonView.
  • Kiedy ktoś opuści Pokój, jego instancja zostaje zniszczona, a osoba ta zostaje usunięta z Listy Graczy.

1. Konfiguracja lobby

Zacznijmy od stworzenia Menu Głównego, które będzie zawierało logikę lobby (przeglądanie istniejących pokoi, tworzenie nowych itp.).

  • Utwórz nową scenę i nazwij ją "MainMenu"
  • Utwórz nowy skrypt C# i nazwij go GameLobby
  • W scenie MainMenu utwórz nowy obiekt GameObject. Nazwij go "_GameLobby" i dołącz do niego skrypt GameLobby

Teraz otwórz skrypt GameLobby.

Najpierw utwórzmy wszystkie niezbędne zmienne:

    //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;

Następną rzeczą, którą musimy zrobić, to włączyć automatyczne dołączanie do lobby i statystyk lobby, co pozwoli nam otrzymać listę pokoi. Odbywa się to w próżni Start().

Włączamy także automatyczną synchronizację sceny, dzięki czemu scena jest automatycznie synchronizowana po dołączeniu do pokoju.

Na koniec wywołujemy PhotonNetwork.ConnectUsingSettings, aby się połączyć.

    // 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);
        }
    }

Aby wiedzieć, czy połączenie z Photon Cloud powiodło się, musimy zaimplementować te 2 wywołania zwrotne: OnReceivedRoomListUpdate() i OnFailedToConnectToPhoton(parametry obiektu).

    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();
    }

Następna jest część interfejsu użytkownika, w której odbywa się przeglądanie pokoju i tworzenie pokoju:

Lobby sieci Photon

Na koniec implementujemy kolejne 4 wywołania zwrotne: OnPhotonCreateRoomFailed(), OnPhotonJoinRoomFailed(obiekt[] przyczyna), OnCreatedRoom() i OnJoinedRoom().

Te wywołania zwrotne służą do ustalenia, czy dołączyliśmy do pokoju/utworzyliśmy pokój lub czy wystąpiły jakieś problemy podczas połączenia.

    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");
    }

A oto końcowy skrypt GameLobby.cs:

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. Tworzenie prefabrykatu odtwarzacza

W grach wieloosobowych instancja Gracza ma dwie strony: lokalną i zdalną.

Instancja lokalna jest kontrolowana lokalnie (przez nas).

Z kolei zdalna instancja jest lokalną reprezentacją tego, co robi drugi gracz. Nasz wkład nie powinien mieć na to wpływu.

Aby określić, czy instancja jest lokalna czy zdalna, używamy komponentu PhotonView.

PhotonView działa jako komunikator, który odbiera i wysyła wartości wymagające synchronizacji, na przykład położenie i obrót.

Zacznijmy więc od utworzenia instancji gracza (jeśli masz już gotową instancję gracza, możesz pominąć ten krok).

W moim przypadku instancja Gracza będzie prostą kostką, którą można przesuwać klawiszami W i S oraz obracać klawiszami A i D.

Instancja odtwarzacza sieciowego Photon

A oto prosty skrypt kontrolera:

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);
        }
    }
}

Następnym krokiem jest dodanie komponentu PhotonView.

  • Dodaj komponent PhotonView do instancji gracza
  • Utwórz nowy skrypt C#, nazwij go PlayerNetworkSync i otwórz go (ten skrypt będzie używany do komunikacji przez PhotonView)

Pierwszą rzeczą, którą musimy zrobić, to zastąpić MonoBehaviour przez Photon.MonoBehaviour. Ten krok jest niezbędny, aby móc używać buforowanej zmiennej photonView zamiast używać GetComponent<PhotonView>().

public class PlayerNetworkSync : Photon.MonoBehaviour

Następnie możemy przystąpić do tworzenia wszystkich niezbędnych zmiennych:

    //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;

Następnie w próżni Start() sprawdzamy, czy odtwarzacz jest lokalny czy zdalny, używając 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);
            }
        }
    }

Rzeczywista synchronizacja odbywa się poprzez wywołanie zwrotne PhotonView: OnPhotonSerializeView(strumień PhotonStream, informacje o 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();
        }
    }

W tym przypadku wysyłamy tylko pozycję i rotację odtwarzacza, ale możesz skorzystać z powyższego przykładu, aby wysłać dowolną wartość potrzebną do synchronizacji przez sieć z dużą częstotliwością.

Otrzymane wartości są następnie stosowane w 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);
        }
    }

Oto ostateczny skrypt PlayerNetworkSync.cs:

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);
        }
    }
}
  • Dodaj skrypt PlayerNetworkSync.cs do PlayerInstance i przypisz go do obserwowanych komponentów PhotonView.
  • Przypisz PlayerCntroller.cs do "Local Scripts" i przypisz GameObjects (które chcesz dezaktywować dla graczy zdalnych) do "Local Objects"

  • Zapisz PlayerInstance w Prefab i przenieś go do folderu o nazwie Resources (jeśli nie ma takiego folderu, utwórz go). Ten krok jest niezbędny, aby móc tworzyć obiekty dla wielu graczy w sieci.

3. Tworzenie poziomu gry

GameLevel to scena, która ładuje się po dołączeniu do Pokoju i to tam dzieje się cała akcja.

  • Utwórz nową Scenę i nazwij ją "GameLevel" (lub jeśli chcesz zachować inną nazwę, pamiętaj o zmianie nazwy w tej linii PhotonNetwork.LoadLevel("GameLevel"); w GameLobby.cs).

W moim przypadku użyję prostej sceny z płaszczyzną:

  • Teraz utwórz nowy skrypt i nadaj mu nazwę RoomController. Ten skrypt zajmie się logiką panującą w Pokoju (np. odradzaniem graczy, wyświetlaniem listy graczy itp.).

Zacznijmy od zdefiniowania niezbędnych zmiennych:

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

Aby utworzyć instancję prefabrykatu Player, używamy 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);
    }

Oraz prosty interfejs użytkownika z przyciskiem "Leave Room" i kilkoma dodatkowymi elementami, takimi jak nazwa pokoju i lista podłączonych graczy:

    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);
        }
    }

Na koniec implementujemy kolejne wywołanie zwrotne PhotonNetwork o nazwie OnLeftRoom(), które jest wywoływane, gdy opuszczamy pokój:

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

A oto końcowy skrypt RoomController.cs:

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");
    }
}
  • Na koniec utwórz nowy obiekt GameObject w scenie GameLevel i nazwij go "_RoomController"
  • Dołącz skrypt RoomController do obiektu _RoomController
  • Przypisz prefabrykat PlayerInstance i transformację SpawnPoint, a następnie zapisz scenę
  • Dodaj zarówno MainMenu, jak i GameLevel do ustawień kompilacji.

4. Tworzenie kompilacji testowej

Teraz czas na wykonanie kompilacji i przetestowanie jej:

Sharp Coder Odtwarzacz wideo

Wszystko działa zgodnie z oczekiwaniami!

Premia

RPC

W Photon Network RPC oznacza Remote Procedury Call i służy do wywoływania funkcji na zdalnych klientach znajdujących się w tym samym pokoju (więcej na ten temat możesz przeczytać tutaj).

RPC mają wiele zastosowań, powiedzmy, że chcesz wysłać wiadomość na czacie do wszystkich graczy w Pokoju. Dzięki RPC jest to łatwe.

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

Zwróć uwagę na [PunRPC] przed funkcją. Ten atrybut jest niezbędny, jeśli planujesz wywołać funkcję za pośrednictwem wywołań RPC.

Do wywołania funkcji oznaczonych jako RPC potrzebny jest PhotonView. Przykładowe wywołanie:

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

Wskazówka dla profesjonalistów: jeśli Twój skrypt to Photon.MonoBehaviour lub Photon.PunBehaviour, możesz użyć: this.photonView.RPC().

Właściwości niestandardowe

W Photon Network właściwości niestandardowe to tablica mieszająca, którą można przypisać do gracza lub pokoju.

Jest to przydatne, gdy chcesz ustawić trwałe dane, które nie muszą być często zmieniane (np. nazwa drużyny gracza, tryb gry w pomieszczeniu itp.).

Najpierw musisz zdefiniować Hashtable, co można zrobić, dodając poniższą linię na początku skryptu:

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

Poniższy przykład ustawia właściwości Room o nazwach "GameMode" i "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"]);

Właściwości odtwarzacza ustawia się podobnie:

        //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"]);

Aby usunąć określoną właściwość, po prostu ustaw jej wartość na null.

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