دليل المبتدئين لشبكة الفوتون (الكلاسيكية).

Photon Network هي خدمة لـ Unity تتيح للمطورين إنشاء ألعاب متعددة اللاعبين في الوقت الفعلي.

فهو يوفر واجهة برمجة تطبيقات API قوية وسهلة الاستخدام مما يجعله مثاليًا حتى للمطورين المبتدئين.

في هذه المقالة، سنبدأ بتنزيل الملفات الضرورية، وإعداد Photon AppID، وبرمجة مثال بسيط للعب الجماعي.

الجزء 1: إعداد شبكة الفوتون

الخطوة الأولى هي تنزيل حزمة Photon Network من Asset Store. أنه يحتوي على كافة البرامج النصية والملفات اللازمة لتكامل متعددة اللاعبين.

  • افتح مشروعك Unity ثم انتقل إلى Asset Store: (نافذة -> عام -> AssetStore) أو اضغط على Ctrl+9
  • ابحث عن "فوتون Unity شبكات كلاسيكية - مجاني" ثم انقر على النتيجة الأولى أو انقر هنا
  • قم باستيراد حزمة الفوتون بعد انتهاء التنزيل

  • بعد استيراد الحزمة، يتعين عليك إنشاء معرف تطبيق Photon، ويتم ذلك على موقع الويب الخاص بهم: https://www.photonengine.com/
  • قم بإنشاء حساب جديد (أو قم بتسجيل الدخول إلى حسابك الحالي)
  • انتقل إلى صفحة التطبيقات بالنقر على أيقونة الملف الشخصي ثم "Your Applications" أو اتبع هذا الرابط: https://dashboard.photonengine.com/en-US/PublicCloud
  • في صفحة التطبيقات، انقر فوق "Create new app"

  • في صفحة الإنشاء، بالنسبة لنوع الفوتون، حدد "Photon Realtime" وبالنسبة للاسم، اكتب أي اسم ثم انقر فوق "Create"

كما ترون، يتم تعيين التطبيق افتراضيًا على الخطة المجانية. يمكنك قراءة المزيد حول خطط التسعير هنا

  • بمجرد إنشاء التطبيق، انسخ معرف التطبيق الموجود أسفل اسم التطبيق

  • ارجع إلى مشروعك Unity ثم انتقل إلى Window -> Photon Unity Networking -> PUN Wizard
  • في معالج PUN، انقر على "Setup Project"، ثم الصق معرف التطبيق الخاص بك ثم انقر "Setup Project"
  • شبكة الفوتون جاهزة الآن

الجزء الثاني: إنشاء لعبة متعددة اللاعبين

الآن دعنا ننتقل إلى الجزء الذي نقوم فيه بالفعل بإنشاء لعبة متعددة اللاعبين.

طريقة التعامل مع اللاعبين المتعددين في Photon هي:

  • أولاً، نتصل بمنطقة الفوتون (مثل شرق الولايات المتحدة الأمريكية وأوروبا وآسيا وما إلى ذلك) والتي تُعرف أيضًا باسم اللوبي.
  • بمجرد وصولنا إلى الردهة، نطلب جميع الغرف التي تم إنشاؤها في المنطقة، ثم يمكننا إما الانضمام إلى إحدى الغرف أو إنشاء غرفتنا الخاصة.
  • بعد انضمامنا إلى الغرفة، نطلب قائمة باللاعبين المتصلين بالغرفة وننشئ مثيلات اللاعب الخاصة بهم، والتي تتم بعد ذلك مزامنتها مع مثيلاتهم المحلية من خلال PhotonView.
  • عندما يغادر شخص ما الغرفة، يتم تدمير مثيله وإزالته من قائمة اللاعبين.

1. إعداد اللوبي

لنبدأ بإنشاء القائمة الرئيسية التي ستحتوي على منطق الردهة (استعراض الغرف الموجودة، وإنشاء غرف جديدة، وما إلى ذلك).

  • قم بإنشاء مشهد جديد وسميه "MainMenu"
  • أنشئ نصًا جديدًا لـ C# وأطلق عليه اسم GameLobby
  • في مشهد القائمة الرئيسية، قم بإنشاء كائن GameObject جديد. أطلق عليه "_GameLobby" وأرفق البرنامج النصي GameLobby به

الآن افتح البرنامج النصي GameLobby.

