Hướng dẫn cho người mới bắt đầu về Mạng Photon (Cổ điển)

Photon Network là dịch vụ dành cho Unity cho phép các nhà phát triển tạo trò chơi nhiều người chơi trong thời gian thực.

Nó cung cấp API mạnh mẽ và dễ sử dụng, khiến nó trở nên hoàn hảo ngay cả đối với các nhà phát triển mới làm quen.

Trong bài đăng này, chúng tôi sẽ thực hiện tải xuống các tệp cần thiết, thiết lập Photon AppID và lập trình một ví dụ nhiều người chơi đơn giản.

Phần 1: Thiết lập mạng Photon

Bước đầu tiên là tải xuống gói Photon Network từ Asset Store. Nó chứa tất cả các tập lệnh và tệp cần thiết để tích hợp nhiều người chơi.

  • Mở Unity project của bạn sau đó đi tới Asset Store: (Cửa sổ -> Chung -> AssetStore) hoặc nhấn Ctrl+9
  • Tìm kiếm "Photon Unity Networking Classic - Free" sau đó nhấp vào kết quả đầu tiên hoặc nhấp vào đây
  • Nhập gói Photon sau khi Tải xuống xong

  • Sau khi gói được nhập, bạn cần tạo ID ứng dụng Photon, việc này được thực hiện trên trang web của họ: https://www.photonengine.com/
  • Tạo một tài khoản mới (hoặc Đăng nhập vào tài khoản hiện có của bạn)
  • Đi tới trang Ứng dụng bằng cách nhấp vào biểu tượng hồ sơ rồi "Your Applications" hoặc theo liên kết sau: https://dashboard.photonengine.com/en-US/PublicCloud
  • Trên trang Ứng dụng, nhấp vào "Create new app"

  • Trên trang tạo, phần Photon Type chọn "Photon Realtime" và phần Name, nhập tên bất kỳ rồi nhấp vào "Create"

Như bạn có thể thấy, Ứng dụng mặc định ở gói Miễn phí. Bạn có thể đọc thêm về Gói giá tại đây

  • Sau khi Ứng dụng được tạo, hãy sao chép ID ứng dụng nằm dưới Tên ứng dụng

  • Quay lại dự án Unity của bạn sau đó vào Window -> Photon Unity Networking -> PUN Wizard
  • Trong Trình hướng dẫn PUN, nhấp vào "Setup Project", dán ID ứng dụng của bạn rồi nhấp vào "Setup Project"
  • Mạng Photon hiện đã sẵn sàng

Phần 2: Tạo trò chơi nhiều người chơi

Bây giờ hãy chuyển sang phần chúng ta thực sự tạo ra một trò chơi nhiều người chơi.

Cách xử lý nhiều người chơi trong Photon là:

  • Đầu tiên, chúng tôi kết nối với Vùng Photon (ví dụ: Đông Hoa Kỳ, Châu Âu, Châu Á, v.v.) còn được gọi là Sảnh.
  • Khi ở trong Sảnh, chúng tôi yêu cầu tất cả các Phòng được tạo trong khu vực, sau đó chúng tôi có thể tham gia một trong các Phòng hoặc tạo Phòng của riêng mình.
  • Sau khi tham gia phòng, chúng tôi yêu cầu danh sách những người chơi được kết nối với phòng và khởi tạo phiên bản Người chơi của họ, sau đó được đồng bộ hóa với phiên bản cục bộ của họ thông qua PhotonView.
  • Khi ai đó rời khỏi Phòng, phiên bản của họ sẽ bị hủy và họ bị xóa khỏi Danh sách người chơi.

1. Thiết lập sảnh

Hãy bắt đầu bằng cách tạo MainMenu chứa logic Sảnh (Duyệt các phòng hiện có, tạo phòng mới, v.v.).

  • Tạo một Cảnh mới và gọi nó "MainMenu"
  • Tạo một tập lệnh C# mới và gọi nó là GameLobby
  • Trong cảnh MainMenu, tạo GameObject mới. Gọi nó là "_GameLobby" và đính kèm tập lệnh GameLobby vào nó

Bây giờ hãy mở tập lệnh GameLobby.

Đầu tiên, hãy tạo tất cả các biến cần thiết:

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

