Photon Network (Classic) Посібник для початківців
Photon Network — це сервіс для Unity, який дозволяє розробникам створювати багатокористувацькі ігри в реальному часі.
Він забезпечує потужний і простий у використанні API, що робить його ідеальним навіть для розробників-початківців.
У цій публікації ми розповімо про завантаження необхідних файлів, налаштування Photon AppID і програмування простого прикладу для кількох гравців.
Частина 1: Налаштування мережі Photon
Першим кроком є завантаження пакета Photon Network з Asset Store. Він містить усі необхідні сценарії та файли для багатокористувацької інтеграції.
- Відкрийте свій Unity проект, потім перейдіть до Asset Store: (Вікно -> Загальні -> AssetStore) або натисніть Ctrl+9
- Шукайте "Photon Unity Networking Classic - Free", а потім клацніть перший результат або клацніть тут
- Імпортуйте пакет Photon після завершення завантаження
- Після імпорту пакета вам потрібно створити ідентифікатор додатка Photon, це можна зробити на веб-сайті: https://www.photonengine.com/
- Створіть новий обліковий запис (або увійдіть у свій існуючий обліковий запис)
- Перейдіть на сторінку програм, натиснувши піктограму профілю, а потім "Your Applications" або перейдіть за цим посиланням: https://dashboard.photonengine.com/en-US/PublicCloud
- На сторінці додатків натисніть "Create new app"
- На сторінці створення в полі Photon Type виберіть "Photon Realtime", а в полі Name введіть будь-яке ім’я та клацніть "Create"
Як бачите, за замовчуванням програма використовує безкоштовний план. Ви можете прочитати більше про тарифні плани тут
- Коли програму буде створено, скопіюйте ідентифікатор програми, розташований під назвою програми
- Поверніться до свого проекту Unity, потім перейдіть до Window -> Photon Unity Networking -> PUN Wizard
- У майстрі PUN натисніть "Setup Project", вставте свій ідентифікатор програми та натисніть "Setup Project"
- Мережа Photon тепер готова
Частина 2: Створення багатокористувацької гри
Тепер давайте перейдемо до тієї частини, де ми фактично створюємо гру для кількох гравців.
Багатокористувацька гра у Photon реалізована так:
- По-перше, ми підключаємося до регіону Photon (наприклад, Схід США, Європа, Азія тощо), який також відомий як лобі.
- Опинившись у лобі, ми запитуємо всі кімнати, створені в регіоні, а потім можемо приєднатися до однієї з кімнат або створити власну кімнату.
- Після того, як ми приєдналися до кімнати, ми запитуємо список гравців, підключених до кімнати, і створюємо їхні екземпляри Player, які потім синхронізуються з їхніми локальними екземплярами через PhotonView.
- Коли хтось залишає кімнату, його екземпляр знищується, і він видаляється зі списку гравців.
1. Облаштування лобі
Почнемо зі створення головного меню, яке міститиме логіку лобі (перегляд існуючих кімнат, створення нових кімнат тощо).
- Створіть нову сцену та назвіть її "MainMenu"
- Створіть новий сценарій C# і назвіть його GameLobby
- У сцені MainMenu створіть новий 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;
Наступне, що нам потрібно зробити, це ввімкнути автоматичне приєднання до лобі та статистику лобі, це дозволить нам отримати список кімнат. Це робиться в void Start().
Крім того, ми вмикаємо automaticSyncScene, щоб сцена автоматично синхронізувалась, коли ми приєднуємося до кімнати.
І нарешті, ми викликаємо 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 успішним, ми маємо реалізувати ці 2 зворотні виклики: OnReceivedRoomListUpdate() і OnFailedToConnectToPhoton(параметри об’єкта).
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. Створення префабу Player
У багатокористувацьких іграх екземпляр гравця має 2 сторони: локальну та віддалену.
Локальний екземпляр контролюється локально (нами).
Віддалений екземпляр, з іншого боку, є локальним представленням того, що робить інший гравець. Наш внесок не повинен впливати на нього.
Щоб визначити, чи є примірник локальним чи віддаленим, ми використовуємо компонент PhotonView.
PhotonView діє як месенджер, який отримує та надсилає значення, які потрібно синхронізувати, наприклад положення та обертання.
Отже, почнемо зі створення примірника програвача (якщо у вас уже є готовий примірник програвача, ви можете пропустити цей крок).
У моєму випадку екземпляр Player буде простим кубом, який переміщується за допомогою клавіш 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 до екземпляра Player
- Створіть новий сценарій 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(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();
}
}
У цьому випадку ми надсилаємо гравцеві лише позицію та обертання, але ви можете використати приклад вище, щоб надіслати будь-яке значення, яке потрібно синхронізувати через мережу, з високою частотою.
Отримані значення потім застосовуються в 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);
}
}
Ось остаточний сценарій 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.
- Призначте 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;
Щоб створити префаб Player, ми використовуємо 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. Створення тестової збірки
Тепер настав час створити збірку та протестувати її:
Все працює як очікувалося!
Бонус
RPC
У мережі Photon RPC розшифровується як Remote Procedure Call, він використовується для виклику функції на віддалених клієнтах, які знаходяться в одній кімнаті (ви можете прочитати більше про це тут).
RPC мають багато застосувань, наприклад, скажімо, вам потрібно надіслати повідомлення чату всім гравцям у кімнаті. З RPC це легко зробити.
[PunRPC]
void ChatMessage(string senderName, string messageText)
{
Debug.Log(string.Format("{0}: {1}", senderName, messageText));
}
Зверніть увагу на [PunRPC] перед функцією. Цей атрибут необхідний, якщо ви плануєте викликати функцію через RPC.
Щоб викликати функції, позначені як 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 спеціальні властивості — це хеш-таблиця, яку можна призначити гравцеві або кімнаті.
Це корисно, коли вам потрібно встановити постійні дані, які не потрібно часто змінювати (наприклад, назва команди гравця, ігровий режим кімнати тощо).
Спочатку вам потрібно визначити хеш-таблицю, що робиться додаванням рядка нижче на початку сценарію:
//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);