أولاً، لنقم بإنشاء كافة المتغيرات الضرورية:

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

الشيء التالي الذي يتعين علينا القيام به هو تمكين الانضمام التلقائي إلى الردهة وإحصائيات الردهة، وهذا سيسمح لنا بتلقي قائمة الغرف. يتم ذلك في الفراغ Start().

نقوم أيضًا بتمكين SyncScene تلقائيًا بحيث تتم مزامنة المشهد تلقائيًا بمجرد انضمامنا إلى الغرفة.

وأخيرًا، نسمي PhotonNetwork.ConnectUsingSettings للاتصال.

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

لمعرفة ما إذا كان الاتصال بـ Photon Cloud ناجحًا، يتعين علينا تنفيذ ردي الاتصال التاليين: OnReceivedRoomListUpdate() وOnFailedToConnectToPhoton(object بارامترات).

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

التالي هو جزء واجهة المستخدم، حيث يتم تصفح الغرفة وإنشاء الغرفة:

ردهة شبكة الفوتون

وأخيرًا، قمنا بتنفيذ 4 عمليات رد اتصال أخرى: OnPhotonCreateRoomFailed(), OnPhotonJoinRoomFailed(object[] Cause), OnCreatedRoom() و OnJoinedRoom().

تُستخدم عمليات رد الاتصال هذه لتحديد ما إذا كنا قد انضممنا/أنشأنا الغرفة أو إذا كانت هناك أية مشكلات أثناء الاتصال.

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

وإليك النص النهائي لـ 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. إنشاء لاعب الجاهزة

في الألعاب متعددة اللاعبين، يكون لمثيل اللاعب وجهان: محلي وعن بعد.

يتم التحكم في المثيل المحلي محليًا (بواسطتنا).

ومن ناحية أخرى، فإن المثيل البعيد هو تمثيل محلي لما يفعله اللاعب الآخر. يجب أن لا يتأثر بمدخلاتنا.

لتحديد ما إذا كان المثيل محليًا أم بعيدًا، نستخدم مكون PhotonView.

يعمل PhotonView بمثابة برنامج مراسلة يستقبل ويرسل القيم التي تحتاج إلى المزامنة، على سبيل المثال، الموضع والتدوير.

فلنبدأ بإنشاء مثيل المشغل (إذا كان لديك بالفعل مثيل المشغل الخاص بك جاهزًا، فيمكنك تخطي هذه الخطوة).

في حالتي، سيكون مثيل المشغل عبارة عن مكعب بسيط يتم نقله باستخدام مفاتيح W وS وتدويره باستخدام مفاتيح A وD.

مثيل مشغل شبكة الفوتون

وهنا نص تحكم بسيط:

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

الخطوة التالية هي إضافة مكون PhotonView.

  • أضف مكون PhotonView إلى مثيل المشغل
  • قم بإنشاء برنامج نصي جديد لـ C#، وأطلق عليه اسم PlayerNetworkSync، وافتحه (سيتم استخدام هذا البرنامج النصي للتواصل من خلال PhotonView)

أول شيء يتعين علينا القيام به هو استبدال MonoBehaviour بـ Photon.MonoBehaviour. هذه الخطوة ضرورية لتتمكن من استخدام متغير photonView المخزن مؤقتًا بدلاً من استخدام GetComponent<PhotonView>().

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[] localObjects;
    //Values that will be synced over network
    Vector3 latestPos;
    Quaternion latestRot;

ثم في الفراغ Start() نتحقق مما إذا كان اللاعب محليًا أو بعيدًا باستخدام 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);
            }
        }
    }

تتم المزامنة الفعلية من خلال رد اتصال PhotonView: OnPhotonSerializeView(PhotonStreamstream, 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();
        }
    }

في هذه الحالة، نرسل فقط موضع اللاعب ودورانه، ولكن يمكنك استخدام المثال أعلاه لإرسال أي قيمة مطلوبة لمزامنتها عبر الشبكة، بتردد عالٍ.

يتم بعد ذلك تطبيق القيم المستلمة في التحديث الفارغ ():

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