Điều tiếp theo chúng ta cần làm là kích hoạt Auto-Join Lobby và Lobby Stats, điều này sẽ cho phép chúng ta nhận được danh sách phòng. Việc này được thực hiện trong void Start().

Ngoài ra, chúng tôi bật tự độngSyncScene để Cảnh được tự động đồng bộ hóa khi chúng tôi tham gia Phòng.

Và cuối cùng, chúng tôi gọi PhotonNetwork.ConnectUsingSettings để kết nối.

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

Để biết liệu kết nối với Photon Cloud có thành công hay không, chúng tôi phải triển khai 2 lệnh gọi lại sau: OnReceivedRoomListUpdate()OnFailedToConnectToPhoton(objectparameter).

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

Tiếp theo là phần UI, nơi thực hiện duyệt Room và tạo Room:

Sảnh mạng Photon

Và cuối cùng, chúng tôi triển khai 4 lệnh gọi lại khác: OnPhotonCreateRoomFailed(), OnPhotonJoinRoomFailed(object[] Cause), OnCreatedRoom()OnJoinedRoom().

Những lệnh gọi lại này được sử dụng để xác định xem chúng tôi đã Tham gia/Tạo phòng hay có bất kỳ vấn đề nào trong quá trình kết nối.

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

Và đây là tập lệnh GameLobby.cs cuối cùng:

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. Tạo prefab Player

Trong trò chơi Multiplayer, phiên bản Player có 2 mặt: Local và Remote.

Một phiên bản cục bộ được kiểm soát cục bộ (bởi chúng tôi).

Mặt khác, phiên bản từ xa là sự thể hiện cục bộ về những gì người chơi khác đang làm. Nó sẽ không bị ảnh hưởng bởi đầu vào của chúng tôi.

Để xác định xem phiên bản là Cục bộ hay Từ xa, chúng tôi sử dụng thành phần PhotonView.

PhotonView hoạt động như một trình nhắn tin nhận và gửi các giá trị cần được đồng bộ hóa, chẳng hạn như vị trí và góc quay.

Vì vậy, hãy bắt đầu bằng cách tạo phiên bản trình phát (Nếu bạn đã có sẵn phiên bản trình phát, bạn có thể bỏ qua bước này).

Trong trường hợp của tôi, phiên bản Player sẽ là một Khối lập phương đơn giản được di chuyển bằng các phím W và S và được xoay bằng các phím A và D.

Phiên bản trình phát mạng Photon

Và đây là một tập lệnh điều khiển đơn giản:

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

Bước tiếp theo là thêm thành phần PhotonView.

  • Thêm thành phần PhotonView vào Phiên bản trình phát
  • Tạo một tập lệnh C# mới, gọi nó là PlayerNetworkSync và mở nó (tập lệnh này sẽ được sử dụng để giao tiếp qua PhotonView)

Điều đầu tiên chúng ta cần làm là thay thế MonoBehaviour bằng Photon.MonoBehaviour. Bước này là cần thiết để có thể sử dụng biến photonView được lưu trong bộ nhớ đệm thay vì sử dụng GetComponent<PhotonView>().

public class PlayerNetworkSync : Photon.MonoBehaviour

Sau đó, chúng ta có thể chuyển sang tạo tất cả các biến cần thiết:

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

Sau đó, trong khoảng trống Start(), chúng tôi kiểm tra xem trình phát là Cục bộ hay Từ xa bằng cách sử dụng 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);
            }
        }
    }

Việc đồng bộ hóa thực tế được thực hiện thông qua lệnh gọi lại của PhotonView: OnPhotonSerializeView(PhotonStream Stream, PhotonMessageInfo info):

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

Trong trường hợp này, chúng tôi chỉ gửi Vị trí và Xoay vòng của người chơi, nhưng bạn có thể sử dụng ví dụ trên để gửi bất kỳ giá trị nào cần thiết để đồng bộ hóa qua mạng ở tần suất cao.

Các giá trị nhận được sau đó sẽ được áp dụng trong 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);
        }
    }