إليك النص البرمجي النهائي لـ 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);
        }
    }
}
  • أضف البرنامج النصي PlayerNetworkSync.cs إلى PlayerInstance وقم بتعيينه إلى PhotonView Observed Components.
  • قم بتعيين PlayerCntroller.cs إلى "Local Scripts" وقم بتعيين GameObjects (التي تريد إلغاء تنشيطها للاعبين عن بعد) إلى "Local Objects"

  • احفظ PlayerInstance في Prefab وانقله إلى المجلد المسمى Resources (إذا لم يكن هناك مثل هذا المجلد، فقم بإنشاء واحد). هذه الخطوة ضرورية لتتمكن من إنتاج كائنات متعددة اللاعبين عبر الشبكة.

3. إنشاء مستوى اللعبة

GameLevel هو مشهد يتم تحميله بعد الانضمام إلى الغرفة وهو المكان الذي تحدث فيه كل الأحداث.

  • أنشئ مشهدًا جديدًا وأطلق عليه "GameLevel" (أو إذا كنت تريد الاحتفاظ باسم مختلف، فتأكد من تغيير الاسم في هذا السطر PhotonNetwork.LoadLevel("GameLevel"); على GameLobby.cs).

في حالتي، سأستخدم مشهدًا بسيطًا بالطائرة:

  • الآن قم بإنشاء برنامج نصي جديد وأطلق عليه اسم RoomController. سيتعامل هذا البرنامج النصي مع المنطق داخل الغرفة (مثل إنتاج اللاعبين، وإظهار قائمة اللاعبين، وما إلى ذلك).

لنبدأ بتحديد المتغيرات الضرورية:

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

لإنشاء مثيل للمشغل الجاهز نستخدم 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);
    }

وواجهة مستخدم بسيطة تحتوي على زر "Leave Room" وبعض العناصر الإضافية مثل اسم الغرفة وقائمة اللاعبين المتصلين:

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

وأخيرًا، قمنا بتنفيذ رد اتصال PhotonNetwork آخر يسمى OnLeftRoom() والذي يتم استدعاؤه عندما نغادر الغرفة:

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

وإليك النص النهائي 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");
    }
}
  • وأخيرًا، قم بإنشاء GameObject جديد في مشهد GameLevel وقم بتسميته "_RoomController"
  • أرفق البرنامج النصي RoomController بكائن _RoomController
  • قم بتعيين الإعداد الجاهز لـ PlayerInstance وتحويل SpawnPoint إليه ثم احفظ المشهد
  • أضف كلاً من MainMenu وGameLevel إلى إعدادات البناء.

4. إجراء اختبار البناء

حان الوقت الآن لإنشاء البنية واختبارها:

Sharp Coder مشغل فديوهات

كل شيء يعمل كما هو متوقع!

علاوة

RPC

في Photon Network، يرمز RPC إلى Remote الإجراء Call، ويتم استخدامه لاستدعاء وظيفة على العملاء البعيدين الموجودين في نفس الغرفة (يمكنك قراءة المزيد عنها هنا ).

تتمتع RPCs بالعديد من الاستخدامات، على سبيل المثال، لنفترض أنك بحاجة إلى إرسال رسالة دردشة إلى جميع اللاعبين في الغرفة. مع RPCs، من السهل القيام بذلك.

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

لاحظ [PunRPC] قبل الوظيفة. تعد هذه السمة ضرورية إذا كنت تخطط لاستدعاء الوظيفة عبر RPCs.

لاستدعاء الوظائف التي تم وضع علامة RPC عليها، تحتاج إلى PhotonView. مثال على المكالمة:

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

نصيحة احترافية: إذا كان البرنامج النصي الخاص بك هو Photon.MonoBehaviour أو Photon.PunBehaviour، فيمكنك استخدام: this.photonView.RPC().

خصائص مخصصة

في Photon Network، تعد الخصائص المخصصة عبارة عن جدول تجزئة يمكن تخصيصه للاعب أو للغرفة.

يكون هذا مفيدًا عندما تحتاج إلى تعيين بيانات ثابتة لا تحتاج إلى تغييرها بشكل متكرر (على سبيل المثال، اسم فريق اللاعب، وضع اللعب في الغرفة، وما إلى ذلك).

أولاً، يجب عليك تحديد Hashtable، ويتم ذلك عن طريق إضافة السطر أدناه في بداية البرنامج النصي:

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

يعين المثال أدناه خصائص الغرفة المسماة "GameMode" و"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"]);

يتم تعيين خصائص اللاعب بالمثل:

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

لإزالة خاصية معينة، فقط قم بتعيين قيمتها على null.

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