Đây là tập lệnh PlayerNetworkSync.cs cuối cùng:

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);
        }
    }
}
  • Thêm tập lệnh PlayerNetworkSync.cs vào PlayerInstance và gán nó cho các Thành phần được quan sát của PhotonView.
  • Gán PlayerCntroller.cs cho "Local Scripts" và gán GameObjects (mà bạn muốn vô hiệu hóa đối với Người chơi từ xa) cho "Local Objects"

  • Lưu PlayerInstance vào Prefab và di chuyển nó vào thư mục có tên Tài nguyên (Nếu không có thư mục đó, hãy tạo một thư mục). Bước này là cần thiết để có thể tạo ra các Đối tượng nhiều người chơi qua Mạng.

3. Tạo cấp độ trò chơi

GameLevel là Cảnh được tải sau khi tham gia Phòng và đó là nơi diễn ra tất cả hành động.

  • Tạo một Cảnh mới và đặt tên là "GameLevel" (Hoặc nếu bạn muốn giữ một tên khác, hãy nhớ đổi tên ở dòng này PhotonNetwork.LoadLevel("GameLevel"); tại GameLobby.cs).

Trong trường hợp của tôi, tôi sẽ sử dụng Cảnh đơn giản với Máy bay:

  • Bây giờ hãy tạo một tập lệnh mới và gọi nó là RoomController. Tập lệnh này sẽ xử lý logic bên trong Phòng (Giống như sinh ra người chơi, hiển thị danh sách người chơi, v.v.).

Hãy bắt đầu bằng cách xác định các biến cần thiết:

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

Để khởi tạo prefab Player, chúng tôi sử dụng 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);
    }

Và giao diện người dùng đơn giản với nút "Leave Room" và một số thành phần bổ sung như Tên phòng và danh sách Người chơi được kết nối:

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

Và cuối cùng, chúng tôi triển khai một lệnh gọi lại PhotonNetwork khác có tên OnLeftRoom() được gọi khi chúng tôi rời khỏi Phòng:

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

Và đây là tập lệnh RoomController.cs cuối cùng:

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");
    }
}
  • Cuối cùng, tạo một GameObject mới trong khung cảnh GameLevel và gọi nó là "_RoomController"
  • Đính kèm tập lệnh RoomController vào đối tượng _RoomController
  • Gán prefab PlayerInstance và Chuyển đổi SpawnPoint cho nó sau đó lưu Cảnh
  • Thêm cả MainMenu và GameLevel vào Cài đặt bản dựng.

4. Xây dựng bản thử nghiệm

Bây giờ là lúc tạo bản dựng và thử nghiệm nó:

Sharp Coder Trình phát video

Mọi thứ hoạt động như mong đợi!

Thưởng

RPC

Trong Mạng Photon, RPC là viết tắt của Gọi thủ tục từ xa, nó được dùng để gọi một chức năng trên các máy khách Từ xa trong cùng một phòng (Bạn có thể đọc thêm về nó tại đây).

RPC có nhiều công dụng, chẳng hạn như giả sử bạn cần gửi tin nhắn trò chuyện tới tất cả người chơi trong Phòng. Với RPC, điều đó thật dễ dàng thực hiện.

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

Lưu ý [PunRPC] trước hàm. Thuộc tính này là cần thiết nếu bạn định gọi hàm thông qua RPC.

Để gọi các hàm được đánh dấu là RPC, bạn cần có PhotonView. Cuộc gọi ví dụ:

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

Mẹo chuyên nghiệp: Nếu tập lệnh của bạn là Photon.MonoBehaviour hoặc Photon.PunBehaviour thì bạn có thể sử dụng: this.photonView.RPC().

Thuộc tính tùy chỉnh

Trong Mạng Photon, Thuộc tính tùy chỉnh là Hashtable có thể được chỉ định cho Người chơi hoặc Phòng.

Điều này hữu ích khi bạn cần đặt dữ liệu liên tục không cần thay đổi thường xuyên (ví dụ: Tên nhóm người chơi, Chế độ trò chơi trong phòng, v.v.).

Trước tiên, bạn phải xác định Hashtable, việc này được thực hiện bằng cách thêm dòng bên dưới vào đầu tập lệnh:

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

Ví dụ bên dưới đặt thuộc tính Phòng có tên "GameMode" và "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"]);

Thuộc tính người chơi được đặt tương tự:

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

Để xóa thuộc tính cụ thể, chỉ cần đặt giá trị của nó thành null.

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